diff options
author | Allan Sandfeld Jensen <allan.jensen@qt.io> | 2017-01-04 14:17:57 +0100 |
---|---|---|
committer | Allan Sandfeld Jensen <allan.jensen@qt.io> | 2017-01-05 10:05:06 +0000 |
commit | 39d357e3248f80abea0159765ff39554affb40db (patch) | |
tree | aba0e6bfb76de0244bba0f5fdbd64b830dd6e621 /chromium/ui/views | |
parent | 87778abf5a1f89266f37d1321b92a21851d8244d (diff) | |
download | qtwebengine-chromium-39d357e3248f80abea0159765ff39554affb40db.tar.gz |
BASELINE: Update Chromium to 55.0.2883.105
And updates ninja to 1.7.2
Change-Id: I20d43c737f82764d857ada9a55586901b18b9243
Reviewed-by: Michael BrĂ¼ning <michael.bruning@qt.io>
Diffstat (limited to 'chromium/ui/views')
297 files changed, 11072 insertions, 7386 deletions
diff --git a/chromium/ui/views/BUILD.gn b/chromium/ui/views/BUILD.gn index 80ca87aa04c..aa80e6f0cae 100644 --- a/chromium/ui/views/BUILD.gn +++ b/chromium/ui/views/BUILD.gn @@ -5,11 +5,7 @@ import("//build/config/features.gni") import("//build/config/ui.gni") import("//testing/test.gni") - -gypi_values = exec_script("//build/gypi_to_gn.py", - [ rebase_path("views.gyp") ], - "scope", - [ "views.gyp" ]) +import("//ui/ozone/ozone.gni") config("flags") { defines = [ "TOOLKIT_VIEWS=1" ] @@ -17,7 +13,374 @@ config("flags") { component("views") { all_dependent_configs = [ ":flags" ] - sources = gypi_values.views_sources + sources = [ + "accessibility/native_view_accessibility.cc", + "accessibility/native_view_accessibility.h", + "accessibility/native_view_accessibility_win.cc", + "accessibility/native_view_accessibility_win.h", + "accessible_pane_view.cc", + "accessible_pane_view.h", + "animation/bounds_animator.cc", + "animation/bounds_animator.h", + "animation/flood_fill_ink_drop_ripple.cc", + "animation/flood_fill_ink_drop_ripple.h", + "animation/ink_drop.h", + "animation/ink_drop_animation_ended_reason.cc", + "animation/ink_drop_animation_ended_reason.h", + "animation/ink_drop_highlight.cc", + "animation/ink_drop_highlight.h", + "animation/ink_drop_highlight_observer.h", + "animation/ink_drop_host.h", + "animation/ink_drop_host_view.cc", + "animation/ink_drop_host_view.h", + "animation/ink_drop_impl.cc", + "animation/ink_drop_impl.h", + "animation/ink_drop_painted_layer_delegates.cc", + "animation/ink_drop_painted_layer_delegates.h", + "animation/ink_drop_ripple.cc", + "animation/ink_drop_ripple.h", + "animation/ink_drop_ripple_observer.h", + "animation/ink_drop_state.cc", + "animation/ink_drop_state.h", + "animation/ink_drop_stub.cc", + "animation/ink_drop_stub.h", + "animation/scroll_animator.cc", + "animation/scroll_animator.h", + "animation/square_ink_drop_ripple.cc", + "animation/square_ink_drop_ripple.h", + "background.cc", + "background.h", + "border.cc", + "border.h", + "bubble/bubble_border.cc", + "bubble/bubble_border.h", + "bubble/bubble_dialog_delegate.cc", + "bubble/bubble_dialog_delegate.h", + "bubble/bubble_frame_view.cc", + "bubble/bubble_frame_view.h", + "button_drag_utils.cc", + "button_drag_utils.h", + "cocoa/bridged_content_view.h", + "cocoa/bridged_content_view.mm", + "cocoa/bridged_native_widget.h", + "cocoa/bridged_native_widget.mm", + "cocoa/bridged_native_widget_owner.h", + "cocoa/cocoa_mouse_capture.h", + "cocoa/cocoa_mouse_capture.mm", + "cocoa/cocoa_mouse_capture_delegate.h", + "cocoa/cocoa_window_move_loop.h", + "cocoa/cocoa_window_move_loop.mm", + "cocoa/drag_drop_client_mac.h", + "cocoa/drag_drop_client_mac.mm", + "cocoa/native_widget_mac_nswindow.h", + "cocoa/native_widget_mac_nswindow.mm", + "cocoa/tooltip_manager_mac.h", + "cocoa/tooltip_manager_mac.mm", + "cocoa/views_nswindow_delegate.h", + "cocoa/views_nswindow_delegate.mm", + "cocoa/views_scrollbar_bridge.h", + "cocoa/views_scrollbar_bridge.mm", + "cocoa/widget_owner_nswindow_adapter.h", + "cocoa/widget_owner_nswindow_adapter.mm", + "color_chooser/color_chooser_listener.h", + "color_chooser/color_chooser_view.cc", + "color_chooser/color_chooser_view.h", + "context_menu_controller.h", + "controls/button/blue_button.cc", + "controls/button/blue_button.h", + "controls/button/button.cc", + "controls/button/button.h", + "controls/button/checkbox.cc", + "controls/button/checkbox.h", + "controls/button/custom_button.cc", + "controls/button/custom_button.h", + "controls/button/image_button.cc", + "controls/button/image_button.h", + "controls/button/label_button.cc", + "controls/button/label_button.h", + "controls/button/label_button_border.cc", + "controls/button/label_button_border.h", + "controls/button/md_text_button.cc", + "controls/button/md_text_button.h", + "controls/button/menu_button.cc", + "controls/button/menu_button.h", + "controls/button/menu_button_listener.h", + "controls/button/radio_button.cc", + "controls/button/radio_button.h", + "controls/button/toggle_button.cc", + "controls/button/toggle_button.h", + "controls/button/vector_icon_button.cc", + "controls/button/vector_icon_button.h", + "controls/button/vector_icon_button_delegate.cc", + "controls/button/vector_icon_button_delegate.h", + "controls/combobox/combobox.cc", + "controls/combobox/combobox.h", + "controls/combobox/combobox_listener.h", + "controls/focus_ring.cc", + "controls/focus_ring.h", + "controls/focusable_border.cc", + "controls/focusable_border.h", + "controls/focusable_rounded_border_mac.cc", + "controls/focusable_rounded_border_mac.h", + "controls/glow_hover_controller.cc", + "controls/glow_hover_controller.h", + "controls/image_view.cc", + "controls/image_view.h", + "controls/label.cc", + "controls/label.h", + "controls/link.cc", + "controls/link.h", + "controls/link_listener.h", + "controls/md_slider.cc", + "controls/md_slider.h", + "controls/menu/display_change_listener_mac.cc", + "controls/menu/menu_config.cc", + "controls/menu/menu_config.h", + "controls/menu/menu_config_chromeos.cc", + "controls/menu/menu_config_linux.cc", + "controls/menu/menu_config_mac.mm", + "controls/menu/menu_config_win.cc", + "controls/menu/menu_controller.cc", + "controls/menu/menu_controller.h", + "controls/menu/menu_controller_delegate.h", + "controls/menu/menu_delegate.cc", + "controls/menu/menu_delegate.h", + "controls/menu/menu_host.cc", + "controls/menu/menu_host.h", + "controls/menu/menu_host_root_view.cc", + "controls/menu/menu_host_root_view.h", + "controls/menu/menu_image_util.cc", + "controls/menu/menu_image_util.h", + "controls/menu/menu_insertion_delegate_win.h", + "controls/menu/menu_item_view.cc", + "controls/menu/menu_item_view.h", + "controls/menu/menu_listener.cc", + "controls/menu/menu_listener.h", + "controls/menu/menu_message_loop.h", + "controls/menu/menu_message_loop_mac.cc", + "controls/menu/menu_message_loop_mac.h", + "controls/menu/menu_model_adapter.cc", + "controls/menu/menu_model_adapter.h", + "controls/menu/menu_runner.cc", + "controls/menu/menu_runner.h", + "controls/menu/menu_runner_handler.h", + "controls/menu/menu_runner_impl.cc", + "controls/menu/menu_runner_impl.h", + "controls/menu/menu_runner_impl_adapter.cc", + "controls/menu/menu_runner_impl_adapter.h", + "controls/menu/menu_runner_impl_cocoa.h", + "controls/menu/menu_runner_impl_cocoa.mm", + "controls/menu/menu_runner_impl_interface.h", + "controls/menu/menu_scroll_view_container.cc", + "controls/menu/menu_scroll_view_container.h", + "controls/menu/menu_separator.h", + "controls/menu/menu_separator_views.cc", + "controls/menu/menu_separator_win.cc", + "controls/menu/menu_types.h", + "controls/menu/native_menu_win.cc", + "controls/menu/native_menu_win.h", + "controls/menu/submenu_view.cc", + "controls/menu/submenu_view.h", + "controls/message_box_view.cc", + "controls/message_box_view.h", + "controls/native/native_view_host.cc", + "controls/native/native_view_host.h", + "controls/native/native_view_host_mac.h", + "controls/native/native_view_host_mac.mm", + "controls/non_md_slider.cc", + "controls/non_md_slider.h", + "controls/prefix_delegate.h", + "controls/prefix_selector.cc", + "controls/prefix_selector.h", + "controls/progress_bar.cc", + "controls/progress_bar.h", + "controls/resize_area.cc", + "controls/resize_area.h", + "controls/resize_area_delegate.h", + "controls/scroll_view.cc", + "controls/scroll_view.h", + "controls/scrollbar/base_scroll_bar.cc", + "controls/scrollbar/base_scroll_bar.h", + "controls/scrollbar/base_scroll_bar_button.cc", + "controls/scrollbar/base_scroll_bar_button.h", + "controls/scrollbar/base_scroll_bar_thumb.cc", + "controls/scrollbar/base_scroll_bar_thumb.h", + "controls/scrollbar/cocoa_scroll_bar.h", + "controls/scrollbar/cocoa_scroll_bar.mm", + "controls/scrollbar/native_scroll_bar.cc", + "controls/scrollbar/native_scroll_bar.h", + "controls/scrollbar/native_scroll_bar_views.cc", + "controls/scrollbar/native_scroll_bar_views.h", + "controls/scrollbar/native_scroll_bar_wrapper.h", + "controls/scrollbar/overlay_scroll_bar.cc", + "controls/scrollbar/overlay_scroll_bar.h", + "controls/scrollbar/scroll_bar.cc", + "controls/scrollbar/scroll_bar.h", + "controls/separator.cc", + "controls/separator.h", + "controls/slide_out_view.cc", + "controls/slide_out_view.h", + "controls/slider.cc", + "controls/slider.h", + "controls/styled_label.cc", + "controls/styled_label.h", + "controls/styled_label_listener.h", + "controls/tabbed_pane/tabbed_pane.cc", + "controls/tabbed_pane/tabbed_pane.h", + "controls/tabbed_pane/tabbed_pane_listener.h", + "controls/table/table_header.cc", + "controls/table/table_header.h", + "controls/table/table_utils.cc", + "controls/table/table_utils.h", + "controls/table/table_view.cc", + "controls/table/table_view.h", + "controls/table/table_view_observer.h", + "controls/table/table_view_row_background_painter.h", + "controls/textfield/textfield.cc", + "controls/textfield/textfield.h", + "controls/textfield/textfield_controller.cc", + "controls/textfield/textfield_controller.h", + "controls/textfield/textfield_model.cc", + "controls/textfield/textfield_model.h", + "controls/throbber.cc", + "controls/throbber.h", + "controls/tree/tree_view.cc", + "controls/tree/tree_view.h", + "controls/tree/tree_view_controller.cc", + "controls/tree/tree_view_controller.h", + "debug_utils.cc", + "debug_utils.h", + "drag_controller.h", + "drag_utils.cc", + "drag_utils.h", + "drag_utils_mac.mm", + "event_monitor.h", + "event_monitor_mac.h", + "event_monitor_mac.mm", + "focus/external_focus_tracker.cc", + "focus/external_focus_tracker.h", + "focus/focus_manager.cc", + "focus/focus_manager.h", + "focus/focus_manager_delegate.h", + "focus/focus_manager_factory.cc", + "focus/focus_manager_factory.h", + "focus/focus_search.cc", + "focus/focus_search.h", + "focus/view_storage.cc", + "focus/view_storage.h", + "focus/widget_focus_manager.cc", + "focus/widget_focus_manager.h", + "layout/box_layout.cc", + "layout/box_layout.h", + "layout/fill_layout.cc", + "layout/fill_layout.h", + "layout/grid_layout.cc", + "layout/grid_layout.h", + "layout/layout_constants.h", + "layout/layout_manager.cc", + "layout/layout_manager.h", + "linux_ui/linux_ui.cc", + "linux_ui/linux_ui.h", + "linux_ui/status_icon_linux.cc", + "linux_ui/status_icon_linux.h", + "linux_ui/window_button_order_observer.h", + "linux_ui/window_button_order_provider.cc", + "masked_targeter_delegate.cc", + "masked_targeter_delegate.h", + "metrics.cc", + "metrics.h", + "metrics_mac.cc", + "mouse_constants.h", + "mouse_watcher.cc", + "mouse_watcher.h", + "mouse_watcher_view_host.cc", + "mouse_watcher_view_host.h", + "native_cursor.h", + "native_cursor_mac.mm", + "native_theme_delegate.h", + "painter.cc", + "painter.h", + "pointer_watcher.h", + "rect_based_targeting_utils.cc", + "rect_based_targeting_utils.h", + "repeat_controller.cc", + "repeat_controller.h", + "round_rect_painter.cc", + "round_rect_painter.h", + "shadow_border.cc", + "shadow_border.h", + "style/mac/combobox_background_mac.cc", + "style/mac/combobox_background_mac.h", + "style/platform_style.cc", + "style/platform_style.h", + "style/platform_style_mac.mm", + "view.cc", + "view.h", + "view_constants.cc", + "view_constants.h", + "view_model.cc", + "view_model.h", + "view_model_utils.cc", + "view_model_utils.h", + "view_targeter.cc", + "view_targeter.h", + "view_targeter_delegate.cc", + "view_targeter_delegate.h", + "views_delegate.cc", + "views_delegate.h", + "views_export.h", + "views_exports.cc", + "views_switches.cc", + "views_switches.h", + "views_touch_selection_controller_factory.h", + "views_touch_selection_controller_factory_mac.cc", + "widget/drop_helper.cc", + "widget/drop_helper.h", + "widget/monitor_win.cc", + "widget/monitor_win.h", + "widget/native_widget.h", + "widget/native_widget_delegate.h", + "widget/native_widget_mac.h", + "widget/native_widget_mac.mm", + "widget/native_widget_private.h", + "widget/root_view.cc", + "widget/root_view.h", + "widget/root_view_targeter.cc", + "widget/root_view_targeter.h", + "widget/tooltip_manager.cc", + "widget/tooltip_manager.h", + "widget/widget.cc", + "widget/widget.h", + "widget/widget_aura_utils.cc", + "widget/widget_aura_utils.h", + "widget/widget_delegate.cc", + "widget/widget_delegate.h", + "widget/widget_deletion_observer.cc", + "widget/widget_deletion_observer.h", + "widget/widget_observer.h", + "widget/widget_removals_observer.h", + "window/client_view.cc", + "window/client_view.h", + "window/custom_frame_view.cc", + "window/custom_frame_view.h", + "window/dialog_client_view.cc", + "window/dialog_client_view.h", + "window/dialog_delegate.cc", + "window/dialog_delegate.h", + "window/frame_background.cc", + "window/frame_background.h", + "window/frame_buttons.h", + "window/native_frame_view.cc", + "window/native_frame_view.h", + "window/non_client_view.cc", + "window/non_client_view.h", + "window/window_button_order_provider.cc", + "window/window_button_order_provider.h", + "window/window_resources.h", + "window/window_shape.cc", + "window/window_shape.h", + "word_lookup_client.h", + ] configs += [ "//build/config:precompiled_headers", @@ -36,6 +399,7 @@ component("views") { "//ui/accessibility", "//ui/display", "//ui/native_theme", + "//ui/native_theme:native_theme_browser", "//ui/resources", "//ui/strings", "//ui/views/resources", @@ -81,7 +445,21 @@ component("views") { } if (is_win) { - sources += gypi_values.views_win_sources + sources += [ + "widget/widget_hwnd_utils.cc", + "widget/widget_hwnd_utils.h", + "win/fullscreen_handler.cc", + "win/fullscreen_handler.h", + "win/hwnd_message_handler.cc", + "win/hwnd_message_handler.h", + "win/hwnd_message_handler_delegate.h", + "win/hwnd_util.h", + "win/hwnd_util_aurawin.cc", + "win/scoped_fullscreen_visibility.cc", + "win/scoped_fullscreen_visibility.h", + "win/windows_session_change_observer.cc", + "win/windows_session_change_observer.h", + ] libs = [ "dwmapi.lib", "imm32.lib", @@ -112,24 +490,136 @@ component("views") { } if (use_aura) { - sources += gypi_values.views_aura_sources + sources += [ + "accessibility/ax_aura_obj_cache.cc", + "accessibility/ax_aura_obj_cache.h", + "accessibility/ax_view_obj_wrapper.cc", + "accessibility/ax_view_obj_wrapper.h", + "accessibility/ax_widget_obj_wrapper.cc", + "accessibility/ax_widget_obj_wrapper.h", + "accessibility/ax_window_obj_wrapper.cc", + "accessibility/ax_window_obj_wrapper.h", + "bubble/bubble_window_targeter.cc", + "bubble/bubble_window_targeter.h", + "bubble/tray_bubble_view.cc", + "bubble/tray_bubble_view.h", + "controls/menu/display_change_listener_aura.cc", + "controls/menu/menu_message_loop_aura.cc", + "controls/menu/menu_message_loop_aura.h", + "controls/menu/menu_pre_target_handler.cc", + "controls/menu/menu_pre_target_handler.h", + "controls/native/native_view_host_aura.cc", + "controls/native/native_view_host_aura.h", + "corewm/cursor_height_provider_win.cc", + "corewm/cursor_height_provider_win.h", + "corewm/tooltip.h", + "corewm/tooltip_aura.cc", + "corewm/tooltip_aura.h", + "corewm/tooltip_controller.cc", + "corewm/tooltip_controller.h", + "corewm/tooltip_win.cc", + "corewm/tooltip_win.h", + "drag_utils_aura.cc", + "event_monitor_aura.cc", + "event_monitor_aura.h", + "metrics_aura.cc", + "native_cursor_aura.cc", + "touchui/touch_selection_controller_impl.cc", + "touchui/touch_selection_controller_impl.h", + "touchui/touch_selection_menu_runner_views.cc", + "touchui/touch_selection_menu_runner_views.h", + "view_constants_aura.cc", + "view_constants_aura.h", + "views_touch_selection_controller_factory_aura.cc", + "widget/focus_manager_event_handler.cc", + "widget/focus_manager_event_handler.h", + "widget/native_widget_aura.cc", + "widget/native_widget_aura.h", + "widget/tooltip_manager_aura.cc", + "widget/tooltip_manager_aura.h", + "widget/window_reorderer.cc", + "widget/window_reorderer.h", + ] deps += [ "//ui/aura", "//ui/touch_selection", "//ui/wm", ] if (!is_chromeos) { - sources += gypi_values.views_desktop_aura_sources + sources += [ + "widget/desktop_aura/desktop_capture_client.cc", + "widget/desktop_aura/desktop_capture_client.h", + "widget/desktop_aura/desktop_cursor_loader_updater.h", + "widget/desktop_aura/desktop_drop_target_win.cc", + "widget/desktop_aura/desktop_drop_target_win.h", + "widget/desktop_aura/desktop_event_client.cc", + "widget/desktop_aura/desktop_event_client.h", + "widget/desktop_aura/desktop_focus_rules.cc", + "widget/desktop_aura/desktop_focus_rules.h", + "widget/desktop_aura/desktop_native_cursor_manager.cc", + "widget/desktop_aura/desktop_native_cursor_manager.h", + "widget/desktop_aura/desktop_native_widget_aura.cc", + "widget/desktop_aura/desktop_native_widget_aura.h", + "widget/desktop_aura/desktop_screen.h", + "widget/desktop_aura/desktop_screen_position_client.cc", + "widget/desktop_aura/desktop_screen_position_client.h", + "widget/desktop_aura/desktop_window_tree_host.h", + ] if (use_x11) { - sources += gypi_values.views_desktop_aura_x11_sources - configs += [ "//ui/accessibility:atk" ] + sources += [ + "widget/desktop_aura/desktop_drag_drop_client_aurax11.cc", + "widget/desktop_aura/desktop_drag_drop_client_aurax11.h", + "widget/desktop_aura/desktop_screen_x11.cc", + "widget/desktop_aura/desktop_screen_x11.h", + "widget/desktop_aura/desktop_window_tree_host_x11.cc", + "widget/desktop_aura/desktop_window_tree_host_x11.h", + "widget/desktop_aura/x11_desktop_handler.cc", + "widget/desktop_aura/x11_desktop_handler.h", + "widget/desktop_aura/x11_desktop_handler_observer.h", + "widget/desktop_aura/x11_desktop_window_move_client.cc", + "widget/desktop_aura/x11_desktop_window_move_client.h", + "widget/desktop_aura/x11_move_loop.h", + "widget/desktop_aura/x11_move_loop_delegate.h", + "widget/desktop_aura/x11_pointer_grab.cc", + "widget/desktop_aura/x11_pointer_grab.h", + "widget/desktop_aura/x11_topmost_window_finder.cc", + "widget/desktop_aura/x11_topmost_window_finder.h", + "widget/desktop_aura/x11_whole_screen_move_loop.cc", + "widget/desktop_aura/x11_whole_screen_move_loop.h", + "widget/desktop_aura/x11_window_event_filter.cc", + "widget/desktop_aura/x11_window_event_filter.h", + ] + if (use_atk) { + sources += [ + "accessibility/native_view_accessibility_auralinux.cc", + "accessibility/native_view_accessibility_auralinux.h", + ] + configs += [ "//build/config/linux/atk" ] + } } else if (is_win) { - sources += gypi_values.views_desktop_aura_win_sources + sources += [ + "widget/desktop_aura/desktop_cursor_loader_updater_aurawin.cc", + "widget/desktop_aura/desktop_drag_drop_client_win.cc", + "widget/desktop_aura/desktop_drag_drop_client_win.h", + "widget/desktop_aura/desktop_screen_win.cc", + "widget/desktop_aura/desktop_screen_win.h", + "widget/desktop_aura/desktop_window_tree_host_win.cc", + "widget/desktop_aura/desktop_window_tree_host_win.h", + ] } else if (use_ozone) { - sources += gypi_values.views_desktop_aura_ozone_sources + sources += [ + "widget/desktop_aura/desktop_factory_ozone.cc", + "widget/desktop_aura/desktop_factory_ozone.h", + "widget/desktop_aura/desktop_screen_ozone.cc", + "widget/desktop_aura/desktop_window_tree_host_ozone.cc", + ] } if (is_linux) { - sources += gypi_values.views_desktop_aura_linux_sources + sources += [ + "style/platform_style_linux.cc", + "widget/desktop_aura/desktop_cursor_loader_updater_auralinux.cc", + "widget/desktop_aura/desktop_cursor_loader_updater_auralinux.h", + ] } } } @@ -141,7 +631,6 @@ component("views") { ] libs = [ "AppKit.framework", - "ApplicationServices.framework", # Temporary hack around https://crbug.com/620127. Remove after https://crbug.com/622481 is fixed. "CoreGraphics.framework", "Foundation.framework", "QuartzCore.framework", # Required by bridged_native_widget.mm. @@ -149,9 +638,75 @@ component("views") { } } -source_set("test_support_internal") { +static_library("test_support_internal") { testonly = true - sources = gypi_values.views_test_support_sources + sources = [ + "animation/test/flood_fill_ink_drop_ripple_test_api.cc", + "animation/test/flood_fill_ink_drop_ripple_test_api.h", + "animation/test/ink_drop_highlight_test_api.cc", + "animation/test/ink_drop_highlight_test_api.h", + "animation/test/ink_drop_host_view_test_api.cc", + "animation/test/ink_drop_host_view_test_api.h", + "animation/test/ink_drop_impl_test_api.cc", + "animation/test/ink_drop_impl_test_api.h", + "animation/test/ink_drop_ripple_test_api.cc", + "animation/test/ink_drop_ripple_test_api.h", + "animation/test/ink_drop_utils.cc", + "animation/test/square_ink_drop_ripple_test_api.cc", + "animation/test/square_ink_drop_ripple_test_api.h", + "animation/test/test_ink_drop.cc", + "animation/test/test_ink_drop.h", + "animation/test/test_ink_drop_animation_observer_helper.h", + "animation/test/test_ink_drop_highlight_observer.cc", + "animation/test/test_ink_drop_highlight_observer.h", + "animation/test/test_ink_drop_host.cc", + "animation/test/test_ink_drop_host.h", + "animation/test/test_ink_drop_ripple_observer.cc", + "animation/test/test_ink_drop_ripple_observer.h", + "controls/textfield/textfield_test_api.cc", + "controls/textfield/textfield_test_api.h", + "test/capture_tracking_view.cc", + "test/capture_tracking_view.h", + "test/combobox_test_api.cc", + "test/combobox_test_api.h", + "test/desktop_test_views_delegate.h", + "test/desktop_test_views_delegate_mac.mm", + "test/event_generator_delegate_mac.h", + "test/event_generator_delegate_mac.mm", + "test/focus_manager_test.cc", + "test/focus_manager_test.h", + "test/menu_runner_test_api.cc", + "test/menu_runner_test_api.h", + "test/menu_test_utils.cc", + "test/menu_test_utils.h", + "test/native_widget_factory.cc", + "test/native_widget_factory.h", + "test/scoped_views_test_helper.cc", + "test/scoped_views_test_helper.h", + "test/slider_test_api.cc", + "test/slider_test_api.h", + "test/test_slider.cc", + "test/test_slider.h", + "test/test_views.cc", + "test/test_views.h", + "test/test_views_delegate.h", + "test/test_views_delegate_mac.mm", + "test/test_widget_observer.cc", + "test/test_widget_observer.h", + "test/views_test_base.cc", + "test/views_test_base.h", + "test/views_test_helper.cc", + "test/views_test_helper.h", + "test/views_test_helper_mac.h", + "test/views_test_helper_mac.mm", + "test/widget_test.cc", + "test/widget_test.h", + "test/widget_test_mac.mm", + "test/x11_property_change_waiter.cc", + "test/x11_property_change_waiter.h", + "views_test_suite.cc", + "views_test_suite.h", + ] # External code should depend upon "test_support". visibility = [ "./*" ] @@ -187,19 +742,37 @@ source_set("test_support_internal") { ] if (use_aura) { - sources += gypi_values.views_test_support_aura_sources + sources += [ + "corewm/tooltip_controller_test_helper.cc", + "corewm/tooltip_controller_test_helper.h", + "test/desktop_test_views_delegate_aura.cc", + "test/test_views_delegate_aura.cc", + "test/views_test_helper_aura.cc", + "test/views_test_helper_aura.h", + "test/widget_test_aura.cc", + ] deps += [ "//ui/aura", "//ui/aura:test_support", "//ui/wm", ] if (use_x11 && !is_chromeos) { - sources += gypi_values.views_test_support_desktop_aura_x11_sources + sources += [ + "test/desktop_screen_x11_test_api.cc", + "test/desktop_screen_x11_test_api.h", + "test/test_desktop_screen_x11.cc", + "test/test_desktop_screen_x11.h", + "test/ui_controls_factory_desktop_aurax11.cc", + "test/ui_controls_factory_desktop_aurax11.h", + ] } } if (use_x11) { deps += [ "//ui/gfx/x" ] } + if (ozone_platform_x11) { + deps += [ "//ui/base/x" ] + } if (use_ozone || !use_x11) { sources -= [ "test/x11_property_change_waiter.cc", @@ -218,10 +791,85 @@ static_library("test_support") { ] } -test("views_unittests") { - sources = gypi_values.views_unittests_sources +# This target contains the unit tests that are shared between views_unittests +# and views_mus_unittests. +source_set("views_unittests_sources") { + testonly = true + sources = [ + "accessibility/native_view_accessibility_unittest.cc", + "accessibility/native_view_accessibility_win_unittest.cc", + "accessible_pane_view_unittest.cc", + "animation/bounds_animator_unittest.cc", + "animation/flood_fill_ink_drop_ripple_unittest.cc", + "animation/ink_drop_highlight_unittest.cc", + "animation/ink_drop_host_view_unittest.cc", + "animation/ink_drop_impl_unittest.cc", + "animation/ink_drop_ripple_unittest.cc", + "animation/ink_drop_unittest.cc", + "animation/square_ink_drop_ripple_unittest.cc", + "border_unittest.cc", + "bubble/bubble_border_unittest.cc", + "bubble/bubble_dialog_delegate_unittest.cc", + "bubble/bubble_frame_view_unittest.cc", + "bubble/bubble_window_targeter_unittest.cc", + "cocoa/bridged_native_widget_unittest.mm", + "cocoa/cocoa_mouse_capture_unittest.mm", + "cocoa/drag_drop_client_mac_unittest.mm", + "controls/button/blue_button_unittest.cc", + "controls/button/custom_button_unittest.cc", + "controls/button/image_button_unittest.cc", + "controls/button/label_button_unittest.cc", + "controls/button/menu_button_unittest.cc", + "controls/combobox/combobox_unittest.cc", + "controls/label_unittest.cc", + "controls/menu/menu_controller_unittest.cc", + "controls/menu/menu_item_view_unittest.cc", + "controls/menu/menu_model_adapter_unittest.cc", + "controls/menu/menu_runner_cocoa_unittest.mm", + "controls/menu/menu_runner_unittest.cc", + "controls/native/native_view_host_mac_unittest.mm", + "controls/native/native_view_host_test_base.cc", + "controls/native/native_view_host_test_base.h", + "controls/native/native_view_host_unittest.cc", + "controls/prefix_selector_unittest.cc", + "controls/progress_bar_unittest.cc", + "controls/scroll_view_unittest.cc", + "controls/scrollbar/scrollbar_unittest.cc", + "controls/slider_unittest.cc", + "controls/styled_label_unittest.cc", + "controls/tabbed_pane/tabbed_pane_unittest.cc", + "controls/table/table_utils_unittest.cc", + "controls/table/table_view_unittest.cc", + "controls/table/test_table_model.cc", + "controls/table/test_table_model.h", + "controls/textfield/textfield_model_unittest.cc", + "controls/tree/tree_view_unittest.cc", + "event_monitor_unittest.cc", + "focus/focus_manager_unittest.cc", + "focus/focus_traversal_unittest.cc", + "layout/box_layout_unittest.cc", + "layout/grid_layout_unittest.cc", + "rect_based_targeting_utils_unittest.cc", + "view_model_unittest.cc", + "view_model_utils_unittest.cc", + "view_targeter_unittest.cc", + "view_unittest.cc", + "widget/native_widget_mac_accessibility_unittest.mm", + "widget/native_widget_mac_unittest.mm", + "widget/native_widget_unittest.cc", + "widget/root_view_unittest.cc", + "widget/widget_unittest.cc", + "widget/window_reorderer_unittest.cc", + "window/custom_frame_view_unittest.cc", + "window/dialog_client_view_unittest.cc", + "window/dialog_delegate_unittest.cc", + ] - deps = [ + configs += [ "//build/config:precompiled_headers" ] + + # Make all deps in this target public so both views_unittests and + # views_mus_unittests will get them. + public_deps = [ ":test_support", "//base", "//base:i18n", @@ -254,7 +902,7 @@ test("views_unittests") { ] if (is_win) { - deps += [ + public_deps += [ "//build/win:default_exe_manifest", "//third_party/iaccessible2", "//third_party/wtl", @@ -271,7 +919,7 @@ test("views_unittests") { "//build/config/linux:x11", "//build/config/linux:xext", ] - deps += [ + public_deps += [ "//ui/events/devices", "//ui/events/platform/x11", "//ui/gfx/x", @@ -279,23 +927,19 @@ test("views_unittests") { } if (use_aura) { - sources += gypi_values.views_unittests_aura_sources - deps += [ + sources += [ + "accessibility/ax_aura_obj_cache_unittest.cc", + "controls/native/native_view_host_aura_unittest.cc", + "touchui/touch_selection_menu_runner_views_unittest.cc", + "view_unittest_aura.cc", + "widget/native_widget_aura_unittest.cc", + ] + public_deps += [ "//ui/aura", "//ui/aura:test_support", "//ui/touch_selection", "//ui/wm", ] - if (!is_chromeos) { - sources += gypi_values.views_unittests_desktop_aura_sources - if (use_x11) { - sources += gypi_values.views_unittests_desktop_aurax11_sources - } - } - } - - if (!is_chromeos) { - sources += gypi_values.views_unittests_desktop_sources } if (is_mac) { @@ -305,10 +949,53 @@ test("views_unittests") { "controls/native/native_view_host_unittest.cc", "widget/window_reorderer_unittest.cc", ] - deps += [ "//ui/accelerated_widget_mac" ] + public_deps += [ "//ui/accelerated_widget_mac" ] } } +test("views_unittests") { + sources = [ + "run_all_unittests_main.cc", + + # EventGenerator doesn't work well with IME in mus so this must not be in + # the shared unit test sources. + # crbug.com/615033 crbug.com/548407 + "controls/textfield/textfield_unittest.cc", + ] + + if (use_aura) { + sources += [ + # Tooltips. Can not be shared with mus: crbug.com/599558 + "corewm/tooltip_controller_unittest.cc", + + # Some of the tests need drag-drop support. crbug.com/614037 + "touchui/touch_selection_controller_impl_unittest.cc", + ] + + if (!is_chromeos) { + sources += [ + "widget/desktop_aura/desktop_focus_rules_unittest.cc", + "widget/desktop_aura/desktop_native_widget_aura_unittest.cc", + ] + if (use_x11) { + sources += [ + "widget/desktop_aura/desktop_drag_drop_client_aurax11_unittest.cc", + "widget/desktop_aura/desktop_screen_x11_unittest.cc", + "widget/desktop_aura/desktop_window_tree_host_x11_unittest.cc", + ] + } + } + } + + if (!is_chromeos) { + sources += [ "widget/desktop_widget_unittest.cc" ] + } + + deps = [ + ":views_unittests_sources", + ] +} + if (is_mac) { test("macviews_interactive_ui_tests") { sources = [ diff --git a/chromium/ui/views/accessibility/ax_aura_obj_cache.cc b/chromium/ui/views/accessibility/ax_aura_obj_cache.cc index 9bd021e1d00..0f47bb4b674 100644 --- a/chromium/ui/views/accessibility/ax_aura_obj_cache.cc +++ b/chromium/ui/views/accessibility/ax_aura_obj_cache.cc @@ -4,8 +4,8 @@ #include "ui/views/accessibility/ax_aura_obj_cache.h" +#include "base/memory/ptr_util.h" #include "base/memory/singleton.h" -#include "base/stl_util.h" #include "ui/aura/client/focus_client.h" #include "ui/aura/window.h" #include "ui/views/accessibility/ax_aura_obj_wrapper.h" @@ -74,17 +74,21 @@ void AXAuraObjCache::Remove(Widget* widget) { RemoveViewSubtree(widget->GetRootView()); } -void AXAuraObjCache::Remove(aura::Window* window) { +void AXAuraObjCache::Remove(aura::Window* window, aura::Window* parent) { + int id = GetIDInternal(parent, window_to_id_map_); + AXAuraObjWrapper* parent_window_obj = Get(id); RemoveInternal(window, window_to_id_map_); + if (parent && delegate_) + delegate_->OnChildWindowRemoved(parent_window_obj); } AXAuraObjWrapper* AXAuraObjCache::Get(int32_t id) { - std::map<int32_t, AXAuraObjWrapper*>::iterator it = cache_.find(id); + auto it = cache_.find(id); if (it == cache_.end()) - return NULL; + return nullptr; - return it->second; + return it->second.get(); } void AXAuraObjCache::Remove(int32_t id) { @@ -94,14 +98,12 @@ void AXAuraObjCache::Remove(int32_t id) { return; cache_.erase(id); - delete obj; } void AXAuraObjCache::GetTopLevelWindows( std::vector<AXAuraObjWrapper*>* children) { - for (std::map<aura::Window*, int32_t>::iterator it = - window_to_id_map_.begin(); - it != window_to_id_map_.end(); ++it) { + for (auto it = window_to_id_map_.begin(); it != window_to_id_map_.end(); + ++it) { if (!it->first->parent()) children->push_back(GetOrCreate(it->first)); } @@ -117,12 +119,11 @@ AXAuraObjWrapper* AXAuraObjCache::GetFocus() { AXAuraObjCache::AXAuraObjCache() : current_id_(1), focus_client_(nullptr), - is_destroying_(false) { -} + is_destroying_(false), + delegate_(nullptr) {} AXAuraObjCache::~AXAuraObjCache() { is_destroying_ = true; - STLDeleteContainerPairSecondPointers(cache_.begin(), cache_.end()); cache_.clear(); } @@ -169,17 +170,16 @@ AXAuraObjWrapper* AXAuraObjCache::CreateInternal( AuraView* aura_view, std::map<AuraView*, int32_t>& aura_view_to_id_map) { if (!aura_view) - return NULL; + return nullptr; - typename std::map<AuraView*, int32_t>::iterator it = - aura_view_to_id_map.find(aura_view); + auto it = aura_view_to_id_map.find(aura_view); if (it != aura_view_to_id_map.end()) return Get(it->second); AXAuraObjWrapper* wrapper = new AuraViewWrapper(aura_view); aura_view_to_id_map[aura_view] = current_id_; - cache_[current_id_] = wrapper; + cache_[current_id_] = base::WrapUnique(wrapper); current_id_++; return wrapper; } diff --git a/chromium/ui/views/accessibility/ax_aura_obj_cache.h b/chromium/ui/views/accessibility/ax_aura_obj_cache.h index 763f8f99171..fc9a8c91352 100644 --- a/chromium/ui/views/accessibility/ax_aura_obj_cache.h +++ b/chromium/ui/views/accessibility/ax_aura_obj_cache.h @@ -8,6 +8,7 @@ #include <stdint.h> #include <map> +#include <memory> #include <vector> #include "base/macros.h" @@ -39,6 +40,11 @@ class VIEWS_EXPORT AXAuraObjCache // Get the single instance of this class. static AXAuraObjCache* GetInstance(); + class Delegate { + public: + virtual void OnChildWindowRemoved(AXAuraObjWrapper* parent) = 0; + }; + // Get or create an entry in the cache based on an Aura view. AXAuraObjWrapper* GetOrCreate(View* view); AXAuraObjWrapper* GetOrCreate(Widget* widget); @@ -56,7 +62,10 @@ class VIEWS_EXPORT AXAuraObjCache // Removes an entry from this cache based on an Aura view. void Remove(View* view); void Remove(Widget* widget); - void Remove(aura::Window* window); + + // Removes |window| and optionally notifies delegate by sending an event on + // the |parent| if provided. + void Remove(aura::Window* window, aura::Window* parent); // Removes a view and all of its descendants from the cache. void RemoveViewSubtree(View* view); @@ -76,6 +85,8 @@ class VIEWS_EXPORT AXAuraObjCache // Indicates if this object's currently being destroyed. bool is_destroying() { return is_destroying_; } + void SetDelegate(Delegate* delegate) { delegate_ = delegate; } + private: friend struct base::DefaultSingletonTraits<AXAuraObjCache>; @@ -109,7 +120,7 @@ class VIEWS_EXPORT AXAuraObjCache std::map<views::Widget*, int32_t> widget_to_id_map_; std::map<aura::Window*, int32_t> window_to_id_map_; - std::map<int32_t, AXAuraObjWrapper*> cache_; + std::map<int32_t, std::unique_ptr<AXAuraObjWrapper>> cache_; int32_t current_id_; aura::client::FocusClient* focus_client_; @@ -117,6 +128,8 @@ class VIEWS_EXPORT AXAuraObjCache // True immediately when entering this object's destructor. bool is_destroying_; + Delegate* delegate_; + DISALLOW_COPY_AND_ASSIGN(AXAuraObjCache); }; diff --git a/chromium/ui/views/accessibility/ax_view_obj_wrapper.cc b/chromium/ui/views/accessibility/ax_view_obj_wrapper.cc index 3ea1e04efb2..b043dedc504 100644 --- a/chromium/ui/views/accessibility/ax_view_obj_wrapper.cc +++ b/chromium/ui/views/accessibility/ax_view_obj_wrapper.cc @@ -58,7 +58,7 @@ void AXViewObjWrapper::Serialize(ui::AXNodeData* out_node_data) { if (!view_->visible()) out_node_data->state |= 1 << ui::AX_STATE_INVISIBLE; - out_node_data->location = view_->GetBoundsInScreen(); + out_node_data->location = gfx::RectF(view_->GetBoundsInScreen()); out_node_data->AddStringAttribute( ui::AX_ATTR_NAME, base::UTF16ToUTF8(view_data.name)); diff --git a/chromium/ui/views/accessibility/ax_widget_obj_wrapper.cc b/chromium/ui/views/accessibility/ax_widget_obj_wrapper.cc index adba19f7a17..2a05b196651 100644 --- a/chromium/ui/views/accessibility/ax_widget_obj_wrapper.cc +++ b/chromium/ui/views/accessibility/ax_widget_obj_wrapper.cc @@ -46,7 +46,7 @@ void AXWidgetObjWrapper::Serialize(ui::AXNodeData* out_node_data) { ui::AX_ATTR_NAME, base::UTF16ToUTF8( widget_->widget_delegate()->GetAccessibleWindowTitle())); - out_node_data->location = widget_->GetWindowBoundsInScreen(); + out_node_data->location = gfx::RectF(widget_->GetWindowBoundsInScreen()); out_node_data->state = 0; } @@ -58,6 +58,10 @@ void AXWidgetObjWrapper::OnWidgetDestroying(Widget* widget) { AXAuraObjCache::GetInstance()->Remove(widget); } +void AXWidgetObjWrapper::OnWidgetClosing(Widget* widget) { + AXAuraObjCache::GetInstance()->Remove(widget); +} + void AXWidgetObjWrapper::OnWillRemoveView(Widget* widget, View* view) { AXAuraObjCache::GetInstance()->RemoveViewSubtree(view); } diff --git a/chromium/ui/views/accessibility/ax_widget_obj_wrapper.h b/chromium/ui/views/accessibility/ax_widget_obj_wrapper.h index 4913581b699..244fca50176 100644 --- a/chromium/ui/views/accessibility/ax_widget_obj_wrapper.h +++ b/chromium/ui/views/accessibility/ax_widget_obj_wrapper.h @@ -31,6 +31,7 @@ class AXWidgetObjWrapper : public AXAuraObjWrapper, // WidgetObserver overrides. void OnWidgetDestroying(Widget* widget) override; + void OnWidgetClosing(Widget* widget) override; // WidgetRemovalsObserver overrides. void OnWillRemoveView(Widget* widget, View* view) override; diff --git a/chromium/ui/views/accessibility/ax_window_obj_wrapper.cc b/chromium/ui/views/accessibility/ax_window_obj_wrapper.cc index 5b6f226ea35..7117443cc5c 100644 --- a/chromium/ui/views/accessibility/ax_window_obj_wrapper.cc +++ b/chromium/ui/views/accessibility/ax_window_obj_wrapper.cc @@ -53,7 +53,7 @@ void AXWindowObjWrapper::Serialize(ui::AXNodeData* out_node_data) { out_node_data->AddStringAttribute(ui::AX_ATTR_NAME, base::UTF16ToUTF8(window_->title())); out_node_data->state = 0; - out_node_data->location = window_->bounds(); + out_node_data->location = gfx::RectF(window_->bounds()); } int32_t AXWindowObjWrapper::GetID() { @@ -61,7 +61,19 @@ int32_t AXWindowObjWrapper::GetID() { } void AXWindowObjWrapper::OnWindowDestroyed(aura::Window* window) { - AXAuraObjCache::GetInstance()->Remove(window); + AXAuraObjCache::GetInstance()->Remove(window, nullptr); +} + +void AXWindowObjWrapper::OnWindowDestroying(aura::Window* window) { + Widget* widget = Widget::GetWidgetForNativeView(window); + if (widget) + AXAuraObjCache::GetInstance()->Remove(widget); +} + +void AXWindowObjWrapper::OnWindowHierarchyChanged( + const HierarchyChangeParams& params) { + if (params.phase == WindowObserver::HierarchyChangeParams::HIERARCHY_CHANGED) + AXAuraObjCache::GetInstance()->Remove(params.target, params.old_parent); } } // namespace views diff --git a/chromium/ui/views/accessibility/ax_window_obj_wrapper.h b/chromium/ui/views/accessibility/ax_window_obj_wrapper.h index bcf35481d73..5a949c722f9 100644 --- a/chromium/ui/views/accessibility/ax_window_obj_wrapper.h +++ b/chromium/ui/views/accessibility/ax_window_obj_wrapper.h @@ -38,6 +38,8 @@ class AXWindowObjWrapper : public AXAuraObjWrapper, // WindowObserver overrides. void OnWindowDestroyed(aura::Window* window) override; + void OnWindowDestroying(aura::Window* window) override; + void OnWindowHierarchyChanged(const HierarchyChangeParams& params) override; private: aura::Window* window_; diff --git a/chromium/ui/views/accessibility/native_view_accessibility.cc b/chromium/ui/views/accessibility/native_view_accessibility.cc index 0cdec0d1503..a984c3c6c08 100644 --- a/chromium/ui/views/accessibility/native_view_accessibility.cc +++ b/chromium/ui/views/accessibility/native_view_accessibility.cc @@ -8,6 +8,7 @@ #include "build/build_config.h" #include "ui/accessibility/ax_view_state.h" #include "ui/events/event_utils.h" +#include "ui/gfx/native_widget_types.h" #include "ui/views/controls/native/native_view_host.h" #include "ui/views/view.h" #include "ui/views/widget/widget.h" @@ -51,12 +52,21 @@ void NativeViewAccessibility::NotifyAccessibilityEvent(ui::AXEvent event_type) { // ui::AXPlatformNodeDelegate const ui::AXNodeData& NativeViewAccessibility::GetData() { + data_ = ui::AXNodeData(); + + // Views may misbehave if their widget is closed; return an unknown role + // rather than possibly crashing. + if (!view_->GetWidget() || view_->GetWidget()->IsClosed()) { + data_.role = ui::AX_ROLE_UNKNOWN; + data_.state = 1 << ui::AX_STATE_DISABLED; + return data_; + } + ui::AXViewState state; view_->GetAccessibleState(&state); - data_ = ui::AXNodeData(); data_.role = state.role; data_.state = state.state(); - data_.location = view_->GetBoundsInScreen(); + data_.location = gfx::RectF(view_->GetBoundsInScreen()); data_.AddStringAttribute(ui::AX_ATTR_NAME, base::UTF16ToUTF8(state.name)); data_.AddStringAttribute(ui::AX_ATTR_VALUE, base::UTF16ToUTF8(state.value)); data_.AddStringAttribute(ui::AX_ATTR_ACTION, @@ -65,6 +75,12 @@ const ui::AXNodeData& NativeViewAccessibility::GetData() { base::UTF16ToUTF8(state.keyboard_shortcut)); data_.AddStringAttribute(ui::AX_ATTR_PLACEHOLDER, base::UTF16ToUTF8(state.placeholder)); + + if (state.description.empty() && + view_->GetTooltipText(gfx::Point(), &state.description)) + data_.AddStringAttribute(ui::AX_ATTR_DESCRIPTION, + base::UTF16ToUTF8(state.description)); + data_.AddIntAttribute(ui::AX_ATTR_TEXT_SEL_START, state.selection_start); data_.AddIntAttribute(ui::AX_ATTR_TEXT_SEL_END, state.selection_end); @@ -105,6 +121,12 @@ gfx::NativeViewAccessible NativeViewAccessibility::ChildAtIndex(int index) { return nullptr; } +gfx::NativeWindow NativeViewAccessibility::GetTopLevelWidget() { + if (view_->GetWidget()) + return view_->GetWidget()->GetTopLevelWidget()->GetNativeWindow(); + return nullptr; +} + gfx::NativeViewAccessible NativeViewAccessibility::GetParent() { if (view_->parent()) return view_->parent()->GetNativeViewAccessible(); @@ -193,15 +215,22 @@ void NativeViewAccessibility::DoDefaultAction() { bool NativeViewAccessibility::SetStringValue(const base::string16& new_value) { // Return an error if the view can't set the value. - ui::AXViewState state; - view_->GetAccessibleState(&state); - if (state.set_value_callback.is_null()) + if (!CanSetStringValue()) return false; + ui::AXViewState state; + view_->GetAccessibleState(&state); state.set_value_callback.Run(new_value); return true; } +bool NativeViewAccessibility::CanSetStringValue() { + ui::AXViewState state; + view_->GetAccessibleState(&state); + return !ui::AXViewState::IsFlagSet(GetData().state, ui::AX_STATE_READ_ONLY) && + !state.set_value_callback.is_null(); +} + void NativeViewAccessibility::OnWidgetDestroying(Widget* widget) { if (parent_widget_ == widget) { parent_widget_->RemoveObserver(this); diff --git a/chromium/ui/views/accessibility/native_view_accessibility.h b/chromium/ui/views/accessibility/native_view_accessibility.h index ac59130bace..95fcc3de365 100644 --- a/chromium/ui/views/accessibility/native_view_accessibility.h +++ b/chromium/ui/views/accessibility/native_view_accessibility.h @@ -49,6 +49,7 @@ class VIEWS_EXPORT NativeViewAccessibility const ui::AXNodeData& GetData() override; int GetChildCount() override; gfx::NativeViewAccessible ChildAtIndex(int index) override; + gfx::NativeWindow GetTopLevelWidget() override; gfx::NativeViewAccessible GetParent() override; gfx::Vector2d GetGlobalCoordinateOffset() override; gfx::NativeViewAccessible HitTestSync(int x, int y) override; @@ -56,6 +57,7 @@ class VIEWS_EXPORT NativeViewAccessibility gfx::AcceleratedWidget GetTargetForNativeAccessibilityEvent() override; void DoDefaultAction() override; bool SetStringValue(const base::string16& new_value) override; + bool CanSetStringValue() override; // WidgetObserver void OnWidgetDestroying(Widget* widget) override; diff --git a/chromium/ui/views/accessibility/native_view_accessibility_auralinux.cc b/chromium/ui/views/accessibility/native_view_accessibility_auralinux.cc index 90927af368d..8e68cc00bff 100644 --- a/chromium/ui/views/accessibility/native_view_accessibility_auralinux.cc +++ b/chromium/ui/views/accessibility/native_view_accessibility_auralinux.cc @@ -46,7 +46,7 @@ class AuraLinuxApplication return; widget = widget->GetTopLevelWidget(); - if (ContainsValue(widgets_, widget)) + if (base::ContainsValue(widgets_, widget)) return; widgets_.push_back(widget); @@ -57,9 +57,7 @@ class AuraLinuxApplication return platform_node_->GetNativeViewAccessible(); } - // - // WidgetObserver overrides. - // + // WidgetObserver: void OnWidgetDestroying(Widget* widget) override { auto iter = std::find(widgets_.begin(), widgets_.end(), widget); @@ -67,14 +65,14 @@ class AuraLinuxApplication widgets_.erase(iter); } - // - // ui::AXPlatformNodeDelegate overrides. - // + // ui::AXPlatformNodeDelegate: const ui::AXNodeData& GetData() override { return data_; } + gfx::NativeWindow GetTopLevelWidget() override { return nullptr; } + gfx::NativeViewAccessible GetParent() override { return nullptr; } @@ -115,6 +113,8 @@ class AuraLinuxApplication return false; } + bool CanSetStringValue() override { return false; } + private: friend struct base::DefaultSingletonTraits<AuraLinuxApplication>; diff --git a/chromium/ui/views/accessibility/native_view_accessibility_unittest.cc b/chromium/ui/views/accessibility/native_view_accessibility_unittest.cc index a60b1cb6b95..37435e205a1 100644 --- a/chromium/ui/views/accessibility/native_view_accessibility_unittest.cc +++ b/chromium/ui/views/accessibility/native_view_accessibility_unittest.cc @@ -4,6 +4,7 @@ #include "base/strings/utf_string_conversions.h" #include "ui/accessibility/ax_view_state.h" +#include "ui/gfx/geometry/rect_conversions.h" #include "ui/views/accessibility/native_view_accessibility.h" #include "ui/views/controls/button/button.h" #include "ui/views/controls/label.h" @@ -30,16 +31,27 @@ class NativeViewAccessibilityTest : public ViewsTestBase { void SetUp() override { ViewsTestBase::SetUp(); - button_.reset(new TestButton()); + + widget_ = new views::Widget; + views::Widget::InitParams params = + CreateParams(views::Widget::InitParams::TYPE_WINDOW_FRAMELESS); + params.bounds = gfx::Rect(0, 0, 200, 200); + widget_->Init(params); + + button_ = new TestButton(); button_->SetSize(gfx::Size(20, 20)); - button_accessibility_ = NativeViewAccessibility::Create(button_.get()); + button_accessibility_ = NativeViewAccessibility::Create(button_); + + label_ = new Label(); + button_->AddChildView(label_); + label_accessibility_ = NativeViewAccessibility::Create(label_); - label_.reset(new Label); - button_->AddChildView(label_.get()); - label_accessibility_ = NativeViewAccessibility::Create(label_.get()); + widget_->SetContentsView(button_); } void TearDown() override { + if (!widget_->IsClosed()) + widget_->Close(); button_accessibility_->Destroy(); button_accessibility_ = NULL; label_accessibility_->Destroy(); @@ -48,9 +60,10 @@ class NativeViewAccessibilityTest : public ViewsTestBase { } protected: - std::unique_ptr<TestButton> button_; + views::Widget* widget_; + TestButton* button_; NativeViewAccessibility* button_accessibility_; - std::unique_ptr<Label> label_; + Label* label_; NativeViewAccessibility* label_accessibility_; }; @@ -60,7 +73,8 @@ TEST_F(NativeViewAccessibilityTest, RoleShouldMatch) { } TEST_F(NativeViewAccessibilityTest, BoundsShouldMatch) { - gfx::Rect bounds = button_accessibility_->GetData().location; + gfx::Rect bounds = gfx::ToEnclosingRect( + button_accessibility_->GetData().location); bounds.Offset(button_accessibility_->GetGlobalCoordinateOffset()); EXPECT_EQ(button_->GetBoundsInScreen(), bounds); } diff --git a/chromium/ui/views/animation/flood_fill_ink_drop_ripple.cc b/chromium/ui/views/animation/flood_fill_ink_drop_ripple.cc index 3bad83e4262..e69de79a2ef 100644 --- a/chromium/ui/views/animation/flood_fill_ink_drop_ripple.cc +++ b/chromium/ui/views/animation/flood_fill_ink_drop_ripple.cc @@ -153,8 +153,8 @@ ui::Layer* FloodFillInkDropRipple::GetRootLayer() { return &root_layer_; } -bool FloodFillInkDropRipple::IsVisible() const { - return root_layer_.visible(); +bool FloodFillInkDropRipple::OverridesHighlight() const { + return false; } void FloodFillInkDropRipple::AnimateStateChange( diff --git a/chromium/ui/views/animation/flood_fill_ink_drop_ripple.h b/chromium/ui/views/animation/flood_fill_ink_drop_ripple.h index bad8cd79aea..89b38db158c 100644 --- a/chromium/ui/views/animation/flood_fill_ink_drop_ripple.h +++ b/chromium/ui/views/animation/flood_fill_ink_drop_ripple.h @@ -57,7 +57,7 @@ class VIEWS_EXPORT FloodFillInkDropRipple : public InkDropRipple { // InkDropRipple: void SnapToActivated() override; ui::Layer* GetRootLayer() override; - bool IsVisible() const override; + bool OverridesHighlight() const override; private: friend class test::FloodFillInkDropRippleTestApi; diff --git a/chromium/ui/views/animation/ink_drop_highlight.cc b/chromium/ui/views/animation/ink_drop_highlight.cc index 32f1231dbc7..d2077b16c32 100644 --- a/chromium/ui/views/animation/ink_drop_highlight.cc +++ b/chromium/ui/views/animation/ink_drop_highlight.cc @@ -66,7 +66,11 @@ InkDropHighlight::InkDropHighlight(const gfx::Size& size, layer_->SetOpacity(visible_opacity_); } -InkDropHighlight::~InkDropHighlight() {} +InkDropHighlight::~InkDropHighlight() { + // Explicitly aborting all the animations ensures all callbacks are invoked + // while this instance still exists. + layer_->GetAnimator()->AbortAllAnimations(); +} bool InkDropHighlight::IsFadingInOrVisible() const { return last_animation_initiated_was_fade_in_; diff --git a/chromium/ui/views/animation/ink_drop_host_view.cc b/chromium/ui/views/animation/ink_drop_host_view.cc index 2c6ffe2b2a0..89f320d2a50 100644 --- a/chromium/ui/views/animation/ink_drop_host_view.cc +++ b/chromium/ui/views/animation/ink_drop_host_view.cc @@ -44,22 +44,22 @@ const int InkDropHostView::kInkDropSmallCornerRadius = 2; // TODO(bruthig): Consider getting rid of this class. class InkDropHostView::InkDropGestureHandler : public ui::EventHandler { public: - InkDropGestureHandler(InkDropHostView* host_view, InkDrop* ink_drop) + explicit InkDropGestureHandler(InkDropHostView* host_view) : target_handler_(new ui::ScopedTargetHandler(host_view, this)), - host_view_(host_view), - ink_drop_(ink_drop) {} + host_view_(host_view) {} ~InkDropGestureHandler() override {} - void SetInkDrop(InkDrop* ink_drop) { ink_drop_ = ink_drop; } - // ui::EventHandler: void OnGestureEvent(ui::GestureEvent* event) override { - InkDropState current_ink_drop_state = ink_drop_->GetTargetInkDropState(); + InkDropState current_ink_drop_state = + host_view_->ink_drop()->GetTargetInkDropState(); InkDropState ink_drop_state = InkDropState::HIDDEN; switch (event->type()) { case ui::ET_GESTURE_TAP_DOWN: + if (current_ink_drop_state == InkDropState::ACTIVATED) + return; ink_drop_state = InkDropState::ACTION_PENDING; // The ui::ET_GESTURE_TAP_DOWN event needs to be marked as handled so // that @@ -67,16 +67,17 @@ class InkDropHostView::InkDropGestureHandler : public ui::EventHandler { event->SetHandled(); break; case ui::ET_GESTURE_LONG_PRESS: + if (current_ink_drop_state == InkDropState::ACTIVATED) + return; ink_drop_state = InkDropState::ALTERNATE_ACTION_PENDING; break; case ui::ET_GESTURE_LONG_TAP: ink_drop_state = InkDropState::ALTERNATE_ACTION_TRIGGERED; break; case ui::ET_GESTURE_END: + case ui::ET_GESTURE_SCROLL_BEGIN: if (current_ink_drop_state == InkDropState::ACTIVATED) return; - // Fall through to ui::ET_GESTURE_SCROLL_BEGIN case. - case ui::ET_GESTURE_SCROLL_BEGIN: ink_drop_state = InkDropState::HIDDEN; break; default: @@ -102,9 +103,6 @@ class InkDropHostView::InkDropGestureHandler : public ui::EventHandler { // The host view to cache ui::Events to when animating the ink drop. InkDropHostView* host_view_; - // Animation controller for the ink drop ripple effect. - InkDrop* ink_drop_; - DISALLOW_COPY_AND_ASSIGN(InkDropGestureHandler); }; @@ -235,17 +233,16 @@ bool InkDropHostView::ShouldShowInkDropForFocus() const { return false; } -void InkDropHostView::SetHasInkDrop(bool has_an_ink_drop) { - if (has_an_ink_drop) { +void InkDropHostView::SetInkDropMode(InkDropMode ink_drop_mode) { + if (ink_drop_mode == InkDropMode::OFF) + ink_drop_.reset(new InkDropStub()); + else ink_drop_.reset(new InkDropImpl(this)); - if (!gesture_handler_) - gesture_handler_.reset(new InkDropGestureHandler(this, ink_drop_.get())); - else - gesture_handler_->SetInkDrop(ink_drop_.get()); - } else { + + if (ink_drop_mode != InkDropMode::ON) gesture_handler_.reset(); - ink_drop_.reset(new InkDropStub()); - } + else if (!gesture_handler_) + gesture_handler_.reset(new InkDropGestureHandler(this)); } } // namespace views diff --git a/chromium/ui/views/animation/ink_drop_host_view.h b/chromium/ui/views/animation/ink_drop_host_view.h index 030faa810d1..2f30b24b3b7 100644 --- a/chromium/ui/views/animation/ink_drop_host_view.h +++ b/chromium/ui/views/animation/ink_drop_host_view.h @@ -38,6 +38,16 @@ class VIEWS_EXPORT InkDropHostView : public View, public InkDropHost { void set_ink_drop_size(const gfx::Size& size) { ink_drop_size_ = size; } protected: + // Used in SetInkDropMode() to specify whether the ink drop effect is enabled + // or not for the view. In case of having an ink drop, it also specifies + // whether the default gesture event handler for the ink drop should be + // installed or the subclass will handle gesture events itself. + enum class InkDropMode { + OFF, + ON, + ON_NO_GESTURE_HANDLER, + }; + static const int kInkDropSmallCornerRadius; void set_ink_drop_visible_opacity(float visible_opacity) { @@ -84,7 +94,7 @@ class VIEWS_EXPORT InkDropHostView : public View, public InkDropHost { // Toggle to enable/disable an InkDrop on this View. Descendants can override // CreateInkDropHighlight() and CreateInkDropRipple() to change the look/feel // of the InkDrop. - void SetHasInkDrop(bool has_an_ink_drop); + void SetInkDropMode(InkDropMode ink_drop_mode); private: class InkDropGestureHandler; diff --git a/chromium/ui/views/animation/ink_drop_host_view_unittest.cc b/chromium/ui/views/animation/ink_drop_host_view_unittest.cc index b0cf3116379..4526a11a4d6 100644 --- a/chromium/ui/views/animation/ink_drop_host_view_unittest.cc +++ b/chromium/ui/views/animation/ink_drop_host_view_unittest.cc @@ -16,6 +16,7 @@ namespace views { namespace test { +using InkDropMode = InkDropHostViewTestApi::InkDropMode; class InkDropHostViewColor : public InkDropHostView { protected: @@ -65,17 +66,21 @@ TEST_F(InkDropHostViewTest, GetInkDropCenterBasedOnLastEventForLocatedEvent) { EXPECT_EQ(gfx::Point(5, 6), test_api_.GetInkDropCenterBasedOnLastEvent()); } -// Verifies that SetHasInkDrop() sets up gesture handling properly. -TEST_F(InkDropHostViewTest, SetHasInkDropGestureHandler) { +// Verifies that SetInkDropMode() sets up gesture handling properly. +TEST_F(InkDropHostViewTest, SetInkDropModeGestureHandler) { EXPECT_FALSE(test_api_.HasGestureHandler()); - test_api_.SetHasInkDrop(true); + test_api_.SetInkDropMode(InkDropMode::ON_NO_GESTURE_HANDLER); + EXPECT_FALSE(test_api_.HasGestureHandler()); + + test_api_.SetInkDropMode(InkDropMode::ON); EXPECT_TRUE(test_api_.HasGestureHandler()); - test_api_.SetHasInkDrop(true); + // Enabling gesture handler the second time should just work (no crash). + test_api_.SetInkDropMode(InkDropMode::ON); EXPECT_TRUE(test_api_.HasGestureHandler()); - test_api_.SetHasInkDrop(false); + test_api_.SetInkDropMode(InkDropMode::OFF); EXPECT_FALSE(test_api_.HasGestureHandler()); } @@ -83,7 +88,7 @@ TEST_F(InkDropHostViewTest, SetHasInkDropGestureHandler) { TEST_F(InkDropHostViewTest, NoInkDropOnTouchOrGestureEvents) { host_view_.SetSize(gfx::Size(20, 20)); - test_api_.SetHasInkDrop(true); + test_api_.SetInkDropMode(InkDropMode::ON_NO_GESTURE_HANDLER); // Ensure the target ink drop is in the expected state. EXPECT_EQ(test_api_.ink_drop()->GetTargetInkDropState(), @@ -117,7 +122,7 @@ TEST_F(InkDropHostViewTest, NoInkDropOnTouchOrGestureEvents) { TEST_F(InkDropHostViewTest, DismissInkDropOnTouchOrGestureEvents) { host_view_.SetSize(gfx::Size(20, 20)); - test_api_.SetHasInkDrop(true); + test_api_.SetInkDropMode(InkDropMode::ON_NO_GESTURE_HANDLER); // Ensure the target ink drop is in the expected state. EXPECT_EQ(test_api_.ink_drop()->GetTargetInkDropState(), diff --git a/chromium/ui/views/animation/ink_drop_impl.cc b/chromium/ui/views/animation/ink_drop_impl.cc index cde9818ce5d..9d65896d284 100644 --- a/chromium/ui/views/animation/ink_drop_impl.cc +++ b/chromium/ui/views/animation/ink_drop_impl.cc @@ -17,23 +17,23 @@ namespace { // The duration, in milliseconds, of the highlight state fade in animation when // it is triggered by user input. -const int kHighlightFadeInFromUserInputDurationInMs = 250; +const int kHighlightFadeInFromUserInputDurationMs = 250; // The duration, in milliseconds, of the highlight state fade out animation when // it is triggered by user input. -const int kHighlightFadeOutFromUserInputDurationInMs = 250; +const int kHighlightFadeOutFromUserInputDurationMs = 250; // The duration, in milliseconds, of the highlight state fade in animation when // it is triggered by an ink drop ripple animation ending. -const int kHighlightFadeInAfterRippleDurationInMs = 250; +const int kHighlightFadeInAfterRippleDurationMs = 250; // The duration, in milliseconds, of the highlight state fade out animation when // it is triggered by an ink drop ripple animation starting. -const int kHighlightFadeOutBeforeRippleDurationInMs = 120; +const int kHighlightFadeOutBeforeRippleDurationMs = 120; // The amount of time in milliseconds that |highlight_| should delay after a -// ripple animation before fading in. -const int kHighlightFadeInAfterRippleDelayInMs = 1000; +// ripple animation before fading in, for highlight due to mouse hover. +const int kHoverFadeInAfterRippleDelayMs = 1000; // Returns true if an ink drop with the given |ink_drop_state| should // automatically transition to the InkDropState::HIDDEN state. @@ -78,10 +78,21 @@ void InkDropImpl::AnimateToState(InkDropState ink_drop_state) { if (!ink_drop_ripple_) CreateInkDropRipple(); - if (ink_drop_state != views::InkDropState::HIDDEN) { - SetHighlight(false, base::TimeDelta::FromMilliseconds( - kHighlightFadeOutBeforeRippleDurationInMs), - true); + if (ink_drop_ripple_->OverridesHighlight()) { + // When deactivating and the host is focused, snap back to the highlight + // state. (In the case of highlighting due to hover, we'll animate the + // highlight back in after a delay.) + if (ink_drop_state == views::InkDropState::DEACTIVATED && is_focused_) { + ink_drop_ripple_->HideImmediately(); + SetHighlight(true, base::TimeDelta(), false); + return; + } + + if (ink_drop_state != views::InkDropState::HIDDEN) { + SetHighlight(false, base::TimeDelta::FromMilliseconds( + kHighlightFadeOutBeforeRippleDurationMs), + true); + } } ink_drop_ripple_->AnimateToState(ink_drop_state); @@ -92,7 +103,8 @@ void InkDropImpl::SnapToActivated() { if (!ink_drop_ripple_) CreateInkDropRipple(); - SetHighlight(false, base::TimeDelta(), false); + if (ink_drop_ripple_->OverridesHighlight()) + SetHighlight(false, base::TimeDelta(), false); ink_drop_ripple_->SnapToActivated(); } @@ -102,9 +114,9 @@ void InkDropImpl::SetHovered(bool is_hovered) { SetHighlight(ShouldHighlight(), ShouldHighlight() ? base::TimeDelta::FromMilliseconds( - kHighlightFadeInFromUserInputDurationInMs) + kHighlightFadeInFromUserInputDurationMs) : base::TimeDelta::FromMilliseconds( - kHighlightFadeOutFromUserInputDurationInMs), + kHighlightFadeOutFromUserInputDurationMs), false); } @@ -159,6 +171,7 @@ void InkDropImpl::DestroyInkDropHighlight() { void InkDropImpl::AddRootLayerToHostIfNeeded() { DCHECK(highlight_ || ink_drop_ripple_); + DCHECK(!root_layer_->children().empty()); if (!root_layer_added_to_host_) { root_layer_added_to_host_ = true; ink_drop_host_->AddInkDropLayer(root_layer_.get()); @@ -188,8 +201,15 @@ void InkDropImpl::AnimationEnded(InkDropState ink_drop_state, if (ShouldAnimateToHidden(ink_drop_state)) { ink_drop_ripple_->AnimateToState(views::InkDropState::HIDDEN); } else if (ink_drop_state == views::InkDropState::HIDDEN) { - if (is_hovered_) - StartHighlightAfterRippleTimer(); + // Re-highlight, as necessary. For hover, there's a delay; for focus, jump + // straight into the animation. + if (!IsHighlightFadingInOrVisible()) { + if (is_focused_) + HighlightAfterRippleTimerFired(); + else if (is_hovered_) + StartHighlightAfterRippleTimer(); + } + // TODO(bruthig): Investigate whether creating and destroying // InkDropRipples is expensive and consider creating an // InkDropRipplePool. See www.crbug.com/522175. @@ -214,15 +234,18 @@ void InkDropImpl::AnimationEnded(InkDropHighlight::AnimationType animation_type, void InkDropImpl::SetHighlight(bool should_highlight, base::TimeDelta animation_duration, bool explode) { - StopHighlightAfterRippleTimer(); + highlight_after_ripple_timer_.reset(); if (IsHighlightFadingInOrVisible() == should_highlight) return; if (should_highlight) { CreateInkDropHighlight(); - if (highlight_ && !(ink_drop_ripple_ && ink_drop_ripple_->IsVisible())) + if (highlight_ && + !(ink_drop_ripple_ && ink_drop_ripple_->IsVisible() && + ink_drop_ripple_->OverridesHighlight())) { highlight_->FadeIn(animation_duration); + } } else { highlight_->FadeOut(animation_duration, explode); } @@ -233,26 +256,17 @@ bool InkDropImpl::ShouldHighlight() const { } void InkDropImpl::StartHighlightAfterRippleTimer() { - StopHighlightAfterRippleTimer(); - - if (!highlight_after_ripple_timer_) - highlight_after_ripple_timer_.reset(new base::OneShotTimer); - + highlight_after_ripple_timer_.reset(new base::OneShotTimer); highlight_after_ripple_timer_->Start( FROM_HERE, - base::TimeDelta::FromMilliseconds(kHighlightFadeInAfterRippleDelayInMs), + base::TimeDelta::FromMilliseconds(kHoverFadeInAfterRippleDelayMs), base::Bind(&InkDropImpl::HighlightAfterRippleTimerFired, base::Unretained(this))); } -void InkDropImpl::StopHighlightAfterRippleTimer() { - if (highlight_after_ripple_timer_) - highlight_after_ripple_timer_.reset(); -} - void InkDropImpl::HighlightAfterRippleTimerFired() { SetHighlight(true, base::TimeDelta::FromMilliseconds( - kHighlightFadeInAfterRippleDurationInMs), + kHighlightFadeInAfterRippleDurationMs), true); highlight_after_ripple_timer_.reset(); } diff --git a/chromium/ui/views/animation/ink_drop_impl.h b/chromium/ui/views/animation/ink_drop_impl.h index 2b92d7a7589..7f4cca37911 100644 --- a/chromium/ui/views/animation/ink_drop_impl.h +++ b/chromium/ui/views/animation/ink_drop_impl.h @@ -107,9 +107,6 @@ class VIEWS_EXPORT InkDropImpl : public InkDrop, // |highlight_after_ripple_timer_| instance if it exists. void StartHighlightAfterRippleTimer(); - // Stops and destroys the current |highlight_after_ripple_timer_| instance. - void StopHighlightAfterRippleTimer(); - // Callback for when the |highlight_after_ripple_timer_| fires. void HighlightAfterRippleTimerFired(); diff --git a/chromium/ui/views/animation/ink_drop_impl_unittest.cc b/chromium/ui/views/animation/ink_drop_impl_unittest.cc index b6fc8cebb0c..b7c85dcf28b 100644 --- a/chromium/ui/views/animation/ink_drop_impl_unittest.cc +++ b/chromium/ui/views/animation/ink_drop_impl_unittest.cc @@ -8,7 +8,6 @@ #include "base/test/test_simple_task_runner.h" #include "base/threading/thread_task_runner_handle.h" #include "testing/gtest/include/gtest/gtest.h" -#include "ui/views/animation/ink_drop_ripple.h" #include "ui/views/animation/test/ink_drop_impl_test_api.h" #include "ui/views/animation/test/test_ink_drop_host.h" @@ -253,4 +252,122 @@ TEST_F(InkDropImplTest, LayersArentRemovedWhenPreemptingFadeOut) { EXPECT_EQ(1, ink_drop_host_.num_ink_drop_layers()); } +TEST_F(InkDropImplTest, AnimationWhenDeactivated) { + EXPECT_EQ(0, ink_drop_host_.num_ink_drop_layers()); + + ink_drop_.AnimateToState(InkDropState::ACTIVATED); + EXPECT_EQ(1, ink_drop_host_.num_ink_drop_layers()); + test_api_.CompleteAnimations(); + + ink_drop_.AnimateToState(InkDropState::DEACTIVATED); + EXPECT_EQ(1, ink_drop_host_.num_ink_drop_layers()); + EXPECT_TRUE(test_api_.HasActiveAnimations()); + EXPECT_FALSE(test_api_.IsHighlightFadingInOrVisible()); +} + +TEST_F(InkDropImplTest, AnimationSkippedWhenFocusedAndDeactivated) { + ink_drop_host_.set_should_show_highlight(true); + + EXPECT_EQ(0, ink_drop_host_.num_ink_drop_layers()); + + ink_drop_.SetFocused(true); + EXPECT_EQ(1, ink_drop_host_.num_ink_drop_layers()); + + ink_drop_.AnimateToState(InkDropState::ACTIVATED); + EXPECT_EQ(1, ink_drop_host_.num_ink_drop_layers()); + test_api_.CompleteAnimations(); + + ink_drop_.AnimateToState(InkDropState::DEACTIVATED); + EXPECT_EQ(1, ink_drop_host_.num_ink_drop_layers()); + EXPECT_FALSE(test_api_.HasActiveAnimations()); + EXPECT_TRUE(test_api_.IsHighlightFadingInOrVisible()); + EXPECT_EQ(InkDropState::HIDDEN, ink_drop_.GetTargetInkDropState()); +} + +TEST_F(InkDropImplTest, FocusHighlightComesBackImmediatelyAfterAction) { + ink_drop_host_.set_should_show_highlight(true); + + EXPECT_EQ(0, ink_drop_host_.num_ink_drop_layers()); + + ink_drop_.SetFocused(true); + EXPECT_EQ(1, ink_drop_host_.num_ink_drop_layers()); + + ink_drop_.AnimateToState(InkDropState::ACTION_PENDING); + EXPECT_EQ(1, ink_drop_host_.num_ink_drop_layers()); + EXPECT_FALSE(test_api_.IsHighlightFadingInOrVisible()); + + ink_drop_.AnimateToState(InkDropState::ACTION_TRIGGERED); + EXPECT_EQ(1, ink_drop_host_.num_ink_drop_layers()); + + test_api_.CompleteAnimations(); + + // No delay (unlike in the hover case). + EXPECT_FALSE(task_runner_->HasPendingTask()); + + // Highlight should be back. + EXPECT_TRUE(test_api_.IsHighlightFadingInOrVisible()); +} + +TEST_F(InkDropImplTest, HighlightCanCoexistWithRipple) { + ink_drop_host_.set_should_show_highlight(true); + ink_drop_host_.set_ripple_overrides_highlight(false); + + EXPECT_EQ(0, ink_drop_host_.num_ink_drop_layers()); + + ink_drop_.SetHovered(true); + EXPECT_EQ(1, ink_drop_host_.num_ink_drop_layers()); + + ink_drop_.AnimateToState(InkDropState::ACTION_PENDING); + EXPECT_EQ(1, ink_drop_host_.num_ink_drop_layers()); + EXPECT_TRUE(test_api_.IsHighlightFadingInOrVisible()); + + ink_drop_.AnimateToState(InkDropState::ACTION_TRIGGERED); + EXPECT_EQ(1, ink_drop_host_.num_ink_drop_layers()); + EXPECT_TRUE(test_api_.IsHighlightFadingInOrVisible()); + + test_api_.CompleteAnimations(); + + // Nothing to fade in because the highlight has always been visible. + EXPECT_FALSE(task_runner_->HasPendingTask()); + EXPECT_TRUE(test_api_.IsHighlightFadingInOrVisible()); + + // Now try with the ripple showing before the highlight comes in. + ink_drop_.AnimateToState(InkDropState::HIDDEN); + ink_drop_.SetHovered(false); + test_api_.CompleteAnimations(); + EXPECT_EQ(0, ink_drop_host_.num_ink_drop_layers()); + + ink_drop_.AnimateToState(InkDropState::ACTION_PENDING); + EXPECT_EQ(1, ink_drop_host_.num_ink_drop_layers()); + ink_drop_.SetHovered(true); + EXPECT_EQ(1, ink_drop_host_.num_ink_drop_layers()); + EXPECT_TRUE(test_api_.IsHighlightFadingInOrVisible()); +} + +TEST_F(InkDropImplTest, HighlightCanCoexistWithSnapToActivatedRipple) { + ink_drop_host_.set_should_show_highlight(true); + ink_drop_host_.set_ripple_overrides_highlight(false); + + EXPECT_EQ(0, ink_drop_host_.num_ink_drop_layers()); + + ink_drop_.SetHovered(true); + EXPECT_EQ(1, ink_drop_host_.num_ink_drop_layers()); + + ink_drop_.SnapToActivated(); + EXPECT_EQ(1, ink_drop_host_.num_ink_drop_layers()); + EXPECT_TRUE(test_api_.IsHighlightFadingInOrVisible()); + + // Now try with the ripple showing before the highlight comes in. + ink_drop_.AnimateToState(InkDropState::HIDDEN); + ink_drop_.SetHovered(false); + test_api_.CompleteAnimations(); + EXPECT_EQ(0, ink_drop_host_.num_ink_drop_layers()); + + ink_drop_.SnapToActivated(); + EXPECT_EQ(1, ink_drop_host_.num_ink_drop_layers()); + ink_drop_.SetHovered(true); + EXPECT_EQ(1, ink_drop_host_.num_ink_drop_layers()); + EXPECT_TRUE(test_api_.IsHighlightFadingInOrVisible()); +} + } // namespace views diff --git a/chromium/ui/views/animation/ink_drop_painted_layer_delegates.cc b/chromium/ui/views/animation/ink_drop_painted_layer_delegates.cc index 0ca68999ba9..75609d7a2fc 100644 --- a/chromium/ui/views/animation/ink_drop_painted_layer_delegates.cc +++ b/chromium/ui/views/animation/ink_drop_painted_layer_delegates.cc @@ -6,7 +6,7 @@ #include "third_party/skia/include/core/SkDrawLooper.h" #include "third_party/skia/include/core/SkPaint.h" -#include "third_party/skia/include/core/SkPath.h" +#include "third_party/skia/include/core/SkRRect.h" #include "ui/compositor/paint_recorder.h" #include "ui/gfx/canvas.h" #include "ui/gfx/color_palette.h" @@ -133,10 +133,12 @@ void RoundedRectangleLayerDelegate::OnPaintLayer( BorderShadowLayerDelegate::BorderShadowLayerDelegate( const std::vector<gfx::ShadowValue>& shadows, const gfx::Rect& shadowed_area_bounds, + SkColor fill_color, int corner_radius) : BasePaintedLayerDelegate(gfx::kPlaceholderColor), shadows_(shadows), bounds_(shadowed_area_bounds), + fill_color_(fill_color), corner_radius_(corner_radius) {} BorderShadowLayerDelegate::~BorderShadowLayerDelegate() {} @@ -153,25 +155,24 @@ gfx::Vector2dF BorderShadowLayerDelegate::GetCenteringOffset() const { void BorderShadowLayerDelegate::OnPaintLayer(const ui::PaintContext& context) { SkPaint paint; - paint.setLooper(gfx::CreateShadowDrawLooperCorrectBlur(shadows_)); - paint.setStyle(SkPaint::kStrokeAndFill_Style); + paint.setStyle(SkPaint::kFill_Style); paint.setAntiAlias(true); - paint.setColor(SK_ColorTRANSPARENT); - paint.setStrokeJoin(SkPaint::kRound_Join); - SkRect path_bounds = gfx::RectFToSkRect( - gfx::RectF(bounds_ - GetPaintedBounds().OffsetFromOrigin())); + paint.setColor(fill_color_); + + gfx::RectF rrect_bounds = + gfx::RectF(bounds_ - GetPaintedBounds().OffsetFromOrigin()); + SkRRect r_rect = SkRRect::MakeRectXY(gfx::RectFToSkRect(rrect_bounds), + corner_radius_, corner_radius_); + // First the fill color. ui::PaintRecorder recorder(context, GetPaintedBounds().size()); - const SkScalar corner = SkFloatToScalar(corner_radius_); - SkPath path; - path.addRoundRect(path_bounds, corner, corner, SkPath::kCCW_Direction); - recorder.canvas()->DrawPath(path, paint); - - SkPaint clear_paint; - clear_paint.setAntiAlias(true); - clear_paint.setXfermodeMode(SkXfermode::kClear_Mode); - recorder.canvas()->sk_canvas()->drawRoundRect(path_bounds, corner, corner, - clear_paint); + recorder.canvas()->sk_canvas()->drawRRect(r_rect, paint); + + // Now the shadow. + paint.setLooper(gfx::CreateShadowDrawLooperCorrectBlur(shadows_)); + recorder.canvas()->sk_canvas()->clipRRect(r_rect, SkRegion::kDifference_Op, + true); + recorder.canvas()->sk_canvas()->drawRRect(r_rect, paint); } } // namespace views diff --git a/chromium/ui/views/animation/ink_drop_painted_layer_delegates.h b/chromium/ui/views/animation/ink_drop_painted_layer_delegates.h index 56148d28015..c4cab4f310b 100644 --- a/chromium/ui/views/animation/ink_drop_painted_layer_delegates.h +++ b/chromium/ui/views/animation/ink_drop_painted_layer_delegates.h @@ -114,11 +114,12 @@ class VIEWS_EXPORT RoundedRectangleLayerDelegate }; // A BasePaintedLayerDelegate that paints a shadow around the outside of a -// specified roundrect. +// specified roundrect, and also fills the round rect. class VIEWS_EXPORT BorderShadowLayerDelegate : public BasePaintedLayerDelegate { public: BorderShadowLayerDelegate(const std::vector<gfx::ShadowValue>& shadows, const gfx::Rect& shadowed_area_bounds, + SkColor fill_color, int corner_radius); ~BorderShadowLayerDelegate() override; @@ -133,9 +134,11 @@ class VIEWS_EXPORT BorderShadowLayerDelegate : public BasePaintedLayerDelegate { const std::vector<gfx::ShadowValue> shadows_; // The bounds of the shadowed area. - gfx::Rect bounds_; + const gfx::Rect bounds_; - int corner_radius_; + const SkColor fill_color_; + + const int corner_radius_; DISALLOW_COPY_AND_ASSIGN(BorderShadowLayerDelegate); }; diff --git a/chromium/ui/views/animation/ink_drop_ripple.cc b/chromium/ui/views/animation/ink_drop_ripple.cc index 06e38439bbe..9b4ee3c151f 100644 --- a/chromium/ui/views/animation/ink_drop_ripple.cc +++ b/chromium/ui/views/animation/ink_drop_ripple.cc @@ -84,6 +84,10 @@ void InkDropRipple::SnapToActivated() { animation_observer->SetActive(); } +bool InkDropRipple::IsVisible() { + return GetRootLayer()->visible(); +} + void InkDropRipple::HideImmediately() { AbortAllAnimations(); SetStateToHidden(); diff --git a/chromium/ui/views/animation/ink_drop_ripple.h b/chromium/ui/views/animation/ink_drop_ripple.h index efc4972e904..5b4a12d3957 100644 --- a/chromium/ui/views/animation/ink_drop_ripple.h +++ b/chromium/ui/views/animation/ink_drop_ripple.h @@ -78,7 +78,10 @@ class VIEWS_EXPORT InkDropRipple { // Returns true when the ripple is visible. This is different from checking if // the ink_drop_state() == HIDDEN because the ripple may be visible while it // animates to the target HIDDEN state. - virtual bool IsVisible() const = 0; + bool IsVisible(); + + // Returns true if this ripple is mutually exclusive with InkDropHighlight. + virtual bool OverridesHighlight() const = 0; // Returns a test api to access internals of this. Default implmentations // should return nullptr and test specific subclasses can override to return diff --git a/chromium/ui/views/animation/square_ink_drop_ripple.cc b/chromium/ui/views/animation/square_ink_drop_ripple.cc index a01bbfd12bb..49fd5512e24 100644 --- a/chromium/ui/views/animation/square_ink_drop_ripple.cc +++ b/chromium/ui/views/animation/square_ink_drop_ripple.cc @@ -214,8 +214,8 @@ ui::Layer* SquareInkDropRipple::GetRootLayer() { return &root_layer_; } -bool SquareInkDropRipple::IsVisible() const { - return root_layer_.visible(); +bool SquareInkDropRipple::OverridesHighlight() const { + return true; } float SquareInkDropRipple::GetCurrentOpacity() const { diff --git a/chromium/ui/views/animation/square_ink_drop_ripple.h b/chromium/ui/views/animation/square_ink_drop_ripple.h index 059d8395e45..293d46a03e9 100644 --- a/chromium/ui/views/animation/square_ink_drop_ripple.h +++ b/chromium/ui/views/animation/square_ink_drop_ripple.h @@ -67,7 +67,7 @@ class VIEWS_EXPORT SquareInkDropRipple : public InkDropRipple { // InkDropRipple: void SnapToActivated() override; ui::Layer* GetRootLayer() override; - bool IsVisible() const override; + bool OverridesHighlight() const override; private: friend class test::SquareInkDropRippleTestApi; diff --git a/chromium/ui/views/border.cc b/chromium/ui/views/border.cc index f2eb92ff111..153411dc40d 100644 --- a/chromium/ui/views/border.cc +++ b/chromium/ui/views/border.cc @@ -187,20 +187,19 @@ std::unique_ptr<Border> Border::NullBorder() { // static std::unique_ptr<Border> Border::CreateSolidBorder(int thickness, SkColor color) { - return base::WrapUnique(new SolidSidedBorder(gfx::Insets(thickness), color)); + return base::MakeUnique<SolidSidedBorder>(gfx::Insets(thickness), color); } // static std::unique_ptr<Border> Border::CreateEmptyBorder(const gfx::Insets& insets) { - return base::WrapUnique(new EmptyBorder(insets)); + return base::MakeUnique<EmptyBorder>(insets); } // static std::unique_ptr<Border> Border::CreateRoundedRectBorder(int thickness, int corner_radius, SkColor color) { - return base::WrapUnique( - new RoundedRectBorder(thickness, corner_radius, color)); + return base::MakeUnique<RoundedRectBorder>(thickness, corner_radius, color); } // static @@ -217,8 +216,8 @@ std::unique_ptr<Border> Border::CreateSolidSidedBorder(int top, int bottom, int right, SkColor color) { - return base::WrapUnique( - new SolidSidedBorder(gfx::Insets(top, left, bottom, right), color)); + return base::MakeUnique<SolidSidedBorder>( + gfx::Insets(top, left, bottom, right), color); } // static diff --git a/chromium/ui/views/bubble/bubble_border.cc b/chromium/ui/views/bubble/bubble_border.cc index f81bb2d7618..d7c7149b9f2 100644 --- a/chromium/ui/views/bubble/bubble_border.cc +++ b/chromium/ui/views/bubble/bubble_border.cc @@ -7,12 +7,15 @@ #include <algorithm> #include "base/logging.h" +#include "third_party/skia/include/core/SkDrawLooper.h" #include "third_party/skia/include/core/SkPaint.h" #include "third_party/skia/include/core/SkPath.h" +#include "ui/base/material_design/material_design_controller.h" #include "ui/base/resource/resource_bundle.h" #include "ui/gfx/canvas.h" #include "ui/gfx/geometry/rect.h" #include "ui/gfx/path.h" +#include "ui/gfx/scoped_canvas.h" #include "ui/gfx/skia_util.h" #include "ui/resources/grit/ui_resources.h" #include "ui/views/painter.h" @@ -57,6 +60,37 @@ BorderImages::~BorderImages() {} namespace { +// Blur and offset values for the two shadows drawn around each dialog. The +// values are all in dip. +const int kSmallShadowVerticalOffset = 2; +const int kSmallShadowBlur = 4; +const SkColor kSmallShadowColor = SkColorSetA(SK_ColorBLACK, 0x33); + +const int kLargeShadowVerticalOffset = 2; +const int kLargeShadowBlur = 6; +const SkColor kLargeShadowColor = SkColorSetA(SK_ColorBLACK, 0x1A); + +bool UseMd() { + return ui::MaterialDesignController::IsSecondaryUiMaterial(); +} + +// Utility functions for getting alignment points on the edge of a rectangle. +gfx::Point CenterTop(const gfx::Rect& rect) { + return gfx::Point(rect.CenterPoint().x(), rect.y()); +} + +gfx::Point CenterBottom(const gfx::Rect& rect) { + return gfx::Point(rect.CenterPoint().x(), rect.bottom()); +} + +gfx::Point LeftCenter(const gfx::Rect& rect) { + return gfx::Point(rect.x(), rect.CenterPoint().y()); +} + +gfx::Point RightCenter(const gfx::Rect& rect) { + return gfx::Point(rect.right(), rect.CenterPoint().y()); +} + // Bubble border and arrow image resource ids. They don't use the IMAGE_GRID // macro because there is no center image. const int kNoShadowImages[] = { @@ -143,6 +177,7 @@ BubbleBorder::BubbleBorder(Arrow arrow, Shadow shadow, SkColor color) arrow_paint_type_(PAINT_NORMAL), alignment_(ALIGN_ARROW_TO_MID_ANCHOR), shadow_(shadow), + images_(nullptr), background_color_(color), use_theme_background_color_(false) { #if defined(OS_MACOSX) @@ -151,13 +186,51 @@ BubbleBorder::BubbleBorder(Arrow arrow, Shadow shadow, SkColor color) shadow_ = NO_ASSETS; #endif // OS_MACOSX DCHECK(shadow_ < SHADOW_COUNT); - images_ = GetBorderImages(shadow_); + if (UseMd()) { + // Harmony bubbles don't use arrows. + alignment_ = ALIGN_EDGE_TO_ANCHOR_EDGE; + arrow_paint_type_ = PAINT_NONE; + } else { + images_ = GetBorderImages(shadow_); + } } BubbleBorder::~BubbleBorder() {} +void BubbleBorder::set_paint_arrow(ArrowPaintType value) { + if (UseMd()) + return; + arrow_paint_type_ = value; +} + gfx::Rect BubbleBorder::GetBounds(const gfx::Rect& anchor_rect, const gfx::Size& contents_size) const { + // In MD, there are no arrows, so positioning logic is significantly simpler. + // TODO(estade): handle more anchor positions. + if (UseMd() && + (arrow_ == TOP_RIGHT || arrow_ == TOP_LEFT || arrow_ == BOTTOM_CENTER || + arrow_ == LEFT_CENTER || arrow_ == RIGHT_CENTER)) { + gfx::Rect contents_bounds(contents_size); + if (arrow_ == TOP_RIGHT) { + contents_bounds += + anchor_rect.bottom_right() - contents_bounds.top_right(); + } else if (arrow_ == TOP_LEFT) { + contents_bounds += + anchor_rect.bottom_left() - contents_bounds.origin(); + } else if (arrow_ == BOTTOM_CENTER) { + contents_bounds += CenterTop(anchor_rect) - CenterBottom(contents_bounds); + } else if (arrow_ == LEFT_CENTER) { + contents_bounds += RightCenter(anchor_rect) - LeftCenter(contents_bounds); + } else if (arrow_ == RIGHT_CENTER) { + contents_bounds += LeftCenter(anchor_rect) - RightCenter(contents_bounds); + } + contents_bounds.Inset(-GetInsets()); + // |arrow_offset_| is used to adjust bubbles that would normally be + // partially offscreen. + contents_bounds += gfx::Vector2d(-arrow_offset_, 0); + return contents_bounds; + } + int x = anchor_rect.x(); int y = anchor_rect.y(); int w = anchor_rect.width(); @@ -166,7 +239,7 @@ gfx::Rect BubbleBorder::GetBounds(const gfx::Rect& anchor_rect, const int arrow_offset = GetArrowOffset(size); // |arrow_shift| is necessary to visually align the tip of the bubble arrow // with the anchor point. This shift is an inverse of the shadow thickness. - int arrow_shift = + int arrow_shift = UseMd() ? 0 : images_->arrow_interior_thickness + kStroke - images_->arrow_thickness; // When arrow is painted transparently the visible border of the bubble needs // to be positioned at the same bounds as when the arrow is shown. @@ -206,14 +279,20 @@ gfx::Rect BubbleBorder::GetBounds(const gfx::Rect& anchor_rect, } int BubbleBorder::GetBorderThickness() const { - return images_->border_thickness - images_->border_interior_thickness; + // TODO(estade): this shouldn't be called in MD. + return UseMd() + ? 0 + : images_->border_thickness - images_->border_interior_thickness; } int BubbleBorder::GetBorderCornerRadius() const { - return images_->corner_radius; + return UseMd() ? 3 : images_->corner_radius; } int BubbleBorder::GetArrowOffset(const gfx::Size& border_size) const { + if (UseMd()) + return 0; + const int edge_length = is_arrow_on_horizontal(arrow_) ? border_size.width() : border_size.height(); if (is_arrow_at_center(arrow_) && arrow_offset_ == 0) @@ -234,7 +313,18 @@ bool BubbleBorder::GetArrowPath(const gfx::Rect& view_bounds, return true; } +void BubbleBorder::SetBorderInteriorThickness(int border_interior_thickness) { + // TODO(estade): remove this function. + DCHECK(!UseMd()); + images_->border_interior_thickness = border_interior_thickness; + if (!has_arrow(arrow_) || arrow_paint_type_ != PAINT_NORMAL) + images_->border_thickness = border_interior_thickness; +} + void BubbleBorder::Paint(const views::View& view, gfx::Canvas* canvas) { + if (UseMd()) + return PaintMd(view, canvas); + gfx::Rect bounds(view.GetContentsBounds()); bounds.Inset(-GetBorderThickness(), -GetBorderThickness()); const gfx::Rect arrow_bounds = GetArrowRect(view.GetLocalBounds()); @@ -250,8 +340,7 @@ void BubbleBorder::Paint(const views::View& view, gfx::Canvas* canvas) { // Clip the arrow bounds out to avoid painting the overlapping edge area. canvas->Save(); - SkRect arrow_rect(gfx::RectToSkRect(arrow_bounds)); - canvas->sk_canvas()->clipRect(arrow_rect, SkRegion::kDifference_Op); + canvas->ClipRect(arrow_bounds, SkRegion::kDifference_Op); Painter::PaintPainterAt(canvas, images_->border_painter.get(), bounds); canvas->Restore(); @@ -259,6 +348,13 @@ void BubbleBorder::Paint(const views::View& view, gfx::Canvas* canvas) { } gfx::Insets BubbleBorder::GetInsets() const { + if (UseMd()) { + gfx::Insets blur(kLargeShadowBlur); + gfx::Insets offset(-kLargeShadowVerticalOffset, 0, + kLargeShadowVerticalOffset, 0); + return blur + offset; + } + // The insets contain the stroke and shadow pixels outside the bubble fill. const int inset = GetBorderThickness(); if (arrow_paint_type_ != PAINT_NORMAL || !has_arrow(arrow_)) @@ -284,6 +380,8 @@ gfx::Size BubbleBorder::GetSizeForContentsSize( gfx::Size size(contents_size); const gfx::Insets insets = GetInsets(); size.Enlarge(insets.width(), insets.height()); + if (UseMd()) + return size; // Ensure the bubble is large enough to not overlap border and arrow images. const int min = 2 * images_->border_thickness; @@ -355,6 +453,7 @@ gfx::Rect BubbleBorder::GetArrowRect(const gfx::Rect& bounds) const { void BubbleBorder::GetArrowPathFromArrowBounds(const gfx::Rect& arrow_bounds, SkPath* path) const { + DCHECK(!UseMd()); const bool horizontal = is_arrow_on_horizontal(arrow_); const int thickness = images_->arrow_interior_thickness; float tip_x = horizontal ? arrow_bounds.CenterPoint().x() : @@ -380,6 +479,7 @@ void BubbleBorder::GetArrowPathFromArrowBounds(const gfx::Rect& arrow_bounds, void BubbleBorder::DrawArrow(gfx::Canvas* canvas, const gfx::Rect& arrow_bounds) const { + DCHECK(!UseMd()); canvas->DrawImageInt(*GetArrowImage(), arrow_bounds.x(), arrow_bounds.y()); SkPath path; GetArrowPathFromArrowBounds(arrow_bounds, &path); @@ -390,6 +490,38 @@ void BubbleBorder::DrawArrow(gfx::Canvas* canvas, canvas->DrawPath(path, paint); } +void BubbleBorder::PaintMd(const View& view, gfx::Canvas* canvas) { + gfx::ScopedCanvas scoped(canvas); + + SkPaint paint; + std::vector<gfx::ShadowValue> shadows; + // gfx::ShadowValue counts blur pixels both inside and outside the shape, + // whereas these blur values only describe the outside portion, hence they + // must be doubled. + shadows.emplace_back(gfx::Vector2d(0, kSmallShadowVerticalOffset), + 2 * kSmallShadowBlur, kSmallShadowColor); + shadows.emplace_back(gfx::Vector2d(0, kLargeShadowVerticalOffset), + 2 * kLargeShadowBlur, kLargeShadowColor); + paint.setLooper(gfx::CreateShadowDrawLooperCorrectBlur(shadows)); + paint.setColor(SkColorSetA(SK_ColorBLACK, 0x26)); + paint.setAntiAlias(true); + + gfx::Rect bounds(view.GetLocalBounds()); + bounds.Inset(GetInsets()); + SkRRect r_rect = + SkRRect::MakeRectXY(gfx::RectToSkRect(bounds), GetBorderCornerRadius(), + GetBorderCornerRadius()); + // Clip out a round rect so the fill and shadow don't draw over the contents + // of the bubble. + SkRRect clip_r_rect = r_rect; + // Stroke width is a single pixel at any scale factor. + const SkScalar one_pixel = SkFloatToScalar(1 / canvas->image_scale()); + clip_r_rect.inset(one_pixel, one_pixel); + canvas->sk_canvas()->clipRRect(clip_r_rect, SkRegion::kDifference_Op, + true /*doAntiAlias*/); + canvas->sk_canvas()->drawRRect(r_rect, paint); +} + internal::BorderImages* BubbleBorder::GetImagesForTest() const { return images_; } @@ -404,8 +536,14 @@ void BubbleBackground::Paint(gfx::Canvas* canvas, views::View* view) const { paint.setStyle(SkPaint::kFill_Style); paint.setColor(border_->background_color()); SkPath path; - gfx::Rect bounds(view->GetLocalBounds()); - bounds.Inset(border_->GetInsets()); + gfx::RectF bounds(view->GetLocalBounds()); + bounds.Inset(gfx::InsetsF(border_->GetInsets())); + if (UseMd()) { + // The border is 1px at all scale factors. Leave room for it. It's partially + // transparent, so we don't want to draw any background underneath it. + const SkScalar one_pixel = SkFloatToScalar(1 / canvas->image_scale()); + bounds.Inset(gfx::InsetsF(one_pixel)); + } canvas->DrawRoundRect(bounds, border_->GetBorderCornerRadius(), paint); } diff --git a/chromium/ui/views/bubble/bubble_border.h b/chromium/ui/views/bubble/bubble_border.h index 2ebdf9c6156..181f5daee38 100644 --- a/chromium/ui/views/bubble/bubble_border.h +++ b/chromium/ui/views/bubble/bubble_border.h @@ -112,6 +112,7 @@ class VIEWS_EXPORT BubbleBorder : public Border { }; // The way the arrow should be painted. + // TODO(estade): Harmony doesn't use this enum; remove it. enum ArrowPaintType { // Fully render the arrow. PAINT_NORMAL, @@ -182,7 +183,7 @@ class VIEWS_EXPORT BubbleBorder : public Border { void set_arrow_offset(int offset) { arrow_offset_ = offset; } // Sets the way the arrow is actually painted. Default is PAINT_NORMAL. - void set_paint_arrow(ArrowPaintType value) { arrow_paint_type_ = value; } + void set_paint_arrow(ArrowPaintType value); // Get the desired widget bounds (in screen coordinates) given the anchor rect // and bubble content size; calculated from shadow and arrow image dimensions. @@ -205,6 +206,10 @@ class VIEWS_EXPORT BubbleBorder : public Border { // The returned path does not account for arrow stroke and shadow. bool GetArrowPath(const gfx::Rect& view_bounds, gfx::Path* path) const; + // Sets border thickness overriding the thickness set on |images_| creation. + // May only be invoked after |arrow_paint_type_| has been set. + void SetBorderInteriorThickness(int border_interior_thickness); + // Overridden from Border: void Paint(const View& view, gfx::Canvas* canvas) override; gfx::Insets GetInsets() const override; @@ -225,6 +230,8 @@ class VIEWS_EXPORT BubbleBorder : public Border { SkPath* path) const; void DrawArrow(gfx::Canvas* canvas, const gfx::Rect& arrow_bounds) const; + void PaintMd(const View& view, gfx::Canvas* canvas); + internal::BorderImages* GetImagesForTest() const; Arrow arrow_; diff --git a/chromium/ui/views/bubble/bubble_dialog_delegate.cc b/chromium/ui/views/bubble/bubble_dialog_delegate.cc index 066626b81da..77994311514 100644 --- a/chromium/ui/views/bubble/bubble_dialog_delegate.cc +++ b/chromium/ui/views/bubble/bubble_dialog_delegate.cc @@ -6,6 +6,7 @@ #include "build/build_config.h" #include "ui/accessibility/ax_view_state.h" +#include "ui/base/material_design/material_design_controller.h" #include "ui/base/resource/resource_bundle.h" #include "ui/gfx/color_utils.h" #include "ui/gfx/geometry/rect.h" @@ -189,6 +190,11 @@ void BubbleDialogDelegateView::SetArrowPaintType( SizeToContents(); } +void BubbleDialogDelegateView::SetBorderInteriorThickness(int thickness) { + GetBubbleFrameView()->bubble_border()->SetBorderInteriorThickness(thickness); + SizeToContents(); +} + void BubbleDialogDelegateView::OnAnchorBoundsChanged() { SizeToContents(); } @@ -289,10 +295,15 @@ void BubbleDialogDelegateView::UpdateColorsFromTheme( const ui::NativeTheme* theme) { if (!color_explicitly_set_) color_ = theme->GetSystemColor(ui::NativeTheme::kColorId_BubbleBackground); - set_background(Background::CreateSolidBackground(color())); BubbleFrameView* frame_view = GetBubbleFrameView(); if (frame_view) frame_view->bubble_border()->set_background_color(color()); + + // When there's an opaque layer, the bubble border background won't show + // through, so explicitly paint a background color. + set_background(layer() && layer()->fills_bounds_opaquely() + ? Background::CreateSolidBackground(color()) + : nullptr); } void BubbleDialogDelegateView::HandleVisibilityChanged(Widget* widget, diff --git a/chromium/ui/views/bubble/bubble_dialog_delegate.h b/chromium/ui/views/bubble/bubble_dialog_delegate.h index e9bccb66883..4f791cd7358 100644 --- a/chromium/ui/views/bubble/bubble_dialog_delegate.h +++ b/chromium/ui/views/bubble/bubble_dialog_delegate.h @@ -99,7 +99,7 @@ class VIEWS_EXPORT BubbleDialogDelegateView : public DialogDelegateView, virtual gfx::Rect GetAnchorRect() const; // Allows delegates to provide custom parameters before widget initialization. - // For example, mus needs to set a custom mus::Window* parent. + // For example, mus needs to set a custom ui::Window* parent. virtual void OnBeforeBubbleWidgetInit(Widget::InitParams* params, Widget* widget) const; @@ -113,6 +113,9 @@ class VIEWS_EXPORT BubbleDialogDelegateView : public DialogDelegateView, // Sets the bubble arrow paint type. void SetArrowPaintType(BubbleBorder::ArrowPaintType paint_type); + // Sets the bubble border interior thickness. + void SetBorderInteriorThickness(int thickness); + // Call this method when the anchor bounds have changed to reposition the // bubble. The bubble is automatically repositioned when the anchor view // bounds change as a result of the widget's bounds changing. diff --git a/chromium/ui/views/bubble/bubble_dialog_delegate_unittest.cc b/chromium/ui/views/bubble/bubble_dialog_delegate_unittest.cc index ea787f8d094..d64355810fe 100644 --- a/chromium/ui/views/bubble/bubble_dialog_delegate_unittest.cc +++ b/chromium/ui/views/bubble/bubble_dialog_delegate_unittest.cc @@ -321,7 +321,7 @@ TEST_F(BubbleDialogDelegateTest, CloseMethods) { BubbleDialogDelegateView::CreateBubble(bubble_delegate); bubble_widget->Show(); BubbleFrameView* frame_view = bubble_delegate->GetBubbleFrameView(); - LabelButton* close_button = frame_view->close_; + Button* close_button = frame_view->close_; ASSERT_TRUE(close_button); frame_view->ButtonPressed( close_button, diff --git a/chromium/ui/views/bubble/bubble_frame_view.cc b/chromium/ui/views/bubble/bubble_frame_view.cc index 43bac842f05..b61cb89cef1 100644 --- a/chromium/ui/views/bubble/bubble_frame_view.cc +++ b/chromium/ui/views/bubble/bubble_frame_view.cc @@ -11,6 +11,7 @@ #include "ui/base/default_style.h" #include "ui/base/hit_test.h" #include "ui/base/l10n/l10n_util.h" +#include "ui/base/material_design/material_design_controller.h" #include "ui/base/resource/resource_bundle.h" #include "ui/compositor/paint_context.h" #include "ui/compositor/paint_recorder.h" @@ -19,11 +20,13 @@ #include "ui/gfx/geometry/vector2d.h" #include "ui/gfx/path.h" #include "ui/gfx/skia_util.h" +#include "ui/gfx/vector_icons_public.h" #include "ui/native_theme/native_theme.h" #include "ui/resources/grit/ui_resources.h" #include "ui/strings/grit/ui_strings.h" #include "ui/views/bubble/bubble_border.h" #include "ui/views/controls/button/label_button.h" +#include "ui/views/controls/button/vector_icon_button.h" #include "ui/views/controls/image_view.h" #include "ui/views/layout/box_layout.h" #include "ui/views/layout/layout_constants.h" @@ -93,29 +96,39 @@ BubbleFrameView::BubbleFrameView(const gfx::Insets& title_margins, close_ = CreateCloseButton(this); close_->SetVisible(false); +#if defined(OS_WIN) + // Windows will automatically create a tooltip for the close button based on + // the HTCLOSE result from NonClientHitTest(). + close_->SetTooltipText(base::string16()); +#endif AddChildView(close_); } BubbleFrameView::~BubbleFrameView() {} // static -LabelButton* BubbleFrameView::CreateCloseButton(ButtonListener* listener) { - ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance(); - LabelButton* close = new LabelButton(listener, base::string16()); - close->SetImage(CustomButton::STATE_NORMAL, - *rb.GetImageNamed(IDR_CLOSE_DIALOG).ToImageSkia()); - close->SetImage(CustomButton::STATE_HOVERED, - *rb.GetImageNamed(IDR_CLOSE_DIALOG_H).ToImageSkia()); - close->SetImage(CustomButton::STATE_PRESSED, - *rb.GetImageNamed(IDR_CLOSE_DIALOG_P).ToImageSkia()); - close->SetBorder(nullptr); - close->SetSize(close->GetPreferredSize()); -#if !defined(OS_WIN) - // Windows will automatically create a tooltip for the close button based on - // the HTCLOSE result from NonClientHitTest(). - close->SetTooltipText(l10n_util::GetStringUTF16(IDS_APP_CLOSE)); -#endif - return close; +Button* BubbleFrameView::CreateCloseButton(VectorIconButtonDelegate* delegate) { + Button* close_button = nullptr; + if (ui::MaterialDesignController::IsSecondaryUiMaterial()) { + VectorIconButton* close = new VectorIconButton(delegate); + close->SetIcon(gfx::VectorIconId::BAR_CLOSE); + close->SetSize(close->GetPreferredSize()); + close_button = close; + } else { + ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance(); + LabelButton* close = new LabelButton(delegate, base::string16()); + close->SetImage(CustomButton::STATE_NORMAL, + *rb.GetImageNamed(IDR_CLOSE_DIALOG).ToImageSkia()); + close->SetImage(CustomButton::STATE_HOVERED, + *rb.GetImageNamed(IDR_CLOSE_DIALOG_H).ToImageSkia()); + close->SetImage(CustomButton::STATE_PRESSED, + *rb.GetImageNamed(IDR_CLOSE_DIALOG_P).ToImageSkia()); + close->SetBorder(nullptr); + close->SetSize(close->GetPreferredSize()); + close_button = close; + } + close_button->SetTooltipText(l10n_util::GetStringUTF16(IDS_APP_CLOSE)); + return close_button; } gfx::Rect BubbleFrameView::GetBoundsForClientView() const { @@ -244,7 +257,10 @@ gfx::Insets BubbleFrameView::GetInsets() const { const bool has_title = icon_height > 0 || label_height > 0; const int title_padding = has_title ? title_margins_.height() : 0; const int title_height = std::max(icon_height, label_height) + title_padding; - const int close_height = close_->visible() ? close_->height() : 0; + const int close_height = + GetWidget()->widget_delegate()->ShouldShowCloseButton() + ? close_->height() + : 0; insets += gfx::Insets(std::max(title_height, close_height), 0, 0, 0); return insets; } diff --git a/chromium/ui/views/bubble/bubble_frame_view.h b/chromium/ui/views/bubble/bubble_frame_view.h index 90f1030c7d9..e15c77991a8 100644 --- a/chromium/ui/views/bubble/bubble_frame_view.h +++ b/chromium/ui/views/bubble/bubble_frame_view.h @@ -9,7 +9,7 @@ #include "base/gtest_prod_util.h" #include "base/macros.h" #include "ui/gfx/geometry/insets.h" -#include "ui/views/controls/button/button.h" +#include "ui/views/controls/button/vector_icon_button_delegate.h" #include "ui/views/window/non_client_view.h" namespace gfx { @@ -19,13 +19,13 @@ class FontList; namespace views { class Label; -class LabelButton; +class Button; class BubbleBorder; class ImageView; // The non-client frame view of bubble-styled widgets. class VIEWS_EXPORT BubbleFrameView : public NonClientFrameView, - public ButtonListener { + public VectorIconButtonDelegate { public: // Internal class name. static const char kViewClassName[]; @@ -35,7 +35,7 @@ class VIEWS_EXPORT BubbleFrameView : public NonClientFrameView, ~BubbleFrameView() override; // Creates a close button used in the corner of the dialog. - static LabelButton* CreateCloseButton(ButtonListener* listener); + static Button* CreateCloseButton(VectorIconButtonDelegate* delegate); // NonClientFrameView overrides: gfx::Rect GetBoundsForClientView() const override; @@ -65,7 +65,7 @@ class VIEWS_EXPORT BubbleFrameView : public NonClientFrameView, void OnThemeChanged() override; void OnNativeThemeChanged(const ui::NativeTheme* theme) override; - // Overridden from ButtonListener: + // Overridden from VectorIconButtonDelegate: void ButtonPressed(Button* sender, const ui::Event& event) override; // Use bubble_border() and SetBubbleBorder(), not border() and SetBorder(). @@ -85,7 +85,7 @@ class VIEWS_EXPORT BubbleFrameView : public NonClientFrameView, bool close_button_clicked() const { return close_button_clicked_; } - LabelButton* GetCloseButtonForTest() { return close_; } + Button* GetCloseButtonForTest() { return close_; } protected: // Returns the available screen bounds if the frame were to show in |rect|. @@ -125,7 +125,7 @@ class VIEWS_EXPORT BubbleFrameView : public NonClientFrameView, // The optional title icon, title, and (x) close button. views::ImageView* title_icon_; Label* title_; - LabelButton* close_; + Button* close_; // A view to contain the footnote view, if it exists. View* footnote_container_; diff --git a/chromium/ui/views/bubble/bubble_frame_view_unittest.cc b/chromium/ui/views/bubble/bubble_frame_view_unittest.cc index 9e338c563d8..bf955dd451c 100644 --- a/chromium/ui/views/bubble/bubble_frame_view_unittest.cc +++ b/chromium/ui/views/bubble/bubble_frame_view_unittest.cc @@ -12,6 +12,7 @@ #include "ui/gfx/geometry/rect.h" #include "ui/gfx/geometry/size.h" #include "ui/views/bubble/bubble_border.h" +#include "ui/views/controls/button/label_button.h" #include "ui/views/test/test_views.h" #include "ui/views/test/views_test_base.h" #include "ui/views/widget/widget.h" @@ -61,9 +62,15 @@ class TestBubbleFrameViewWidgetDelegate : public WidgetDelegate { return contents_view_; } + bool ShouldShowCloseButton() const override { return should_show_close_; } + void SetShouldShowCloseButton(bool should_show_close) { + should_show_close_ = should_show_close; + } + private: Widget* widget_; View* contents_view_ = nullptr; // Owned by |widget_|. + bool should_show_close_ = false; }; class TestBubbleFrameView : public BubbleFrameView { @@ -98,6 +105,10 @@ class TestBubbleFrameView : public BubbleFrameView { return available_bounds_; } + TestBubbleFrameViewWidgetDelegate* widget_delegate() { + return widget_delegate_.get(); + } + private: ViewsTestBase* test_base_; @@ -124,6 +135,24 @@ TEST_F(BubbleFrameViewTest, GetBoundsForClientView) { EXPECT_EQ(insets.top() + margin_y, frame.GetBoundsForClientView().y()); } +TEST_F(BubbleFrameViewTest, GetBoundsForClientViewWithClose) { + TestBubbleFrameView frame(this); + // TestBubbleFrameView::GetWidget() is responsible for creating the widget and + // widget delegate at first call, so it is called here for that side-effect. + ignore_result(frame.GetWidget()); + frame.widget_delegate()->SetShouldShowCloseButton(true); + frame.ResetWindowControls(); + EXPECT_EQ(kArrow, frame.bubble_border()->arrow()); + EXPECT_EQ(kColor, frame.bubble_border()->background_color()); + + int margin_x = frame.content_margins().left(); + int margin_y = frame.content_margins().top() + + frame.GetCloseButtonForTest()->height(); + gfx::Insets insets = frame.bubble_border()->GetInsets(); + EXPECT_EQ(insets.left() + margin_x, frame.GetBoundsForClientView().x()); + EXPECT_EQ(insets.top() + margin_y, frame.GetBoundsForClientView().y()); +} + // Tests that the arrow is mirrored as needed to better fit the screen. TEST_F(BubbleFrameViewTest, GetUpdatedWindowBounds) { TestBubbleFrameView frame(this); diff --git a/chromium/ui/views/bubble/tray_bubble_view.cc b/chromium/ui/views/bubble/tray_bubble_view.cc index 876c6c28dbe..059458a0e64 100644 --- a/chromium/ui/views/bubble/tray_bubble_view.cc +++ b/chromium/ui/views/bubble/tray_bubble_view.cc @@ -200,6 +200,7 @@ TrayBubbleContentMask::TrayBubbleContentMask(int corner_radius) : layer_(ui::LAYER_TEXTURED), corner_radius_(corner_radius) { layer_.set_delegate(this); + layer_.SetFillsBoundsOpaquely(false); } TrayBubbleContentMask::~TrayBubbleContentMask() { @@ -327,6 +328,7 @@ TrayBubbleView::TrayBubbleView(View* anchor, owned_bubble_border_(bubble_border_), is_gesture_dragging_(false), mouse_actively_entered_(false) { + set_can_activate(params_.can_activate); DCHECK(anchor_widget()); // Computed by BubbleDialogDelegateView(). set_notify_enter_exit_on_child(true); set_close_on_deactivate(init_params.close_on_deactivate); @@ -414,10 +416,6 @@ void TrayBubbleView::OnBeforeBubbleWidgetInit(Widget::InitParams* params, delegate_->OnBeforeBubbleWidgetInit(anchor_widget(), bubble_widget, params); } -bool TrayBubbleView::CanActivate() const { - return params_.can_activate; -} - NonClientFrameView* TrayBubbleView::CreateNonClientFrameView(Widget* widget) { BubbleFrameView* frame = static_cast<BubbleFrameView*>( BubbleDialogDelegateView::CreateNonClientFrameView(widget)); @@ -492,7 +490,7 @@ void TrayBubbleView::OnMouseExited(const ui::MouseEvent& event) { } void TrayBubbleView::GetAccessibleState(ui::AXViewState* state) { - if (delegate_ && params_.can_activate) { + if (delegate_ && CanActivate()) { state->role = ui::AX_ROLE_WINDOW; state->name = delegate_->GetAccessibleNameForBubble(); } diff --git a/chromium/ui/views/bubble/tray_bubble_view.h b/chromium/ui/views/bubble/tray_bubble_view.h index 4712e6a756f..ecba48f0143 100644 --- a/chromium/ui/views/bubble/tray_bubble_view.h +++ b/chromium/ui/views/bubble/tray_bubble_view.h @@ -154,7 +154,6 @@ class VIEWS_EXPORT TrayBubbleView : public views::BubbleDialogDelegateView, bool is_gesture_dragging() const { return is_gesture_dragging_; } // Overridden from views::WidgetDelegate. - bool CanActivate() const override; views::NonClientFrameView* CreateNonClientFrameView( views::Widget* widget) override; bool WidgetHasHitTestMask() const override; diff --git a/chromium/ui/views/cocoa/bridged_content_view.mm b/chromium/ui/views/cocoa/bridged_content_view.mm index b6c94e328eb..7c49cd04443 100644 --- a/chromium/ui/views/cocoa/bridged_content_view.mm +++ b/chromium/ui/views/cocoa/bridged_content_view.mm @@ -7,6 +7,7 @@ #include "base/logging.h" #import "base/mac/mac_util.h" #import "base/mac/scoped_nsobject.h" +#import "base/mac/sdk_forward_declarations.h" #include "base/strings/sys_string_conversions.h" #include "skia/ext/skia_utils_mac.h" #include "ui/base/cocoa/cocoa_base_utils.h" @@ -17,9 +18,11 @@ #include "ui/base/ime/text_input_client.h" #include "ui/compositor/canvas_painter.h" #import "ui/events/cocoa/cocoa_event_utils.h" +#include "ui/events/event_utils.h" #include "ui/events/keycodes/dom/dom_code.h" #import "ui/events/keycodes/keyboard_code_conversion_mac.h" #include "ui/gfx/canvas_paint_mac.h" +#include "ui/gfx/decorated_text.h" #include "ui/gfx/geometry/rect.h" #import "ui/gfx/mac/coordinate_conversion.h" #include "ui/gfx/path.h" @@ -32,6 +35,7 @@ #include "ui/views/view.h" #include "ui/views/widget/native_widget_mac.h" #include "ui/views/widget/widget.h" +#include "ui/views/word_lookup_client.h" using views::MenuController; @@ -207,6 +211,42 @@ ui::KeyEvent GetCharacterEventFromNSEvent(NSEvent* event) { ui::KeyboardCodeFromNSEvent(event), ui::EF_NONE); } +NSAttributedString* GetAttributedString( + const gfx::DecoratedText& decorated_text) { + base::scoped_nsobject<NSMutableAttributedString> str( + [[NSMutableAttributedString alloc] + initWithString:base::SysUTF16ToNSString(decorated_text.text)]); + [str beginEditing]; + + NSValue* const line_style = + @(NSUnderlineStyleSingle | NSUnderlinePatternSolid); + + for (const auto& attribute : decorated_text.attributes) { + DCHECK(!attribute.range.is_reversed()); + DCHECK_LE(attribute.range.end(), [str length]); + + NSMutableDictionary* attrs = [NSMutableDictionary dictionary]; + NSRange range = attribute.range.ToNSRange(); + + if (attribute.font.GetNativeFont()) + attrs[NSFontAttributeName] = attribute.font.GetNativeFont(); + + // NSFont does not have underline as an attribute. Hence handle it + // separately. + const bool underline = attribute.font.GetStyle() & gfx::Font::UNDERLINE; + if (underline) + attrs[NSUnderlineStyleAttributeName] = line_style; + + if (attribute.strike) + attrs[NSStrikethroughStyleAttributeName] = line_style; + + [str setAttributes:attrs range:range]; + } + + [str endEditing]; + return str.autorelease(); +} + } // namespace @interface BridgedContentView () @@ -332,7 +372,8 @@ ui::KeyEvent GetCharacterEventFromNSEvent(NSEvent* event) { views::View* view = hostedView_->GetTooltipHandlerForPoint(locationInContent); if (view) { gfx::Point viewPoint = locationInContent; - views::View::ConvertPointToTarget(hostedView_, view, &viewPoint); + views::View::ConvertPointToScreen(hostedView_, &viewPoint); + views::View::ConvertPointFromScreen(view, &viewPoint); if (!view->GetTooltipText(viewPoint, &newTooltipText)) DCHECK(newTooltipText.empty()); } @@ -344,7 +385,7 @@ ui::KeyEvent GetCharacterEventFromNSEvent(NSEvent* event) { - (void)updateWindowMask { DCHECK(![self inLiveResize]); - DCHECK(base::mac::IsOSMavericks()); + DCHECK(base::mac::IsOS10_9()); DCHECK(hostedView_); views::Widget* widget = hostedView_->GetWidget(); @@ -436,11 +477,23 @@ ui::KeyEvent GetCharacterEventFromNSEvent(NSEvent* event) { if (textInputClient_ && ![self activeMenuController]) { // If a single character is inserted by keyDown's call to // interpretKeyEvents: then use InsertChar() to allow editing events to be - // merged. - if (isCharacterEvent) - textInputClient_->InsertChar(GetCharacterEventFromNSEvent(keyDownEvent_)); - else + // merged. We use ui::VKEY_UNKNOWN as the key code since it's not feasible + // to determine the correct key code for each unicode character. Also a + // correct keycode is not needed in the current context. Send ui::EF_NONE as + // the key modifier since |text| already accounts for the pressed key + // modifiers. + + // Also, note we don't use |keyDownEvent_| to generate the synthetic + // ui::KeyEvent since for text inserted using an IME, [keyDownEvent_ + // characters] might not be the same as |text|. This is because + // |keyDownEvent_| will correspond to the event that caused the composition + // text to be confirmed, say, Return key press. + if (isCharacterEvent) { + textInputClient_->InsertChar(ui::KeyEvent([text characterAtIndex:0], + ui::VKEY_UNKNOWN, ui::EF_NONE)); + } else { textInputClient_->InsertText(base::SysNSStringToUTF16(text)); + } return; } @@ -593,7 +646,7 @@ ui::KeyEvent GetCharacterEventFromNSEvent(NSEvent* event) { // We prevent updating the window mask and clipping the border around the // view, during a live resize. Hence update the window mask and redraw the // view after resize has completed. - if (base::mac::IsOSMavericks()) { + if (base::mac::IsOS10_9()) { [self updateWindowMask]; [self setNeedsDisplay:YES]; } @@ -620,7 +673,7 @@ ui::KeyEvent GetCharacterEventFromNSEvent(NSEvent* event) { // crbug.com/543671. if (windowMask_ && ![self inLiveResize] && !IsRectInsidePath(dirtyRect, windowMask_)) { - DCHECK(base::mac::IsOSMavericks()); + DCHECK(base::mac::IsOS10_9()); gfx::ScopedNSGraphicsContextSaveGState state; // The outer rectangular path corresponding to the window. @@ -649,6 +702,14 @@ ui::KeyEvent GetCharacterEventFromNSEvent(NSEvent* event) { ui::CanvasPainter(&canvas, 1.f).context()); } +- (BOOL)isOpaque { + if (!hostedView_) + return NO; + + ui::Layer* layer = hostedView_->GetWidget()->GetLayer(); + return layer && layer->fills_bounds_opaquely(); +} + // To maximize consistency with the Cocoa browser (mac_views_browser=0), accept // mouse clicks immediately so that clicking on Chrome from an inactive window // will allow the event to be processed, rather than merely activate the window. @@ -715,8 +776,38 @@ ui::KeyEvent GetCharacterEventFromNSEvent(NSEvent* event) { if (!hostedView_) return; - ui::MouseWheelEvent event(theEvent); - hostedView_->GetWidget()->OnMouseEvent(&event); + ui::ScrollEvent event(theEvent); + hostedView_->GetWidget()->OnScrollEvent(&event); +} + +- (void)quickLookWithEvent:(NSEvent*)theEvent { + if (!hostedView_) + return; + + const gfx::Point locationInContent = ui::EventLocationFromNative(theEvent); + views::View* target = hostedView_->GetEventHandlerForPoint(locationInContent); + if (!target) + return; + + views::WordLookupClient* wordLookupClient = target->GetWordLookupClient(); + if (!wordLookupClient) + return; + + gfx::Point locationInTarget = locationInContent; + views::View::ConvertPointToTarget(hostedView_, target, &locationInTarget); + gfx::DecoratedText decoratedWord; + gfx::Point baselinePoint; + if (!wordLookupClient->GetDecoratedWordAtPoint( + locationInTarget, &decoratedWord, &baselinePoint)) { + return; + } + + // Convert |baselinePoint| to the coordinate system of |hostedView_|. + views::View::ConvertPointToTarget(target, hostedView_, &baselinePoint); + NSPoint baselinePointAppKit = NSMakePoint( + baselinePoint.x(), NSHeight([self frame]) - baselinePoint.y()); + [self showDefinitionForAttributedString:GetAttributedString(decoratedWord) + atPoint:baselinePointAppKit]; } //////////////////////////////////////////////////////////////////////////////// @@ -742,8 +833,10 @@ ui::KeyEvent GetCharacterEventFromNSEvent(NSEvent* event) { // Selection movement and scrolling. - (void)moveForward:(id)sender { - IsTextRTL(textInputClient_) ? [self moveLeft:sender] - : [self moveRight:sender]; + [self handleAction:ui::TextEditCommand::MOVE_FORWARD + keyCode:ui::VKEY_UNKNOWN + domCode:ui::DomCode::NONE + eventFlags:0]; } - (void)moveRight:(id)sender { @@ -754,8 +847,10 @@ ui::KeyEvent GetCharacterEventFromNSEvent(NSEvent* event) { } - (void)moveBackward:(id)sender { - IsTextRTL(textInputClient_) ? [self moveRight:sender] - : [self moveLeft:sender]; + [self handleAction:ui::TextEditCommand::MOVE_BACKWARD + keyCode:ui::VKEY_UNKNOWN + domCode:ui::DomCode::NONE + eventFlags:0]; } - (void)moveLeft:(id)sender { @@ -766,27 +861,31 @@ ui::KeyEvent GetCharacterEventFromNSEvent(NSEvent* event) { } - (void)moveUp:(id)sender { - [self handleAction:ui::TextEditCommand::MOVE_TO_BEGINNING_OF_LINE + [self handleAction:ui::TextEditCommand::MOVE_UP keyCode:ui::VKEY_UP domCode:ui::DomCode::ARROW_UP eventFlags:0]; } - (void)moveDown:(id)sender { - [self handleAction:ui::TextEditCommand::MOVE_TO_END_OF_LINE + [self handleAction:ui::TextEditCommand::MOVE_DOWN keyCode:ui::VKEY_DOWN domCode:ui::DomCode::ARROW_DOWN eventFlags:0]; } - (void)moveWordForward:(id)sender { - IsTextRTL(textInputClient_) ? [self moveWordLeft:sender] - : [self moveWordRight:sender]; + [self handleAction:ui::TextEditCommand::MOVE_WORD_FORWARD + keyCode:ui::VKEY_UNKNOWN + domCode:ui::DomCode::NONE + eventFlags:0]; } - (void)moveWordBackward:(id)sender { - IsTextRTL(textInputClient_) ? [self moveWordRight:sender] - : [self moveWordLeft:sender]; + [self handleAction:ui::TextEditCommand::MOVE_WORD_BACKWARD + keyCode:ui::VKEY_UNKNOWN + domCode:ui::DomCode::NONE + eventFlags:0]; } - (void)moveToBeginningOfLine:(id)sender { @@ -804,75 +903,91 @@ ui::KeyEvent GetCharacterEventFromNSEvent(NSEvent* event) { } - (void)moveToBeginningOfParagraph:(id)sender { - [self moveToBeginningOfLine:sender]; + [self handleAction:ui::TextEditCommand::MOVE_TO_BEGINNING_OF_PARAGRAPH + keyCode:ui::VKEY_UNKNOWN + domCode:ui::DomCode::NONE + eventFlags:0]; } - (void)moveToEndOfParagraph:(id)sender { - [self moveToEndOfLine:sender]; + [self handleAction:ui::TextEditCommand::MOVE_TO_END_OF_PARAGRAPH + keyCode:ui::VKEY_UNKNOWN + domCode:ui::DomCode::NONE + eventFlags:0]; } - (void)moveToEndOfDocument:(id)sender { - [self handleAction:ui::TextEditCommand::MOVE_TO_END_OF_LINE + [self handleAction:ui::TextEditCommand::MOVE_TO_END_OF_DOCUMENT keyCode:ui::VKEY_END domCode:ui::DomCode::END eventFlags:ui::EF_CONTROL_DOWN]; } - (void)moveToBeginningOfDocument:(id)sender { - [self handleAction:ui::TextEditCommand::MOVE_TO_BEGINNING_OF_LINE + [self handleAction:ui::TextEditCommand::MOVE_TO_BEGINNING_OF_DOCUMENT keyCode:ui::VKEY_HOME domCode:ui::DomCode::HOME eventFlags:ui::EF_CONTROL_DOWN]; } - (void)pageDown:(id)sender { - [self handleAction:ui::TextEditCommand::MOVE_TO_END_OF_LINE + // The pageDown: action message is bound to the key combination + // [Option+PageDown]. + [self handleAction:ui::TextEditCommand::MOVE_PAGE_DOWN keyCode:ui::VKEY_NEXT domCode:ui::DomCode::PAGE_DOWN - eventFlags:0]; + eventFlags:ui::EF_ALT_DOWN]; } - (void)pageUp:(id)sender { - [self handleAction:ui::TextEditCommand::MOVE_TO_BEGINNING_OF_LINE + // The pageUp: action message is bound to the key combination [Option+PageUp]. + [self handleAction:ui::TextEditCommand::MOVE_PAGE_UP keyCode:ui::VKEY_PRIOR domCode:ui::DomCode::PAGE_UP - eventFlags:0]; + eventFlags:ui::EF_ALT_DOWN]; } - (void)moveBackwardAndModifySelection:(id)sender { - IsTextRTL(textInputClient_) ? [self moveRightAndModifySelection:sender] - : [self moveLeftAndModifySelection:sender]; + [self handleAction:ui::TextEditCommand::MOVE_BACKWARD_AND_MODIFY_SELECTION + keyCode:ui::VKEY_UNKNOWN + domCode:ui::DomCode::NONE + eventFlags:0]; } - (void)moveForwardAndModifySelection:(id)sender { - IsTextRTL(textInputClient_) ? [self moveLeftAndModifySelection:sender] - : [self moveRightAndModifySelection:sender]; + [self handleAction:ui::TextEditCommand::MOVE_FORWARD_AND_MODIFY_SELECTION + keyCode:ui::VKEY_UNKNOWN + domCode:ui::DomCode::NONE + eventFlags:0]; } - (void)moveWordForwardAndModifySelection:(id)sender { - IsTextRTL(textInputClient_) ? [self moveWordLeftAndModifySelection:sender] - : [self moveWordRightAndModifySelection:sender]; + [self handleAction:ui::TextEditCommand::MOVE_WORD_FORWARD_AND_MODIFY_SELECTION + keyCode:ui::VKEY_UNKNOWN + domCode:ui::DomCode::NONE + eventFlags:0]; } - (void)moveWordBackwardAndModifySelection:(id)sender { - IsTextRTL(textInputClient_) ? [self moveWordRightAndModifySelection:sender] - : [self moveWordLeftAndModifySelection:sender]; + [self + handleAction:ui::TextEditCommand::MOVE_WORD_BACKWARD_AND_MODIFY_SELECTION + keyCode:ui::VKEY_UNKNOWN + domCode:ui::DomCode::NONE + eventFlags:0]; } - (void)moveUpAndModifySelection:(id)sender { - [self handleAction:ui::TextEditCommand:: - MOVE_TO_BEGINNING_OF_LINE_AND_MODIFY_SELECTION + [self handleAction:ui::TextEditCommand::MOVE_UP_AND_MODIFY_SELECTION keyCode:ui::VKEY_UP domCode:ui::DomCode::ARROW_UP eventFlags:ui::EF_SHIFT_DOWN]; } - (void)moveDownAndModifySelection:(id)sender { - [self - handleAction:ui::TextEditCommand::MOVE_TO_END_OF_LINE_AND_MODIFY_SELECTION - keyCode:ui::VKEY_DOWN - domCode:ui::DomCode::ARROW_DOWN - eventFlags:ui::EF_SHIFT_DOWN]; + [self handleAction:ui::TextEditCommand::MOVE_DOWN_AND_MODIFY_SELECTION + keyCode:ui::VKEY_DOWN + domCode:ui::DomCode::ARROW_DOWN + eventFlags:ui::EF_SHIFT_DOWN]; } - (void)moveToBeginningOfLineAndModifySelection:(id)sender { @@ -892,56 +1007,62 @@ ui::KeyEvent GetCharacterEventFromNSEvent(NSEvent* event) { } - (void)moveToBeginningOfParagraphAndModifySelection:(id)sender { - [self moveToBeginningOfLineAndModifySelection:sender]; + [self handleAction:ui::TextEditCommand:: + MOVE_TO_BEGINNING_OF_PARAGRAPH_AND_MODIFY_SELECTION + keyCode:ui::VKEY_UNKNOWN + domCode:ui::DomCode::NONE + eventFlags:0]; } - (void)moveToEndOfParagraphAndModifySelection:(id)sender { - [self moveToEndOfLineAndModifySelection:sender]; + [self handleAction:ui::TextEditCommand:: + MOVE_TO_END_OF_PARAGRAPH_AND_MODIFY_SELECTION + keyCode:ui::VKEY_UNKNOWN + domCode:ui::DomCode::NONE + eventFlags:0]; } - (void)moveToEndOfDocumentAndModifySelection:(id)sender { - [self - handleAction:ui::TextEditCommand::MOVE_TO_END_OF_LINE_AND_MODIFY_SELECTION - keyCode:ui::VKEY_END - domCode:ui::DomCode::END - eventFlags:ui::EF_CONTROL_DOWN | ui::EF_SHIFT_DOWN]; + [self handleAction:ui::TextEditCommand:: + MOVE_TO_END_OF_DOCUMENT_AND_MODIFY_SELECTION + keyCode:ui::VKEY_END + domCode:ui::DomCode::END + eventFlags:ui::EF_CONTROL_DOWN | ui::EF_SHIFT_DOWN]; } - (void)moveToBeginningOfDocumentAndModifySelection:(id)sender { [self handleAction:ui::TextEditCommand:: - MOVE_TO_BEGINNING_OF_LINE_AND_MODIFY_SELECTION + MOVE_TO_BEGINNING_OF_DOCUMENT_AND_MODIFY_SELECTION keyCode:ui::VKEY_HOME domCode:ui::DomCode::HOME eventFlags:ui::EF_CONTROL_DOWN | ui::EF_SHIFT_DOWN]; } - (void)pageDownAndModifySelection:(id)sender { - [self - handleAction:ui::TextEditCommand::MOVE_TO_END_OF_LINE_AND_MODIFY_SELECTION - keyCode:ui::VKEY_NEXT - domCode:ui::DomCode::PAGE_DOWN - eventFlags:ui::EF_SHIFT_DOWN]; + [self handleAction:ui::TextEditCommand::MOVE_PAGE_DOWN_AND_MODIFY_SELECTION + keyCode:ui::VKEY_NEXT + domCode:ui::DomCode::PAGE_DOWN + eventFlags:ui::EF_SHIFT_DOWN]; } - (void)pageUpAndModifySelection:(id)sender { - [self handleAction:ui::TextEditCommand:: - MOVE_TO_BEGINNING_OF_LINE_AND_MODIFY_SELECTION + [self handleAction:ui::TextEditCommand::MOVE_PAGE_UP_AND_MODIFY_SELECTION keyCode:ui::VKEY_PRIOR domCode:ui::DomCode::PAGE_UP eventFlags:ui::EF_SHIFT_DOWN]; } - (void)moveParagraphForwardAndModifySelection:(id)sender { - [self - handleAction:ui::TextEditCommand::MOVE_TO_END_OF_LINE_AND_MODIFY_SELECTION - keyCode:ui::VKEY_DOWN - domCode:ui::DomCode::ARROW_DOWN - eventFlags:ui::EF_CONTROL_DOWN | ui::EF_SHIFT_DOWN]; + [self handleAction:ui::TextEditCommand:: + MOVE_PARAGRAPH_FORWARD_AND_MODIFY_SELECTION + keyCode:ui::VKEY_DOWN + domCode:ui::DomCode::ARROW_DOWN + eventFlags:ui::EF_CONTROL_DOWN | ui::EF_SHIFT_DOWN]; } - (void)moveParagraphBackwardAndModifySelection:(id)sender { [self handleAction:ui::TextEditCommand:: - MOVE_TO_BEGINNING_OF_LINE_AND_MODIFY_SELECTION + MOVE_PARAGRAPH_BACKWARD_AND_MODIFY_SELECTION keyCode:ui::VKEY_UP domCode:ui::DomCode::ARROW_UP eventFlags:ui::EF_CONTROL_DOWN | ui::EF_SHIFT_DOWN]; @@ -1065,11 +1186,24 @@ ui::KeyEvent GetCharacterEventFromNSEvent(NSEvent* event) { } - (void)deleteToBeginningOfParagraph:(id)sender { - [self deleteToBeginningOfLine:sender]; + [self handleAction:ui::TextEditCommand::DELETE_TO_BEGINNING_OF_PARAGRAPH + keyCode:ui::VKEY_UNKNOWN + domCode:ui::DomCode::NONE + eventFlags:0]; } - (void)deleteToEndOfParagraph:(id)sender { - [self deleteToEndOfLine:sender]; + [self handleAction:ui::TextEditCommand::DELETE_TO_END_OF_PARAGRAPH + keyCode:ui::VKEY_UNKNOWN + domCode:ui::DomCode::NONE + eventFlags:0]; +} + +- (void)yank:(id)sender { + [self handleAction:ui::TextEditCommand::YANK + keyCode:ui::VKEY_Y + domCode:ui::DomCode::US_Y + eventFlags:ui::EF_CONTROL_DOWN]; } // Cancellation. @@ -1213,9 +1347,21 @@ ui::KeyEvent GetCharacterEventFromNSEvent(NSEvent* event) { if ([text isKindOfClass:[NSAttributedString class]]) text = [text string]; + + textInputClient_->DeleteRange(gfx::Range(replacementRange)); ui::CompositionText composition; composition.text = base::SysNSStringToUTF16(text); composition.selection = gfx::Range(selectedRange); + + // Add a black underline with a transparent background to the composition + // text. TODO(karandeepb): On Cocoa textfields, the target clause of the + // composition has a thick underlines. The composition text also has + // discontinous underlines for different clauses. This is also supported in + // the Chrome renderer. Add code to extract underlines from |text| once our + // render text implementation supports thick underlines and discontinous + // underlines for consecutive characters. See http://crbug.com/612675. + composition.underlines.push_back(ui::CompositionUnderline( + 0, [text length], SK_ColorBLACK, false, SK_ColorTRANSPARENT)); textInputClient_->SetCompositionText(composition); } @@ -1286,4 +1432,10 @@ ui::KeyEvent GetCharacterEventFromNSEvent(NSEvent* event) { return [hostedView_->GetNativeViewAccessible() accessibilityHitTest:point]; } +- (id)accessibilityFocusedUIElement { + if (!hostedView_) + return nil; + return [hostedView_->GetNativeViewAccessible() accessibilityFocusedUIElement]; +} + @end diff --git a/chromium/ui/views/cocoa/bridged_native_widget.h b/chromium/ui/views/cocoa/bridged_native_widget.h index ba4c5847c73..d1e2d08a516 100644 --- a/chromium/ui/views/cocoa/bridged_native_widget.h +++ b/chromium/ui/views/cocoa/bridged_native_widget.h @@ -142,6 +142,9 @@ class VIEWS_EXPORT BridgedNativeWidget // being reordered in (or out of) the screen list. void OnVisibilityChanged(); + // Called by the NSWindowDelegate when the system control tint changes. + void OnSystemControlTintChanged(); + // Called by the NSWindowDelegate on a scale factor or color space change. void OnBackingPropertiesChanged(); @@ -311,6 +314,11 @@ class VIEWS_EXPORT BridgedNativeWidget // shadow needs to be invalidated when a frame is received for the new shape. bool invalidate_shadow_on_frame_swap_ = false; + // Whether the window's visibility is suppressed currently. For opaque non- + // modal windows, the window's alpha value is set to 0, till the frame from + // the compositor arrives to avoid "blinking". + bool initial_visibility_suppressed_ = false; + AssociatedViews associated_views_; DISALLOW_COPY_AND_ASSIGN(BridgedNativeWidget); diff --git a/chromium/ui/views/cocoa/bridged_native_widget.mm b/chromium/ui/views/cocoa/bridged_native_widget.mm index 16af930118c..d10b7f17673 100644 --- a/chromium/ui/views/cocoa/bridged_native_widget.mm +++ b/chromium/ui/views/cocoa/bridged_native_widget.mm @@ -12,6 +12,7 @@ #import "base/mac/foundation_util.h" #include "base/mac/mac_util.h" #import "base/mac/sdk_forward_declarations.h" +#include "base/memory/ptr_util.h" #include "base/threading/thread_task_runner_handle.h" #include "ui/accelerated_widget_mac/window_resize_helper_mac.h" #import "ui/base/cocoa/constrained_window/constrained_window_animation.h" @@ -23,6 +24,7 @@ #include "ui/gfx/geometry/dip_util.h" #import "ui/gfx/mac/coordinate_conversion.h" #import "ui/gfx/mac/nswindow_frame_controls.h" +#import "ui/native_theme/native_theme_mac.h" #import "ui/views/cocoa/bridged_content_view.h" #import "ui/views/cocoa/drag_drop_client_mac.h" #import "ui/views/cocoa/cocoa_mouse_capture.h" @@ -399,6 +401,12 @@ void BridgedNativeWidget::Init(base::scoped_nsobject<NSWindow> window, name:NSApplicationDidHideNotification object:nil]; + [[NSNotificationCenter defaultCenter] + addObserver:window_delegate_ + selector:@selector(onSystemControlTintChanged:) + name:NSControlTintDidChangeNotification + object:nil]; + // Validate the window's initial state, otherwise the bridge's initial // tracking state will be incorrect. DCHECK(![window_ isVisible]); @@ -564,6 +572,20 @@ void BridgedNativeWidget::SetVisibilityState(WindowVisibilityState new_state) { return; } + // Non-modal windows are not animated. Hence opaque non-modal windows can + // appear with a "flash" if they are made visible before the frame from the + // compositor arrives. To get around this, set the alpha value of the window + // to 0, till we receive the correct frame from the compositor. Also, ignore + // mouse clicks till then. + // TODO(karandeepb): Investigate whether similar technique is needed for other + // dialog types. + if (layer() && [window_ isOpaque] && + !native_widget_mac_->GetWidget()->IsModal()) { + initial_visibility_suppressed_ = true; + [window_ setAlphaValue:0.0]; + [window_ setIgnoresMouseEvents:YES]; + } + if (new_state == SHOW_AND_ACTIVATE_WINDOW) { [window_ makeKeyAndOrderFront:nil]; [NSApp activateIgnoringOtherApps:YES]; @@ -686,8 +708,10 @@ void BridgedNativeWidget::OnWindowWillClose() { parent_->RemoveChildWindow(this); parent_ = nullptr; } - [window_ setDelegate:nil]; [[NSNotificationCenter defaultCenter] removeObserver:window_delegate_]; + // Note this also clears the NSWindow delegate, after informing Widget + // delegates about the closure. NativeWidgetMac then deletes |this| before + // returning. native_widget_mac_->OnWindowWillClose(); } @@ -775,7 +799,7 @@ void BridgedNativeWidget::OnSizeChanged() { // We don't update the window mask during a live resize, instead it is done // after the resize is completed in viewDidEndLiveResize: in // BridgedContentView. - if (base::mac::IsOSMavericks() && ![window_ inLiveResize]) + if (base::mac::IsOS10_9() && ![window_ inLiveResize]) [bridged_view_ updateWindowMask]; } @@ -819,6 +843,11 @@ void BridgedNativeWidget::OnVisibilityChanged() { if (layer()) { layer()->SetVisible(window_visible_); layer()->SchedulePaint(gfx::Rect(GetClientAreaSize())); + + // For translucent windows which are made visible, recalculate shadow when + // the frame from the compositor arrives. + if (![window_ isOpaque]) + invalidate_shadow_on_frame_swap_ = window_visible_; } NotifyVisibilityChangeDown(); @@ -833,6 +862,10 @@ void BridgedNativeWidget::OnVisibilityChanged() { [window_ setAutodisplay:window_visible_]; } +void BridgedNativeWidget::OnSystemControlTintChanged() { + ui::NativeThemeMac::instance()->NotifyObservers(); +} + void BridgedNativeWidget::OnBackingPropertiesChanged() { if (layer()) UpdateLayerProperties(); @@ -944,7 +977,7 @@ void BridgedNativeWidget::CreateLayer(ui::LayerType layer_type, CreateCompositor(); DCHECK(compositor_); - SetLayer(new ui::Layer(layer_type)); + SetLayer(base::MakeUnique<ui::Layer>(layer_type)); // Note, except for controls, this will set the layer to be hidden, since it // is only called during Init(). layer()->SetVisible(window_visible_); @@ -968,7 +1001,7 @@ void BridgedNativeWidget::CreateLayer(ui::LayerType layer_type, // to generate a window shadow from the composited CALayer. To get around // this, let the window background remain opaque and clip the window // boundary in drawRect method of BridgedContentView. See crbug.com/543671. - if (base::mac::IsOSYosemiteOrLater()) + if (base::mac::IsAtLeastOS10_10()) [window_ setBackgroundColor:[NSColor clearColor]]; } @@ -1089,6 +1122,12 @@ void BridgedNativeWidget::AcceleratedWidgetSwapCompleted() { if (!compositor_widget_->HasFrameOfSize(GetClientAreaSize())) return; + if (initial_visibility_suppressed_) { + initial_visibility_suppressed_ = false; + [window_ setAlphaValue:1.0]; + [window_ setIgnoresMouseEvents:NO]; + } + if (invalidate_shadow_on_frame_swap_) { invalidate_shadow_on_frame_swap_ = false; [window_ invalidateShadow]; @@ -1259,7 +1298,7 @@ void BridgedNativeWidget::AddCompositorSuperview() { if (widget_type_ == Widget::InitParams::TYPE_MENU) { // Giving the canvas opacity messes up subpixel font rendering, so use a // solid background, but make the CALayer transparent. - if (base::mac::IsOSYosemiteOrLater()) { + if (base::mac::IsAtLeastOS10_10()) { [background_layer setOpacity:kYosemiteMenuOpacity]; CGSSetWindowBackgroundBlurRadius( _CGSDefaultConnection(), [window_ windowNumber], kYosemiteMenuBlur); diff --git a/chromium/ui/views/cocoa/bridged_native_widget_unittest.mm b/chromium/ui/views/cocoa/bridged_native_widget_unittest.mm index 4554eee36a5..2b21d350de4 100644 --- a/chromium/ui/views/cocoa/bridged_native_widget_unittest.mm +++ b/chromium/ui/views/cocoa/bridged_native_widget_unittest.mm @@ -19,12 +19,16 @@ #import "testing/gtest_mac.h" #import "ui/base/cocoa/window_size_constants.h" #include "ui/base/ime/input_method.h" +#include "ui/base/material_design/material_design_controller.h" +#include "ui/base/test/material_design_controller_test_api.h" +#include "ui/events/test/cocoa_test_event_utils.h" #import "ui/gfx/mac/coordinate_conversion.h" #import "ui/gfx/test/ui_cocoa_test_helper.h" #import "ui/views/cocoa/bridged_content_view.h" #import "ui/views/cocoa/native_widget_mac_nswindow.h" #import "ui/views/cocoa/views_nswindow_delegate.h" #include "ui/views/controls/textfield/textfield.h" +#include "ui/views/controls/textfield/textfield_model.h" #include "ui/views/view.h" #include "ui/views/widget/native_widget_mac.h" #include "ui/views/widget/root_view.h" @@ -57,6 +61,11 @@ using base::SysUTF8ToNSString; namespace { +enum class TestCase { + ALL, // Test all strings. + LTR_ONLY, // Only test Left To Right strings. +}; + // Implemented NSResponder action messages for use in tests. NSArray* const kMoveActions = @[ @"moveForward:", @@ -113,7 +122,7 @@ NSArray* const kDeleteActions = @[ ]; NSArray* const kMiscActions = - @[ @"insertText:", @"cancelOperation:", @"transpose:" ]; + @[ @"insertText:", @"cancelOperation:", @"transpose:", @"yank:" ]; // Empty range shortcut for readibility. NSRange EmptyRange() { @@ -237,6 +246,7 @@ class BridgedNativeWidgetTestBase : public ui::CocoaTest { // Overridden from testing::Test: void SetUp() override { ui::CocoaTest::SetUp(); + ui::MaterialDesignController::Initialize(); init_params_.native_widget = native_widget_mac_; @@ -256,6 +266,11 @@ class BridgedNativeWidgetTestBase : public ui::CocoaTest { native_widget_mac_->GetWidget()->Init(init_params_); } + void TearDown() override { + ui::test::MaterialDesignControllerTestAPI::Uninitialize(); + ui::CocoaTest::TearDown(); + } + protected: std::unique_ptr<Widget> widget_; MockNativeWidgetMac* native_widget_mac_; // Weak. Owned by |widget_|. @@ -304,6 +319,9 @@ class BridgedNativeWidgetTest : public BridgedNativeWidgetTestBase { // |start|. void MakeSelection(int start, int end); + // Helper method to set the private |keyDownEvent_| field on |ns_view_|. + void SetKeyDownEvent(NSEvent* event); + // testing::Test: void SetUp() override; void TearDown() override; @@ -319,11 +337,12 @@ class BridgedNativeWidgetTest : public BridgedNativeWidgetTestBase { // Test editing commands in |selectors| against the expectations set by // |dummy_text_view_|. This is done by selecting every substring within a set - // of test strings (both RTL and non-RTL) and performing every selector on - // both the NSTextView and the BridgedContentView hosting a focused - // views::TextField to ensure the resulting text and selection ranges match. - // |selectors| is an NSArray of NSStrings. - void TestEditingCommands(NSArray* selectors); + // of test strings (both RTL and non-RTL by default) and performing every + // selector on both the NSTextView and the BridgedContentView hosting a + // focused views::TextField to ensure the resulting text and selection ranges + // match. |selectors| is an NSArray of NSStrings. |cases| determines whether + // RTL strings are to be tested. + void TestEditingCommands(NSArray* selectors, TestCase cases = TestCase::ALL); std::unique_ptr<views::View> view_; @@ -426,6 +445,10 @@ void BridgedNativeWidgetTest::MakeSelection(int start, int end) { [dummy_text_view_ doCommandBySelector:sel]; } +void BridgedNativeWidgetTest::SetKeyDownEvent(NSEvent* event) { + [ns_view_ setValue:event forKey:@"keyDownEvent_"]; +} + void BridgedNativeWidgetTest::SetUp() { BridgedNativeWidgetTestBase::SetUp(); @@ -449,6 +472,9 @@ void BridgedNativeWidgetTest::SetUp() { } void BridgedNativeWidgetTest::TearDown() { + // Clear kill buffer so that no state persists between tests. + TextfieldModel::ClearKillBuffer(); + if (bridge()) bridge()->SetRootView(nullptr); view_.reset(); @@ -483,6 +509,12 @@ void BridgedNativeWidgetTest::TestDeleteBeginning(SEL sel) { EXPECT_NSEQ_3(@" bar ", GetExpectedText(), GetActualText()); EXPECT_EQ_RANGE_3(NSMakeRange(5, 0), GetExpectedSelectionRange(), GetActualSelectionRange()); + + // Verify yanking inserts the deleted text. + PerformCommand(@selector(yank:)); + EXPECT_NSEQ_3(@" bar baz", GetExpectedText(), GetActualText()); + EXPECT_EQ_RANGE_3(NSMakeRange(8, 0), GetExpectedSelectionRange(), + GetActualSelectionRange()); } void BridgedNativeWidgetTest::TestDeleteEnd(SEL sel) { @@ -511,13 +543,22 @@ void BridgedNativeWidgetTest::TestDeleteEnd(SEL sel) { EXPECT_NSEQ_3(@"bar", GetExpectedText(), GetActualText()); EXPECT_EQ_RANGE_3(NSMakeRange(0, 0), GetExpectedSelectionRange(), GetActualSelectionRange()); + + // Verify yanking inserts the deleted text. + PerformCommand(@selector(yank:)); + EXPECT_NSEQ_3(@"foo bar", GetExpectedText(), GetActualText()); + EXPECT_EQ_RANGE_3(NSMakeRange(4, 0), GetExpectedSelectionRange(), + GetActualSelectionRange()); } -void BridgedNativeWidgetTest::TestEditingCommands(NSArray* selectors) { - const base::string16 test_strings[] = { - base::WideToUTF16(L"ab c"), - base::WideToUTF16(L"\x0634\x0632 \x064A") // RTL string. - }; +void BridgedNativeWidgetTest::TestEditingCommands(NSArray* selectors, + TestCase cases) { + std::vector<base::string16> test_strings; + test_strings.push_back(base::WideToUTF16(L"ab c")); + if (cases == TestCase::ALL) { + test_strings.push_back( + base::WideToUTF16(L"\x0634\x0632 \x064A")); // RTL string. + } for (const base::string16& test_string : test_strings) { for (NSString* selector_string in selectors) { @@ -863,6 +904,51 @@ TEST_F(BridgedNativeWidgetTest, TextInput_Compose) { GetExpectedSelectionRange(), GetActualSelectionRange()); } +// Test IME composition for accented characters. +TEST_F(BridgedNativeWidgetTest, TextInput_AccentedCharacter) { + InstallTextField("abc"); + + // Simulate action messages generated when the key 'a' is pressed repeatedly + // and leads to the showing of an IME candidate window. To simulate an event, + // set the private keyDownEvent field on the BridgedContentView. + + // First an insertText: message with key 'a' is generated. + SetKeyDownEvent(cocoa_test_event_utils::SynthesizeKeyEvent( + widget_->GetNativeWindow(), true, ui::VKEY_A, 0)); + [ns_view_ insertText:@"a" replacementRange:EmptyRange()]; + [dummy_text_view_ insertText:@"a" replacementRange:EmptyRange()]; + EXPECT_EQ_3(NO, [dummy_text_view_ hasMarkedText], [ns_view_ hasMarkedText]); + EXPECT_NSEQ_3(@"abca", GetExpectedText(), GetActualText()); + + // Next the IME popup appears. On selecting the accented character using arrow + // keys, setMarkedText action message is generated which replaces the earlier + // inserted 'a'. + SetKeyDownEvent(cocoa_test_event_utils::SynthesizeKeyEvent( + widget_->GetNativeWindow(), true, ui::VKEY_RIGHT, 0)); + [ns_view_ setMarkedText:@"Ă " + selectedRange:NSMakeRange(0, 1) + replacementRange:NSMakeRange(3, 1)]; + [dummy_text_view_ setMarkedText:@"Ă " + selectedRange:NSMakeRange(0, 1) + replacementRange:NSMakeRange(3, 1)]; + EXPECT_EQ_3(YES, [dummy_text_view_ hasMarkedText], [ns_view_ hasMarkedText]); + EXPECT_EQ_RANGE_3(NSMakeRange(3, 1), [dummy_text_view_ markedRange], + [ns_view_ markedRange]); + EXPECT_EQ_RANGE_3(NSMakeRange(3, 1), GetExpectedSelectionRange(), + GetActualSelectionRange()); + EXPECT_NSEQ_3(@"abcĂ ", GetExpectedText(), GetActualText()); + + // On pressing enter, the marked text is confirmed. + SetKeyDownEvent(cocoa_test_event_utils::SynthesizeKeyEvent( + widget_->GetNativeWindow(), true, ui::VKEY_RETURN, 0)); + [ns_view_ insertText:@"Ă " replacementRange:EmptyRange()]; + [dummy_text_view_ insertText:@"Ă " replacementRange:EmptyRange()]; + EXPECT_EQ_3(NO, [dummy_text_view_ hasMarkedText], [ns_view_ hasMarkedText]); + EXPECT_EQ_RANGE_3(NSMakeRange(4, 0), GetExpectedSelectionRange(), + GetActualSelectionRange()); + EXPECT_NSEQ_3(@"abcĂ ", GetExpectedText(), GetActualText()); +} + // Test moving the caret left and right using text input protocol. TEST_F(BridgedNativeWidgetTest, TextInput_MoveLeftRight) { InstallTextField("foo"); @@ -897,6 +983,12 @@ TEST_F(BridgedNativeWidgetTest, TextInput_DeleteBackward) { EXPECT_EQ_RANGE_3(NSMakeRange(0, 0), GetExpectedSelectionRange(), GetActualSelectionRange()); + // Verify that deletion did not modify the kill buffer. + PerformCommand(@selector(yank:)); + EXPECT_NSEQ_3(nil, GetExpectedText(), GetActualText()); + EXPECT_EQ_RANGE_3(NSMakeRange(0, 0), GetExpectedSelectionRange(), + GetActualSelectionRange()); + // Try to delete again on an empty string. PerformCommand(@selector(deleteBackward:)); EXPECT_NSEQ_3(nil, GetExpectedText(), GetActualText()); @@ -922,6 +1014,12 @@ TEST_F(BridgedNativeWidgetTest, TextInput_DeleteForward) { EXPECT_NSEQ_3(nil, GetExpectedText(), GetActualText()); EXPECT_EQ_RANGE_3(NSMakeRange(0, 0), GetExpectedSelectionRange(), GetActualSelectionRange()); + + // Verify that deletion did not modify the kill buffer. + PerformCommand(@selector(yank:)); + EXPECT_NSEQ_3(nil, GetExpectedText(), GetActualText()); + EXPECT_EQ_RANGE_3(NSMakeRange(0, 0), GetExpectedSelectionRange(), + GetActualSelectionRange()); } // Test forward word deletion using text input protocol. @@ -951,6 +1049,12 @@ TEST_F(BridgedNativeWidgetTest, TextInput_DeleteWordForward) { EXPECT_NSEQ_3(@"o b baz", GetExpectedText(), GetActualText()); EXPECT_EQ_RANGE_3(NSMakeRange(0, 0), GetExpectedSelectionRange(), GetActualSelectionRange()); + + // Verify that deletion did not modify the kill buffer. + PerformCommand(@selector(yank:)); + EXPECT_NSEQ_3(@"o b baz", GetExpectedText(), GetActualText()); + EXPECT_EQ_RANGE_3(NSMakeRange(0, 0), GetExpectedSelectionRange(), + GetActualSelectionRange()); } // Test backward word deletion using text input protocol. @@ -982,6 +1086,12 @@ TEST_F(BridgedNativeWidgetTest, TextInput_DeleteWordBackward) { EXPECT_NSEQ_3(@"faz", GetExpectedText(), GetActualText()); EXPECT_EQ_RANGE_3(NSMakeRange(1, 0), GetExpectedSelectionRange(), GetActualSelectionRange()); + + // Verify that deletion did not modify the kill buffer. + PerformCommand(@selector(yank:)); + EXPECT_NSEQ_3(@"faz", GetExpectedText(), GetActualText()); + EXPECT_EQ_RANGE_3(NSMakeRange(1, 0), GetExpectedSelectionRange(), + GetActualSelectionRange()); } // Test deleting to beginning/end of line/paragraph using text input protocol. @@ -1007,12 +1117,12 @@ TEST_F(BridgedNativeWidgetTest, TextInput_MoveEditingCommands) { TestEditingCommands(kMoveActions); } -// Todo(karandeepb): Enable this test once the behavior of all move and select -// commands are fixed. // Test move and select commands against expectations set by |dummy_text_view_|. -TEST_F(BridgedNativeWidgetTest, - TextInput_MoveAndSelectEditingCommands_DISABLED) { - TestEditingCommands(kSelectActions); +TEST_F(BridgedNativeWidgetTest, TextInput_MoveAndSelectEditingCommands) { + // The behavior of NSTextView for RTL strings is buggy for some move and + // select commands. Hence don't test against an RTL string. See + // rdar://27863290. + TestEditingCommands(kSelectActions, TestCase::LTR_ONLY); } // Test delete commands against expectations set by |dummy_text_view_|. diff --git a/chromium/ui/views/cocoa/drag_drop_client_mac.mm b/chromium/ui/views/cocoa/drag_drop_client_mac.mm index ed85acbfe9e..9dda333f01b 100644 --- a/chromium/ui/views/cocoa/drag_drop_client_mac.mm +++ b/chromium/ui/views/cocoa/drag_drop_client_mac.mm @@ -25,7 +25,8 @@ - (id)initWithData:(const ui::OSExchangeData&)data { if ((self = [super init])) { - data_.reset(new OSExchangeData(data.provider().Clone())); + data_.reset(new OSExchangeData( + std::unique_ptr<OSExchangeData::Provider>(data.provider().Clone()))); } return self; } @@ -78,6 +79,11 @@ void DragDropClientMac::StartDragAndDrop( const ui::OSExchangeDataProviderMac& provider = static_cast<const ui::OSExchangeDataProviderMac&>(data.provider()); + // Release capture before beginning the dragging session. Capture may have + // been acquired on the mouseDown, but capture is not required during the + // dragging session and the mouseUp that would release it will be suppressed. + bridge_->ReleaseCapture(); + // Synthesize an event for dragging, since we can't be sure that // [NSApp currentEvent] will return a valid dragging event. NSWindow* window = bridge_->ns_window(); @@ -131,6 +137,8 @@ NSDragOperation DragDropClientMac::DragUpdate(id<NSDraggingInfo> sender) { if (!data_source_.get()) { data_source_.reset([[CocoaDragDropDataProvider alloc] initWithPasteboard:[sender draggingPasteboard]]); + operation_ = ui::DragDropTypes::NSDragOperationToDragOperation( + [sender draggingSourceOperationMask]); } drag_operation = drop_helper_.OnDragOver( @@ -143,6 +151,8 @@ NSDragOperation DragDropClientMac::Drop(id<NSDraggingInfo> sender) { int drag_operation = drop_helper_.OnDrop( *[data_source_ data], LocationInView([sender draggingLocation]), operation_); + data_source_.reset(); + operation_ = 0; return ui::DragDropTypes::DragOperationToNSDragOperation(drag_operation); } diff --git a/chromium/ui/views/cocoa/drag_drop_client_mac_unittest.mm b/chromium/ui/views/cocoa/drag_drop_client_mac_unittest.mm index 7c9f510e5c3..364b73e0d69 100644 --- a/chromium/ui/views/cocoa/drag_drop_client_mac_unittest.mm +++ b/chromium/ui/views/cocoa/drag_drop_client_mac_unittest.mm @@ -6,8 +6,10 @@ #import <Cocoa/Cocoa.h> +#import "base/mac/scoped_objc_class_swizzler.h" #include "base/mac/sdk_forward_declarations.h" #include "base/strings/utf_string_conversions.h" +#include "base/threading/thread_task_runner_handle.h" #import "ui/views/cocoa/bridged_native_widget.h" #include "ui/views/test/widget_test.h" #include "ui/views/view.h" @@ -16,6 +18,18 @@ using base::ASCIIToUTF16; +@interface NSView (DragSessionTestingDonor) +@end + +@implementation NSView (DragSessionTestingDonor) +- (NSDraggingSession*)cr_beginDraggingSessionWithItems:(NSArray*)items + event:(NSEvent*)event + source:(id<NSDraggingSource>) + source { + return nil; +} +@end + // Mocks the NSDraggingInfo sent to the DragDropClientMac's DragUpdate() and // Drop() methods. Out of the required methods of the protocol, only // draggingLocation and draggingPasteboard are used. @@ -61,7 +75,7 @@ using base::ASCIIToUTF16; } - (NSDragOperation)draggingSourceOperationMask { - return NSDragOperationNone; + return NSDragOperationEvery; } - (NSWindow*)draggingDestinationWindow { @@ -209,6 +223,41 @@ TEST_F(DragDropClientMacTest, BasicDragDrop) { EXPECT_EQ(Drop(), NSDragOperationMove); } +// Ensure that capture is released before the end of a drag and drop operation. +TEST_F(DragDropClientMacTest, ReleaseCapture) { + // DragDropView doesn't actually capture the mouse, so explicitly acquire it + // to test that StartDragAndDrop() actually releases it. + // Although this is not an interactive UI test, acquiring capture should be OK + // since the runloop will exit before the system has any opportunity to + // capture anything. + bridge_->AcquireCapture(); + EXPECT_TRUE(bridge_->HasCapture()); + + // Create the drop data + OSExchangeData data; + const base::string16& text = ASCIIToUTF16("text"); + data.SetString(text); + SetData(data); + + // There's no way to cleanly stop NSDraggingSession inside unit tests, so just + // don't start it at all. + base::mac::ScopedObjCClassSwizzler swizzle( + [NSView class], @selector(beginDraggingSessionWithItems:event:source:), + @selector(cr_beginDraggingSessionWithItems:event:source:)); + + // Immediately quit drag'n'drop, or we'll hang. + base::ThreadTaskRunnerHandle::Get()->PostTask( + FROM_HERE, base::Bind(&DragDropClientMac::EndDrag, + base::Unretained(drag_drop_client()))); + + // It will call ReleaseCapture(). + drag_drop_client()->StartDragAndDrop( + target_, data, 0, ui::DragDropTypes::DRAG_EVENT_SOURCE_MOUSE); + + // The capture should be released. + EXPECT_FALSE(bridge_->HasCapture()); +} + // Tests if the drag and drop target rejects the dropped data with the // incorrect format. TEST_F(DragDropClientMacTest, InvalidFormatDragDrop) { diff --git a/chromium/ui/views/cocoa/views_nswindow_delegate.h b/chromium/ui/views/cocoa/views_nswindow_delegate.h index 3c0e06e8e41..715af24dec9 100644 --- a/chromium/ui/views/cocoa/views_nswindow_delegate.h +++ b/chromium/ui/views/cocoa/views_nswindow_delegate.h @@ -42,6 +42,9 @@ VIEWS_EXPORT // a notification such as NSApplicationDidHideNotification. - (void)onWindowOrderChanged:(NSNotification*)notification; +// Notify that the system control tint changed. +- (void)onSystemControlTintChanged:(NSNotification*)notification; + // Called on the delegate of a modal sheet when its modal session ends. - (void)sheetDidEnd:(NSWindow*)sheet returnCode:(NSInteger)returnCode diff --git a/chromium/ui/views/cocoa/views_nswindow_delegate.mm b/chromium/ui/views/cocoa/views_nswindow_delegate.mm index 7500a9596be..6e578e2fa67 100644 --- a/chromium/ui/views/cocoa/views_nswindow_delegate.mm +++ b/chromium/ui/views/cocoa/views_nswindow_delegate.mm @@ -39,6 +39,10 @@ parent_->OnVisibilityChanged(); } +- (void)onSystemControlTintChanged:(NSNotification*)notification { + parent_->OnSystemControlTintChanged(); +} + - (void)sheetDidEnd:(NSWindow*)sheet returnCode:(NSInteger)returnCode contextInfo:(void*)contextInfo { diff --git a/chromium/ui/views/color_chooser/color_chooser_view.cc b/chromium/ui/views/color_chooser/color_chooser_view.cc index 7fd92737484..8913282a7f3 100644 --- a/chromium/ui/views/color_chooser/color_chooser_view.cc +++ b/chromium/ui/views/color_chooser/color_chooser_view.cc @@ -446,10 +446,6 @@ void ColorChooserView::WindowClosing() { listener_->OnColorChooserDialogClosed(); } -View* ColorChooserView::GetContentsView() { - return this; -} - void ColorChooserView::ContentsChanged(Textfield* sender, const base::string16& new_contents) { SkColor color = SK_ColorBLACK; diff --git a/chromium/ui/views/color_chooser/color_chooser_view.h b/chromium/ui/views/color_chooser/color_chooser_view.h index 912e6000076..4d42a93b95e 100644 --- a/chromium/ui/views/color_chooser/color_chooser_view.h +++ b/chromium/ui/views/color_chooser/color_chooser_view.h @@ -52,7 +52,6 @@ class VIEWS_EXPORT ColorChooserView : public WidgetDelegateView, View* GetInitiallyFocusedView() override; ui::ModalType GetModalType() const override; void WindowClosing() override; - View* GetContentsView() override; // TextfieldController overrides: void ContentsChanged(Textfield* sender, diff --git a/chromium/ui/views/controls/button/checkbox.cc b/chromium/ui/views/controls/button/checkbox.cc index c1db4715f33..e52c23bf3ff 100644 --- a/chromium/ui/views/controls/button/checkbox.cc +++ b/chromium/ui/views/controls/button/checkbox.cc @@ -12,12 +12,16 @@ #include "ui/base/material_design/material_design_controller.h" #include "ui/base/resource/resource_bundle.h" #include "ui/gfx/canvas.h" +#include "ui/gfx/color_utils.h" #include "ui/gfx/paint_vector_icon.h" #include "ui/gfx/vector_icons_public.h" #include "ui/resources/grit/ui_resources.h" +#include "ui/views/animation/ink_drop_highlight.h" +#include "ui/views/animation/ink_drop_ripple.h" #include "ui/views/controls/button/label_button_border.h" #include "ui/views/painter.h" #include "ui/views/resources/grit/views_resources.h" +#include "ui/views/style/platform_style.h" namespace views { @@ -32,6 +36,12 @@ Checkbox::Checkbox(const base::string16& label) if (UseMd()) { set_request_focus_on_press(false); + SetInkDropMode(PlatformStyle::kUseRipples ? InkDropMode::ON + : InkDropMode::OFF); + set_has_ink_drop_action_on_click(true); + // The "small" size is 21dp, the large size is 1.33 * 21dp = 28dp. + set_ink_drop_size(gfx::Size(21, 21)); + SetFocusPainter(nullptr); } else { std::unique_ptr<LabelButtonBorder> button_border(new LabelButtonBorder()); // Inset the trailing side by a couple pixels for the focus border. @@ -127,10 +137,12 @@ void Checkbox::OnPaint(gfx::Canvas* canvas) { SkPaint focus_paint; focus_paint.setAntiAlias(true); - focus_paint.setColor(GetNativeTheme()->GetSystemColor( - ui::NativeTheme::kColorId_FocusedBorderColor)); + focus_paint.setColor( + SkColorSetA(GetNativeTheme()->GetSystemColor( + ui::NativeTheme::kColorId_FocusedBorderColor), + 0x66)); focus_paint.setStyle(SkPaint::kStroke_Style); - focus_paint.setStrokeWidth(1); + focus_paint.setStrokeWidth(2); PaintFocusRing(canvas, focus_paint); } @@ -152,14 +164,27 @@ void Checkbox::OnNativeThemeChanged(const ui::NativeTheme* theme) { UpdateImage(); } +std::unique_ptr<InkDropRipple> Checkbox::CreateInkDropRipple() const { + return CreateDefaultInkDropRipple(image()->bounds().CenterPoint()); +} + +std::unique_ptr<InkDropHighlight> Checkbox::CreateInkDropHighlight() const { + return nullptr; +} + +SkColor Checkbox::GetInkDropBaseColor() const { + return GetNativeTheme()->GetSystemColor( + ui::NativeTheme::kColorId_ButtonEnabledColor); +} + gfx::ImageSkia Checkbox::GetImage(ButtonState for_state) const { if (UseMd()) { return gfx::CreateVectorIcon( - checked_ ? gfx::VectorIconId::CHECKBOX_ACTIVE - : gfx::VectorIconId::CHECKBOX_NORMAL, - 16, GetNativeTheme()->GetSystemColor( - checked_ ? ui::NativeTheme::kColorId_FocusedBorderColor - : ui::NativeTheme::kColorId_UnfocusedBorderColor)); + GetVectorIconId(), 16, + // When not checked, the icon color matches the button text color. + GetNativeTheme()->GetSystemColor( + checked_ ? ui::NativeTheme::kColorId_FocusedBorderColor + : ui::NativeTheme::kColorId_ButtonEnabledColor)); } const size_t checked_index = checked_ ? 1 : 0; @@ -182,10 +207,14 @@ void Checkbox::SetCustomImage(bool checked, void Checkbox::PaintFocusRing(gfx::Canvas* canvas, const SkPaint& paint) { gfx::RectF focus_rect(image()->bounds()); - focus_rect.Inset(gfx::InsetsF(-.5f)); canvas->DrawRoundRect(focus_rect, 2.f, paint); } +gfx::VectorIconId Checkbox::GetVectorIconId() const { + return checked() ? gfx::VectorIconId::CHECKBOX_ACTIVE + : gfx::VectorIconId::CHECKBOX_NORMAL; +} + void Checkbox::NotifyClick(const ui::Event& event) { SetChecked(!checked()); LabelButton::NotifyClick(event); diff --git a/chromium/ui/views/controls/button/checkbox.h b/chromium/ui/views/controls/button/checkbox.h index 919c141587e..9c03ae6d7f9 100644 --- a/chromium/ui/views/controls/button/checkbox.h +++ b/chromium/ui/views/controls/button/checkbox.h @@ -14,8 +14,15 @@ class SkPaint; +namespace gfx { +enum class VectorIconId; +} + namespace views { +class InkDropHover; +class InkDropRipple; + // A native themed class representing a checkbox. This class does not use // platform specific objects to replicate the native platforms looks and feel. class VIEWS_EXPORT Checkbox : public LabelButton { @@ -45,6 +52,9 @@ class VIEWS_EXPORT Checkbox : public LabelButton { void OnFocus() override; void OnBlur() override; void OnNativeThemeChanged(const ui::NativeTheme* theme) override; + std::unique_ptr<InkDropRipple> CreateInkDropRipple() const override; + std::unique_ptr<InkDropHighlight> CreateInkDropHighlight() const override; + SkColor GetInkDropBaseColor() const override; gfx::ImageSkia GetImage(ButtonState for_state) const override; // Set the image shown for each button state depending on whether it is @@ -57,6 +67,10 @@ class VIEWS_EXPORT Checkbox : public LabelButton { // Paints a focus indicator for the view. virtual void PaintFocusRing(gfx::Canvas* canvas, const SkPaint& paint); + // Gets the vector icon id used to draw the icon based on the current state of + // |checked_|. + virtual gfx::VectorIconId GetVectorIconId() const; + private: // Overridden from Button: void NotifyClick(const ui::Event& event) override; diff --git a/chromium/ui/views/controls/button/custom_button.cc b/chromium/ui/views/controls/button/custom_button.cc index 62b96997799..5bcd0c907fc 100644 --- a/chromium/ui/views/controls/button/custom_button.cc +++ b/chromium/ui/views/controls/button/custom_button.cc @@ -5,7 +5,6 @@ #include "ui/views/controls/button/custom_button.h" #include "ui/accessibility/ax_view_state.h" -#include "ui/base/material_design/material_design_controller.h" #include "ui/events/event.h" #include "ui/events/event_utils.h" #include "ui/events/keycodes/keyboard_codes.h" @@ -163,15 +162,17 @@ bool CustomButton::OnMousePressed(const ui::MouseEvent& event) { bool CustomButton::OnMouseDragged(const ui::MouseEvent& event) { if (state_ != STATE_DISABLED) { const bool should_enter_pushed = ShouldEnterPushedState(event); + const bool should_show_pending = + should_enter_pushed && notify_action_ == NOTIFY_ON_RELEASE && !InDrag(); if (HitTestPoint(event.location())) { SetState(should_enter_pushed ? STATE_PRESSED : STATE_HOVERED); - if (!InDrag() && should_enter_pushed && + if (should_show_pending && ink_drop()->GetTargetInkDropState() == views::InkDropState::HIDDEN) { AnimateInkDrop(views::InkDropState::ACTION_PENDING, &event); } } else { SetState(STATE_NORMAL); - if (!InDrag() && should_enter_pushed && + if (should_show_pending && ink_drop()->GetTargetInkDropState() == views::InkDropState::ACTION_PENDING) { AnimateInkDrop(views::InkDropState::HIDDEN, &event); @@ -201,11 +202,9 @@ void CustomButton::OnMouseReleased(const ui::MouseEvent& event) { void CustomButton::OnMouseCaptureLost() { // Starting a drag results in a MouseCaptureLost. Reset button state. - // TODO(varkha) While in drag only reset the state with Material Design. - // The same logic may applies everywhere so gather any feedback and update. - bool reset_button_state = - !InDrag() || ui::MaterialDesignController::IsModeMaterial(); - if (state_ != STATE_DISABLED && reset_button_state) + // TODO(varkha): Reset the state even while in drag. The same logic may + // applies everywhere so gather any feedback and update. + if (state_ != STATE_DISABLED) SetState(STATE_NORMAL); AnimateInkDrop(views::InkDropState::HIDDEN, nullptr /* event */); } @@ -468,8 +467,13 @@ void CustomButton::NotifyClick(const ui::Event& event) { } void CustomButton::OnClickCanceled(const ui::Event& event) { - AnimateInkDrop(views::InkDropState::HIDDEN, - ui::LocatedEvent::FromIfValid(&event)); + if (ink_drop()->GetTargetInkDropState() == + views::InkDropState::ACTION_PENDING || + ink_drop()->GetTargetInkDropState() == + views::InkDropState::ALTERNATE_ACTION_PENDING) { + AnimateInkDrop(views::InkDropState::HIDDEN, + ui::LocatedEvent::FromIfValid(&event)); + } Button::OnClickCanceled(event); } diff --git a/chromium/ui/views/controls/button/custom_button_unittest.cc b/chromium/ui/views/controls/button/custom_button_unittest.cc index a911e292dce..816d8a4d9cf 100644 --- a/chromium/ui/views/controls/button/custom_button_unittest.cc +++ b/chromium/ui/views/controls/button/custom_button_unittest.cc @@ -9,7 +9,6 @@ #include "build/build_config.h" #include "testing/gtest/include/gtest/gtest.h" #include "ui/base/layout.h" -#include "ui/base/material_design/material_design_controller.h" #include "ui/display/screen.h" #include "ui/events/event_utils.h" #include "ui/events/test/event_generator.h" @@ -57,8 +56,9 @@ class TestContextMenuController : public ContextMenuController { class TestCustomButton : public CustomButton, public ButtonListener { public: - explicit TestCustomButton() + explicit TestCustomButton(bool has_ink_drop_action_on_click) : CustomButton(this) { + set_has_ink_drop_action_on_click(has_ink_drop_action_on_click); } ~TestCustomButton() override {} @@ -108,7 +108,7 @@ class CustomButtonTest : public ViewsTestBase { widget_->Init(params); widget_->Show(); - button_ = new TestCustomButton(); + button_ = new TestCustomButton(false); widget_->SetContentsView(button_); } @@ -117,9 +117,10 @@ class CustomButtonTest : public ViewsTestBase { ViewsTestBase::TearDown(); } - void CreateButtonWithInkDrop(std::unique_ptr<InkDrop> ink_drop) { + void CreateButtonWithInkDrop(std::unique_ptr<InkDrop> ink_drop, + bool has_ink_drop_action_on_click) { delete button_; - button_ = new TestCustomButton(); + button_ = new TestCustomButton(has_ink_drop_action_on_click); InkDropHostViewTestApi(button_).SetInkDrop(std::move(ink_drop)); widget_->SetContentsView(button_); } @@ -350,7 +351,7 @@ TEST_F(CustomButtonTest, AsCustomButton) { // may enter a different ink drop state. TEST_F(CustomButtonTest, ButtonClickTogglesInkDrop) { TestInkDrop* ink_drop = new TestInkDrop(); - CreateButtonWithInkDrop(base::WrapUnique(ink_drop)); + CreateButtonWithInkDrop(base::WrapUnique(ink_drop), false); ui::test::EventGenerator generator(widget()->GetNativeWindow()); generator.set_current_location(gfx::Point(50, 50)); @@ -365,7 +366,7 @@ TEST_F(CustomButtonTest, ButtonClickTogglesInkDrop) { // Releasing capture should also reset PRESSED button state to NORMAL. TEST_F(CustomButtonTest, CaptureLossHidesInkDrop) { TestInkDrop* ink_drop = new TestInkDrop(); - CreateButtonWithInkDrop(base::WrapUnique(ink_drop)); + CreateButtonWithInkDrop(base::WrapUnique(ink_drop), false); ui::test::EventGenerator generator(widget()->GetNativeWindow()); generator.set_current_location(gfx::Point(50, 50)); @@ -378,15 +379,12 @@ TEST_F(CustomButtonTest, CaptureLossHidesInkDrop) { widget()->ReleaseCapture(); SetDraggedView(nullptr); EXPECT_EQ(InkDropState::HIDDEN, ink_drop->GetTargetInkDropState()); - EXPECT_EQ(ui::MaterialDesignController::IsModeMaterial() - ? Button::ButtonState::STATE_NORMAL - : Button::ButtonState::STATE_PRESSED, - button()->state()); + EXPECT_EQ(Button::ButtonState::STATE_NORMAL, button()->state()); } TEST_F(CustomButtonTest, HideInkDropWhenShowingContextMenu) { TestInkDrop* ink_drop = new TestInkDrop(); - CreateButtonWithInkDrop(base::WrapUnique(ink_drop)); + CreateButtonWithInkDrop(base::WrapUnique(ink_drop), false); TestContextMenuController context_menu_controller; button()->set_context_menu_controller(&context_menu_controller); button()->set_hide_ink_drop_when_showing_context_menu(true); @@ -402,7 +400,7 @@ TEST_F(CustomButtonTest, HideInkDropWhenShowingContextMenu) { TEST_F(CustomButtonTest, DontHideInkDropWhenShowingContextMenu) { TestInkDrop* ink_drop = new TestInkDrop(); - CreateButtonWithInkDrop(base::WrapUnique(ink_drop)); + CreateButtonWithInkDrop(base::WrapUnique(ink_drop), false); TestContextMenuController context_menu_controller; button()->set_context_menu_controller(&context_menu_controller); button()->set_hide_ink_drop_when_showing_context_menu(false); @@ -420,7 +418,7 @@ TEST_F(CustomButtonTest, HideInkDropOnBlur) { gfx::Point center(10, 10); TestInkDrop* ink_drop = new TestInkDrop(); - CreateButtonWithInkDrop(base::WrapUnique(ink_drop)); + CreateButtonWithInkDrop(base::WrapUnique(ink_drop), false); button()->OnFocus(); @@ -440,7 +438,7 @@ TEST_F(CustomButtonTest, HideInkDropOnBlur) { TEST_F(CustomButtonTest, InkDropAfterTryingToShowContextMenu) { TestInkDrop* ink_drop = new TestInkDrop(); - CreateButtonWithInkDrop(base::WrapUnique(ink_drop)); + CreateButtonWithInkDrop(base::WrapUnique(ink_drop), false); button()->set_context_menu_controller(nullptr); ink_drop->SetHovered(true); @@ -452,12 +450,15 @@ TEST_F(CustomButtonTest, InkDropAfterTryingToShowContextMenu) { EXPECT_EQ(InkDropState::ACTION_PENDING, ink_drop->GetTargetInkDropState()); } -TEST_F(CustomButtonTest, InkDropShowHideOnMouseDragged) { +// Tests that when button is set to notify on release, dragging mouse out and +// back transitions ink drop states correctly. +TEST_F(CustomButtonTest, InkDropShowHideOnMouseDraggedNotifyOnRelease) { gfx::Point center(10, 10); gfx::Point oob(-1, -1); TestInkDrop* ink_drop = new TestInkDrop(); - CreateButtonWithInkDrop(base::WrapUnique(ink_drop)); + CreateButtonWithInkDrop(base::WrapUnique(ink_drop), false); + button()->set_notify_action(CustomButton::NOTIFY_ON_RELEASE); button()->OnMousePressed(ui::MouseEvent( ui::ET_MOUSE_PRESSED, center, center, ui::EventTimeForNow(), @@ -490,12 +491,54 @@ TEST_F(CustomButtonTest, InkDropShowHideOnMouseDragged) { EXPECT_FALSE(button()->pressed()); } +// Tests that when button is set to notify on press, dragging mouse out and back +// does not change the ink drop state. +TEST_F(CustomButtonTest, InkDropShowHideOnMouseDraggedNotifyOnPress) { + gfx::Point center(10, 10); + gfx::Point oob(-1, -1); + + TestInkDrop* ink_drop = new TestInkDrop(); + CreateButtonWithInkDrop(base::WrapUnique(ink_drop), true); + button()->set_notify_action(CustomButton::NOTIFY_ON_PRESS); + + button()->OnMousePressed(ui::MouseEvent( + ui::ET_MOUSE_PRESSED, center, center, ui::EventTimeForNow(), + ui::EF_LEFT_MOUSE_BUTTON, ui::EF_LEFT_MOUSE_BUTTON)); + + EXPECT_EQ(InkDropState::ACTION_TRIGGERED, ink_drop->GetTargetInkDropState()); + EXPECT_TRUE(button()->pressed()); + + button()->OnMouseDragged( + ui::MouseEvent(ui::ET_MOUSE_PRESSED, oob, oob, ui::EventTimeForNow(), + ui::EF_LEFT_MOUSE_BUTTON, ui::EF_LEFT_MOUSE_BUTTON)); + + EXPECT_EQ(InkDropState::ACTION_TRIGGERED, ink_drop->GetTargetInkDropState()); + + button()->OnMouseDragged(ui::MouseEvent( + ui::ET_MOUSE_PRESSED, center, center, ui::EventTimeForNow(), + ui::EF_LEFT_MOUSE_BUTTON, ui::EF_LEFT_MOUSE_BUTTON)); + + EXPECT_EQ(InkDropState::ACTION_TRIGGERED, ink_drop->GetTargetInkDropState()); + + button()->OnMouseDragged( + ui::MouseEvent(ui::ET_MOUSE_PRESSED, oob, oob, ui::EventTimeForNow(), + ui::EF_LEFT_MOUSE_BUTTON, ui::EF_LEFT_MOUSE_BUTTON)); + + EXPECT_EQ(InkDropState::ACTION_TRIGGERED, ink_drop->GetTargetInkDropState()); + + button()->OnMouseReleased( + ui::MouseEvent(ui::ET_MOUSE_PRESSED, oob, oob, ui::EventTimeForNow(), + ui::EF_LEFT_MOUSE_BUTTON, ui::EF_LEFT_MOUSE_BUTTON)); + + EXPECT_EQ(InkDropState::ACTION_TRIGGERED, ink_drop->GetTargetInkDropState()); +} + TEST_F(CustomButtonTest, InkDropStaysHiddenWhileDragging) { gfx::Point center(10, 10); gfx::Point oob(-1, -1); TestInkDrop* ink_drop = new TestInkDrop(); - CreateButtonWithInkDrop(base::WrapUnique(ink_drop)); + CreateButtonWithInkDrop(base::WrapUnique(ink_drop), false); button()->OnMousePressed(ui::MouseEvent( ui::ET_MOUSE_PRESSED, center, center, ui::EventTimeForNow(), diff --git a/chromium/ui/views/controls/button/image_button.cc b/chromium/ui/views/controls/button/image_button.cc index a9fda37f5b4..483d971bbea 100644 --- a/chromium/ui/views/controls/button/image_button.cc +++ b/chromium/ui/views/controls/button/image_button.cc @@ -215,7 +215,7 @@ void ToggleImageButton::SetToggled(bool toggled) { toggled_ = toggled; SchedulePaint(); - NotifyAccessibilityEvent(ui::AX_EVENT_VALUE_CHANGED, true); + NotifyAccessibilityEvent(ui::AX_EVENT_ARIA_ATTRIBUTE_CHANGED, true); } void ToggleImageButton::SetToggledImage(ButtonState image_state, @@ -270,6 +270,15 @@ bool ToggleImageButton::GetTooltipText(const gfx::Point& p, void ToggleImageButton::GetAccessibleState(ui::AXViewState* state) { ImageButton::GetAccessibleState(state); GetTooltipText(gfx::Point(), &state->name); + + // Use the visual pressed image as a cue for making this control into an + // accessible toggle button. + if ((toggled_ && !images_[ButtonState::STATE_NORMAL].isNull()) || + (!toggled_ && !alternate_images_[ButtonState::STATE_NORMAL].isNull())) { + state->role = ui::AX_ROLE_TOGGLE_BUTTON; + if (toggled_) + state->AddStateFlag(ui::AX_STATE_PRESSED); + } } } // namespace views diff --git a/chromium/ui/views/controls/button/label_button.cc b/chromium/ui/views/controls/button/label_button.cc index 0979be6bfc5..3d7453d774e 100644 --- a/chromium/ui/views/controls/button/label_button.cc +++ b/chromium/ui/views/controls/button/label_button.cc @@ -12,7 +12,6 @@ #include "base/logging.h" #include "base/memory/ptr_util.h" #include "build/build_config.h" -#include "ui/base/material_design/material_design_controller.h" #include "ui/gfx/animation/throb_animation.h" #include "ui/gfx/canvas.h" #include "ui/gfx/color_utils.h" @@ -92,7 +91,6 @@ namespace views { // static const int LabelButton::kHoverAnimationDurationMs = 170; -const int LabelButton::kFocusRectInset = 3; const char LabelButton::kViewClassName[] = "LabelButton"; LabelButton::LabelButton(ButtonListener* listener, const base::string16& text) @@ -127,8 +125,7 @@ LabelButton::LabelButton(ButtonListener* listener, const base::string16& text) label_->SetHorizontalAlignment(gfx::ALIGN_TO_HEAD); // Inset the button focus rect from the actual border; roughly match Windows. - SetFocusPainter(Painter::CreateDashedFocusPainterWithInsets(gfx::Insets( - kFocusRectInset, kFocusRectInset, kFocusRectInset, kFocusRectInset))); + SetFocusPainter(Painter::CreateDashedFocusPainterWithInsets(gfx::Insets(3))); } LabelButton::~LabelButton() {} @@ -192,6 +189,10 @@ void LabelButton::SetFontList(const gfx::FontList& font_list) { label_->SetFontList(cached_normal_font_list_); } +void LabelButton::AdjustFontSize(int font_size_delta) { + LabelButton::SetFontList(GetFontList().DeriveWithSizeDelta(font_size_delta)); +} + void LabelButton::SetElideBehavior(gfx::ElideBehavior elide_behavior) { label_->SetElideBehavior(elide_behavior); } @@ -386,7 +387,13 @@ void LabelButton::EnableCanvasFlippingForRTLUI(bool flip) { } std::unique_ptr<LabelButtonBorder> LabelButton::CreateDefaultBorder() const { - return PlatformStyle::CreateLabelButtonBorder(style()); + if (style_ != Button::STYLE_TEXTBUTTON) + return base::MakeUnique<LabelButtonAssetBorder>(style_); + std::unique_ptr<LabelButtonBorder> border = + base::MakeUnique<LabelButtonBorder>(); + border->set_insets(views::LabelButtonAssetBorder::GetDefaultInsetsForStyle( + style_)); + return border; } void LabelButton::SetBorder(std::unique_ptr<Border> border) { @@ -422,6 +429,9 @@ void LabelButton::OnNativeThemeChanged(const ui::NativeTheme* theme) { ResetLabelEnabledColor(); // Invalidate the layout to pickup the new insets from the border. InvalidateLayout(); + // The entire button has to be repainted here, since the native theme can + // define the tint for the entire background/border/focus ring. + SchedulePaint(); } void LabelButton::AddInkDropLayer(ui::Layer* ink_drop_layer) { @@ -441,10 +451,9 @@ std::unique_ptr<views::InkDropRipple> LabelButton::CreateInkDropRipple() const { return GetText().empty() ? CreateDefaultInkDropRipple( image()->GetMirroredBounds().CenterPoint()) - : std::unique_ptr<views::InkDropRipple>( - new views::FloodFillInkDropRipple( - GetLocalBounds(), GetInkDropCenterBasedOnLastEvent(), - GetInkDropBaseColor(), ink_drop_visible_opacity())); + : base::MakeUnique<views::FloodFillInkDropRipple>( + GetLocalBounds(), GetInkDropCenterBasedOnLastEvent(), + GetInkDropBaseColor(), ink_drop_visible_opacity()); } std::unique_ptr<views::InkDropHighlight> LabelButton::CreateInkDropHighlight() @@ -454,10 +463,10 @@ std::unique_ptr<views::InkDropHighlight> LabelButton::CreateInkDropHighlight() return GetText().empty() ? CreateDefaultInkDropHighlight( gfx::RectF(image()->GetMirroredBounds()).CenterPoint()) - : base::WrapUnique(new views::InkDropHighlight( + : base::MakeUnique<views::InkDropHighlight>( size(), kInkDropSmallCornerRadius, gfx::RectF(GetLocalBounds()).CenterPoint(), - GetInkDropBaseColor())); + GetInkDropBaseColor()); } void LabelButton::StateChanged() { @@ -481,11 +490,24 @@ void LabelButton::GetExtraParams(ui::NativeTheme::ExtraParams* params) const { void LabelButton::ResetColorsFromNativeTheme() { const ui::NativeTheme* theme = GetNativeTheme(); + bool button_style = style() == STYLE_BUTTON; + // Button colors are used only for STYLE_BUTTON, otherwise we use label + // colors. As it turns out, these are almost always the same color anyway in + // pre-MD, although in the MD world labels and buttons get different colors. + // TODO(estade): simplify this by removing STYLE_BUTTON. SkColor colors[STATE_COUNT] = { - theme->GetSystemColor(ui::NativeTheme::kColorId_ButtonEnabledColor), - theme->GetSystemColor(ui::NativeTheme::kColorId_ButtonHoverColor), - theme->GetSystemColor(ui::NativeTheme::kColorId_ButtonHoverColor), - theme->GetSystemColor(ui::NativeTheme::kColorId_ButtonDisabledColor), + theme->GetSystemColor(button_style + ? ui::NativeTheme::kColorId_ButtonEnabledColor + : ui::NativeTheme::kColorId_LabelEnabledColor), + theme->GetSystemColor(button_style + ? ui::NativeTheme::kColorId_ButtonHoverColor + : ui::NativeTheme::kColorId_LabelEnabledColor), + theme->GetSystemColor(button_style + ? ui::NativeTheme::kColorId_ButtonHoverColor + : ui::NativeTheme::kColorId_LabelEnabledColor), + theme->GetSystemColor(button_style + ? ui::NativeTheme::kColorId_ButtonDisabledColor + : ui::NativeTheme::kColorId_LabelDisabledColor), }; // Use hardcoded colors for inverted color scheme support and STYLE_BUTTON. @@ -496,11 +518,11 @@ void LabelButton::ResetColorsFromNativeTheme() { label_->set_background(Background::CreateSolidBackground(SK_ColorBLACK)); label_->SetAutoColorReadabilityEnabled(true); label_->SetShadows(gfx::ShadowValues()); - } else if (style() == STYLE_BUTTON) { - PlatformStyle::ApplyLabelButtonTextStyle(label_, &colors); - label_->set_background(nullptr); } else { + if (style() == STYLE_BUTTON) + PlatformStyle::ApplyLabelButtonTextStyle(label_, &colors); label_->set_background(nullptr); + label_->SetAutoColorReadabilityEnabled(false); } for (size_t state = STATE_NORMAL; state < STATE_COUNT; ++state) { @@ -541,6 +563,7 @@ void LabelButton::SetTextInternal(const base::string16& text) { void LabelButton::ChildPreferredSizeChanged(View* child) { ResetCachedPreferredSize(); PreferredSizeChanged(); + Layout(); } ui::NativeTheme::Part LabelButton::GetThemePart() const { diff --git a/chromium/ui/views/controls/button/label_button.h b/chromium/ui/views/controls/button/label_button.h index 0b5e24e7834..96fc7af6b1f 100644 --- a/chromium/ui/views/controls/button/label_button.h +++ b/chromium/ui/views/controls/button/label_button.h @@ -31,9 +31,6 @@ class VIEWS_EXPORT LabelButton : public CustomButton, // The length of the hover fade animation. static const int kHoverAnimationDurationMs; - // Amount to inset each edge of the button when drawing the focus rectangle. - static const int kFocusRectInset; - static const char kViewClassName[]; LabelButton(ButtonListener* listener, const base::string16& text); @@ -46,7 +43,7 @@ class VIEWS_EXPORT LabelButton : public CustomButton, // Gets or sets the text shown on the button. const base::string16& GetText() const; - void SetText(const base::string16& text); + virtual void SetText(const base::string16& text); // Sets the text color shown for the specified button |for_state| to |color|. void SetTextColor(ButtonState for_state, SkColor color); @@ -62,7 +59,11 @@ class VIEWS_EXPORT LabelButton : public CustomButton, // Gets or sets the font list used by this button. const gfx::FontList& GetFontList() const; - void SetFontList(const gfx::FontList& font_list); + // TODO(estade): make this function protected. + virtual void SetFontList(const gfx::FontList& font_list); + + // Adjusts the font size up or down by the given amount. + virtual void AdjustFontSize(int font_size_delta); // Sets the elide behavior of this button. void SetElideBehavior(gfx::ElideBehavior elide_behavior); @@ -154,6 +155,7 @@ class VIEWS_EXPORT LabelButton : public CustomButton, FRIEND_TEST_ALL_PREFIXES(LabelButtonTest, Image); FRIEND_TEST_ALL_PREFIXES(LabelButtonTest, LabelAndImage); FRIEND_TEST_ALL_PREFIXES(LabelButtonTest, FontList); + FRIEND_TEST_ALL_PREFIXES(LabelButtonTest, ResetColorsFromNativeTheme); void SetTextInternal(const base::string16& text); diff --git a/chromium/ui/views/controls/button/label_button_border.cc b/chromium/ui/views/controls/button/label_button_border.cc index e8347bb9c79..6c3f3ea7c84 100644 --- a/chromium/ui/views/controls/button/label_button_border.cc +++ b/chromium/ui/views/controls/button/label_button_border.cc @@ -139,20 +139,28 @@ void LabelButtonAssetBorder::Paint(const View& view, gfx::Canvas* canvas) { if (animation && animation->is_animating()) { // Linearly interpolate background and foreground painters during animation. + uint8_t fg_alpha = + static_cast<uint8_t>(animation->CurrentValueBetween(0, 255)); + const SkRect sk_rect = gfx::RectToSkRect(rect); - canvas->sk_canvas()->saveLayer(&sk_rect, NULL); - state = native_theme_delegate->GetBackgroundThemeState(&extra); - PaintHelper(this, canvas, state, rect, extra); + SkAutoCanvasRestore auto_restore(canvas->sk_canvas(), false); + canvas->sk_canvas()->saveLayer(&sk_rect, nullptr); + + { + // First, modulate the background by 1 - alpha. + SkAutoCanvasRestore auto_restore(canvas->sk_canvas(), false); + canvas->sk_canvas()->saveLayerAlpha(&sk_rect, 255 - fg_alpha); + state = native_theme_delegate->GetBackgroundThemeState(&extra); + PaintHelper(this, canvas, state, rect, extra); + } + // Then modulate the foreground by alpha, and blend using kPlus_Mode. SkPaint paint; - double scale = animation->GetCurrentValue(); - paint.setXfermode(SkArithmeticMode::Make(0.0f, scale, 1.0 - scale, 0.0)); + paint.setAlpha(fg_alpha); + paint.setXfermodeMode(SkXfermode::kPlus_Mode); canvas->sk_canvas()->saveLayer(&sk_rect, &paint); state = native_theme_delegate->GetForegroundThemeState(&extra); PaintHelper(this, canvas, state, rect, extra); - canvas->sk_canvas()->restore(); - - canvas->sk_canvas()->restore(); } else { PaintHelper(this, canvas, state, rect, extra); } diff --git a/chromium/ui/views/controls/button/label_button_unittest.cc b/chromium/ui/views/controls/button/label_button_unittest.cc index 1b13b8c50a2..5df8d448391 100644 --- a/chromium/ui/views/controls/button/label_button_unittest.cc +++ b/chromium/ui/views/controls/button/label_button_unittest.cc @@ -14,6 +14,7 @@ #include "ui/base/ui_base_switches.h" #include "ui/events/test/event_generator.h" #include "ui/gfx/canvas.h" +#include "ui/gfx/color_utils.h" #include "ui/gfx/font_list.h" #include "ui/gfx/geometry/size.h" #include "ui/gfx/geometry/vector2d.h" @@ -79,20 +80,22 @@ class LabelButtonTest : public test::WidgetTest { // Establish the expected text colors for testing changes due to state. themed_normal_text_color_ = button_->GetNativeTheme()->GetSystemColor( - ui::NativeTheme::kColorId_ButtonEnabledColor); -#if defined(OS_LINUX) && !defined(OS_CHROMEOS) - // The Linux theme provides a non-black highlight text color, but it's not - // used for styled buttons. - styled_highlight_text_color_ = themed_normal_text_color_; - styled_normal_text_color_ = themed_normal_text_color_; -#else - styled_highlight_text_color_ = button_->GetNativeTheme()->GetSystemColor( - ui::NativeTheme::kColorId_ButtonHighlightColor); + ui::NativeTheme::kColorId_LabelEnabledColor); // For styled buttons only, platforms other than Desktop Linux either ignore // NativeTheme and use a hardcoded black or (on Mac) have a NativeTheme that // reliably returns black. styled_normal_text_color_ = SK_ColorBLACK; +#if defined(OS_LINUX) && !defined(OS_CHROMEOS) + // The Linux theme provides a non-black highlight text color, but it's not + // used for styled buttons. + styled_highlight_text_color_ = styled_normal_text_color_ = + button_->GetNativeTheme()->GetSystemColor( + ui::NativeTheme::kColorId_ButtonEnabledColor); +#elif defined(OS_MACOSX) + styled_highlight_text_color_ = SK_ColorWHITE; +#else + styled_highlight_text_color_ = styled_normal_text_color_; #endif } @@ -310,16 +313,29 @@ TEST_F(LabelButtonTest, ChangeTextSize) { const base::string16 text(ASCIIToUTF16("abc")); const base::string16 longer_text(ASCIIToUTF16("abcdefghijklm")); button_->SetText(text); - + button_->SizeToPreferredSize(); + gfx::Rect bounds(button_->bounds()); const int original_width = button_->GetPreferredSize().width(); + EXPECT_EQ(original_width, bounds.width()); - // The button size increases when the text size is increased. + // Reserve more space in the button. + bounds.set_width(bounds.width() * 10); + button_->SetBoundsRect(bounds); + + // Label view in the button is sized to short text. + const int original_label_width = button_->label()->bounds().width(); + + // The button preferred size and the label size increase when the text size + // is increased. button_->SetText(longer_text); - EXPECT_GT(button_->GetPreferredSize().width(), original_width); + EXPECT_GT(button_->label()->bounds().width(), original_label_width * 2); + EXPECT_GT(button_->GetPreferredSize().width(), original_width * 2); - // The button returns to its original size when the original text is restored. + // The button and the label view return to its original size when the original + // text is restored. button_->SetMinSize(gfx::Size()); button_->SetText(text); + EXPECT_EQ(original_label_width, button_->label()->bounds().width()); EXPECT_EQ(original_width, button_->GetPreferredSize().width()); } @@ -405,6 +421,21 @@ TEST_F(LabelButtonTest, HighlightedButtonStyle) { default_before->label()->enabled_color()); } +// Ensure the label gets the correct enabled color after +// LabelButton::ResetColorsFromNativeTheme() is invoked. +TEST_F(LabelButtonTest, ResetColorsFromNativeTheme) { + ASSERT_FALSE(color_utils::IsInvertedColorScheme()); + ASSERT_NE(button_->label()->background_color(), SK_ColorBLACK); + EXPECT_EQ(themed_normal_text_color_, button_->label()->enabled_color()); + + button_->label()->SetBackgroundColor(SK_ColorBLACK); + button_->label()->SetAutoColorReadabilityEnabled(true); + EXPECT_NE(themed_normal_text_color_, button_->label()->enabled_color()); + + button_->ResetColorsFromNativeTheme(); + EXPECT_EQ(themed_normal_text_color_, button_->label()->enabled_color()); +} + // Test fixture for a LabelButton that has an ink drop configured. class InkDropLabelButtonTest : public ViewsTestBase { public: diff --git a/chromium/ui/views/controls/button/md_text_button.cc b/chromium/ui/views/controls/button/md_text_button.cc index 76cd0d335eb..70492dca432 100644 --- a/chromium/ui/views/controls/button/md_text_button.cc +++ b/chromium/ui/views/controls/button/md_text_button.cc @@ -7,37 +7,31 @@ #include "base/i18n/case_conversion.h" #include "ui/base/material_design/material_design_controller.h" #include "ui/gfx/canvas.h" +#include "ui/gfx/color_palette.h" #include "ui/gfx/color_utils.h" #include "ui/native_theme/native_theme.h" +#include "ui/views/animation/flood_fill_ink_drop_ripple.h" #include "ui/views/animation/ink_drop_highlight.h" #include "ui/views/animation/ink_drop_painted_layer_delegates.h" #include "ui/views/background.h" #include "ui/views/border.h" #include "ui/views/controls/button/blue_button.h" +#include "ui/views/controls/focus_ring.h" #include "ui/views/painter.h" +#include "ui/views/style/platform_style.h" namespace views { namespace { -// Inset between clickable region border and button contents (text). -const int kHorizontalPadding = 16; - // Minimum size to reserve for the button contents. const int kMinWidth = 48; -// The stroke width of the focus border in normal and call to action mode. -const int kFocusBorderThickness = 1; -const int kFocusBorderThicknessCta = 2; - -// The corner radius of the focus border roundrect. -const int kFocusBorderCornerRadius = 3; - LabelButton* CreateButton(ButtonListener* listener, const base::string16& text, bool md) { if (md) - return MdTextButton::CreateMdButton(listener, text); + return MdTextButton::Create(listener, text); LabelButton* button = new LabelButton(listener, text); button->SetStyle(CustomButton::STYLE_BUTTON); @@ -55,41 +49,6 @@ const gfx::FontList& GetMdFontList() { } // namespace -namespace internal { - -class MdFocusRing : public View { - public: - MdFocusRing() : thickness_(kFocusBorderThickness) { - SetPaintToLayer(true); - layer()->SetFillsBoundsOpaquely(false); - } - ~MdFocusRing() override {} - - int thickness() const { return thickness_; } - void set_thickness(int thickness) { thickness_ = thickness; } - - // View: - bool CanProcessEventsWithinSubtree() const override { return false; } - - void OnPaint(gfx::Canvas* canvas) override { - MdTextButton::PaintMdFocusRing(canvas, this, thickness_, 0x33); - } - - private: - int thickness_; - - DISALLOW_COPY_AND_ASSIGN(MdFocusRing); -}; - -} // namespace internal - -// static -LabelButton* MdTextButton::CreateStandardButton(ButtonListener* listener, - const base::string16& text) { - return CreateButton(listener, text, - ui::MaterialDesignController::IsModeMaterial()); -} - // static LabelButton* MdTextButton::CreateSecondaryUiButton(ButtonListener* listener, const base::string16& text) { @@ -102,8 +61,8 @@ LabelButton* MdTextButton::CreateSecondaryUiBlueButton( ButtonListener* listener, const base::string16& text) { if (ui::MaterialDesignController::IsSecondaryUiMaterial()) { - MdTextButton* md_button = MdTextButton::CreateMdButton(listener, text); - md_button->SetCallToAction(true); + MdTextButton* md_button = MdTextButton::Create(listener, text); + md_button->SetProminent(true); return md_button; } @@ -111,56 +70,37 @@ LabelButton* MdTextButton::CreateSecondaryUiBlueButton( } // static -MdTextButton* MdTextButton::CreateMdButton(ButtonListener* listener, - const base::string16& text) { +MdTextButton* MdTextButton::Create(ButtonListener* listener, + const base::string16& text) { MdTextButton* button = new MdTextButton(listener); button->SetText(text); button->SetFocusForPlatform(); return button; } -// static -void MdTextButton::PaintMdFocusRing(gfx::Canvas* canvas, - views::View* view, - int thickness, - SkAlpha alpha) { - SkPaint paint; - paint.setAntiAlias(true); - paint.setColor(SkColorSetA(view->GetNativeTheme()->GetSystemColor( - ui::NativeTheme::kColorId_CallToActionColor), - alpha)); - paint.setStyle(SkPaint::kStroke_Style); - paint.setStrokeWidth(thickness); - gfx::RectF rect(view->GetLocalBounds()); - rect.Inset(gfx::InsetsF(thickness / 2.f)); - canvas->DrawRoundRect(rect, kFocusBorderCornerRadius, paint); -} +MdTextButton::~MdTextButton() {} -void MdTextButton::SetCallToAction(bool cta) { - if (is_cta_ == cta) +void MdTextButton::SetProminent(bool is_prominent) { + if (is_prominent_ == is_prominent) return; - is_cta_ = cta; - focus_ring_->set_thickness(cta ? kFocusBorderThicknessCta - : kFocusBorderThickness); + is_prominent_ = is_prominent; UpdateColors(); } -void MdTextButton::Layout() { - LabelButton::Layout(); - gfx::Rect focus_bounds = GetLocalBounds(); - focus_bounds.Inset(gfx::Insets(-focus_ring_->thickness())); - focus_ring_->SetBoundsRect(focus_bounds); +void MdTextButton::SetBgColorOverride(const base::Optional<SkColor>& color) { + bg_color_override_ = color; + UpdateColors(); } void MdTextButton::OnFocus() { LabelButton::OnFocus(); - focus_ring_->SetVisible(true); + FocusRing::Install(this); } void MdTextButton::OnBlur() { LabelButton::OnBlur(); - focus_ring_->SetVisible(false); + FocusRing::Uninstall(this); } void MdTextButton::OnNativeThemeChanged(const ui::NativeTheme* theme) { @@ -172,16 +112,28 @@ SkColor MdTextButton::GetInkDropBaseColor() const { return color_utils::DeriveDefaultIconColor(label()->enabled_color()); } +std::unique_ptr<views::InkDropRipple> MdTextButton::CreateInkDropRipple() + const { + return std::unique_ptr<views::InkDropRipple>( + new views::FloodFillInkDropRipple( + GetLocalBounds(), GetInkDropCenterBasedOnLastEvent(), + GetInkDropBaseColor(), ink_drop_visible_opacity())); +} + +void MdTextButton::StateChanged() { + LabelButton::StateChanged(); + UpdateColors(); +} + std::unique_ptr<views::InkDropHighlight> MdTextButton::CreateInkDropHighlight() const { if (!ShouldShowInkDropHighlight()) return nullptr; - if (!is_cta_) - return LabelButton::CreateInkDropHighlight(); - // The call to action hover effect is a shadow. + // The prominent button hover effect is a shadow. const int kYOffset = 1; - const int kSkiaBlurRadius = 1; + const int kSkiaBlurRadius = 2; + const int shadow_alpha = is_prominent_ ? 0x3D : 0x1A; std::vector<gfx::ShadowValue> shadows; // The notion of blur that gfx::ShadowValue uses is twice the Skia/CSS value. // Skia counts the number of pixels outside the mask area whereas @@ -189,15 +141,17 @@ std::unique_ptr<views::InkDropHighlight> MdTextButton::CreateInkDropHighlight() // the mask bounds. shadows.push_back(gfx::ShadowValue(gfx::Vector2d(0, kYOffset), 2 * kSkiaBlurRadius, - SkColorSetA(SK_ColorBLACK, 0x3D))); - return base::WrapUnique(new InkDropHighlight( + SkColorSetA(SK_ColorBLACK, shadow_alpha))); + const SkColor fill_color = + SkColorSetA(SK_ColorWHITE, is_prominent_ ? 0x0D : 0x05); + return base::MakeUnique<InkDropHighlight>( gfx::RectF(GetLocalBounds()).CenterPoint(), base::WrapUnique(new BorderShadowLayerDelegate( - shadows, GetLocalBounds(), kInkDropSmallCornerRadius)))); + shadows, GetLocalBounds(), fill_color, kInkDropSmallCornerRadius))); } bool MdTextButton::ShouldShowInkDropForFocus() const { - // These types of button use |focus_ring_|. + // These types of button use FocusRing. return false; } @@ -206,74 +160,119 @@ void MdTextButton::SetEnabledTextColors(SkColor color) { UpdateColors(); } +void MdTextButton::SetText(const base::string16& text) { + LabelButton::SetText(text); + UpdatePadding(); +} + +void MdTextButton::AdjustFontSize(int size_delta) { + LabelButton::AdjustFontSize(size_delta); + UpdatePadding(); +} + void MdTextButton::UpdateStyleToIndicateDefaultStatus() { + is_prominent_ = is_prominent_ || is_default(); UpdateColors(); } +void MdTextButton::SetFontList(const gfx::FontList& font_list) { + NOTREACHED() + << "Don't call MdTextButton::SetFontList (it will soon be protected)"; +} + MdTextButton::MdTextButton(ButtonListener* listener) : LabelButton(listener, base::string16()), - focus_ring_(new internal::MdFocusRing()), - is_cta_(false) { - SetHasInkDrop(true); + is_prominent_(false) { + SetInkDropMode(PlatformStyle::kUseRipples ? InkDropMode::ON + : InkDropMode::OFF); set_has_ink_drop_action_on_click(true); SetHorizontalAlignment(gfx::ALIGN_CENTER); SetFocusForPlatform(); SetMinSize(gfx::Size(kMinWidth, 0)); SetFocusPainter(nullptr); label()->SetAutoColorReadabilityEnabled(false); - SetFontList(GetMdFontList()); - - AddChildView(focus_ring_); - focus_ring_->SetVisible(false); set_request_focus_on_press(false); + LabelButton::SetFontList(GetMdFontList()); + + // Paint to a layer so that the canvas is snapped to pixel boundaries (useful + // for fractional DSF). + SetPaintToLayer(true); + layer()->SetFillsBoundsOpaquely(false); +} - // Top and bottom padding depend on the font. Example: if font cap height is - // 9dp, use 8dp bottom padding and 7dp top padding to total 24dp. - const gfx::FontList& font = label()->font_list(); - int text_height = font.GetCapHeight(); - int even_text_height = text_height - (text_height % 2); - const int top_padding = even_text_height - (text_height - even_text_height); - const int bottom_padding = even_text_height; - DCHECK_EQ(3 * even_text_height, top_padding + text_height + bottom_padding); +void MdTextButton::UpdatePadding() { + // Don't use font-based padding when there's no text visible. + if (GetText().empty()) { + SetBorder(Border::NullBorder()); + return; + } - const int inbuilt_top_padding = font.GetBaseline() - font.GetCapHeight(); - const int inbuilt_bottom_padding = - font.GetHeight() - label()->font_list().GetBaseline(); + // Text buttons default to 28dp in height on all platforms when the base font + // is in use, but should grow or shrink if the font size is adjusted up or + // down. When the system font size has been adjusted, the base font will be + // larger than normal such that 28dp might not be enough, so also enforce a + // minimum height of twice the font size. + // Example 1: + // * Normal button on ChromeOS, 12pt Roboto. Button height of 28dp. + // * Button on ChromeOS that has been adjusted to 14pt Roboto. Button height + // of 28 + 2 * 2 = 32dp. + // * Linux user sets base system font size to 17dp. For a normal button, the + // |size_delta| will be zero, so to adjust upwards we double 17 to get 34. + int size_delta = + label()->font_list().GetFontSize() - GetMdFontList().GetFontSize(); + const int kBaseHeight = 28; + int target_height = std::max(kBaseHeight + size_delta * 2, + label()->font_list().GetFontSize() * 2); + + int label_height = label()->GetPreferredSize().height(); + int top_padding = (target_height - label_height) / 2; + int bottom_padding = (target_height - label_height + 1) / 2; + DCHECK_EQ(target_height, label_height + top_padding + bottom_padding); // TODO(estade): can we get rid of the platform style border hoopla if // we apply the MD treatment to all buttons, even GTK buttons? - SetBorder(Border::CreateEmptyBorder( - top_padding - inbuilt_top_padding, kHorizontalPadding, - bottom_padding - inbuilt_bottom_padding, kHorizontalPadding)); + const int kHorizontalPadding = 16; + SetBorder(Border::CreateEmptyBorder(top_padding, kHorizontalPadding, + bottom_padding, kHorizontalPadding)); } -MdTextButton::~MdTextButton() {} - void MdTextButton::UpdateColors() { ui::NativeTheme::ColorId fg_color_id = - is_cta_ ? ui::NativeTheme::kColorId_TextOnCallToActionColor - : ui::NativeTheme::kColorId_ButtonEnabledColor; + is_prominent_ ? ui::NativeTheme::kColorId_TextOnProminentButtonColor + : ui::NativeTheme::kColorId_ButtonEnabledColor; - // When there's no call to action, respect a color override if one has - // been set. For call to action styling, don't let individual buttons - // specify a color. ui::NativeTheme* theme = GetNativeTheme(); - if (is_cta_ || !explicitly_set_normal_color()) + if (!explicitly_set_normal_color()) LabelButton::SetEnabledTextColors(theme->GetSystemColor(fg_color_id)); + // Prominent buttons keep their enabled text color; disabled state is conveyed + // by shading the background instead. + if (is_prominent_) + SetTextColor(STATE_DISABLED, theme->GetSystemColor(fg_color_id)); + SkColor text_color = label()->enabled_color(); SkColor bg_color = - is_cta_ - ? theme->GetSystemColor(ui::NativeTheme::kColorId_CallToActionColor) - : is_default() - ? color_utils::BlendTowardOppositeLuma(text_color, 0xD8) - : SK_ColorTRANSPARENT; + theme->GetSystemColor(ui::NativeTheme::kColorId_DialogBackground); + + if (bg_color_override_) { + bg_color = *bg_color_override_; + } else if (is_prominent_) { + bg_color = theme->GetSystemColor( + ui::NativeTheme::kColorId_ProminentButtonColor); + if (state() == STATE_DISABLED) + bg_color = color_utils::BlendTowardOppositeLuma( + bg_color, gfx::kDisabledControlAlpha); + } - const SkAlpha kStrokeOpacity = 0x1A; - SkColor stroke_color = (is_cta_ || color_utils::IsDark(text_color)) - ? SkColorSetA(SK_ColorBLACK, kStrokeOpacity) - : SkColorSetA(SK_ColorWHITE, 2 * kStrokeOpacity); + if (state() == STATE_PRESSED) { + SkColor shade = + theme->GetSystemColor(ui::NativeTheme::kColorId_ButtonPressedShade); + bg_color = color_utils::GetResultingPaintColor(shade, bg_color); + } + SkColor stroke_color = + is_prominent_ ? SK_ColorTRANSPARENT : SkColorSetA(text_color, 0x33); + DCHECK_EQ(SK_AlphaOPAQUE, static_cast<int>(SkColorGetA(bg_color))); set_background(Background::CreateBackgroundPainter( true, Painter::CreateRoundRectWith1PxBorderPainter( bg_color, stroke_color, kInkDropSmallCornerRadius))); diff --git a/chromium/ui/views/controls/button/md_text_button.h b/chromium/ui/views/controls/button/md_text_button.h index 687de5e43f5..0da68d4bfca 100644 --- a/chromium/ui/views/controls/button/md_text_button.h +++ b/chromium/ui/views/controls/button/md_text_button.h @@ -7,63 +7,61 @@ #include <memory> +#include "base/optional.h" #include "ui/views/controls/button/label_button.h" namespace views { -namespace internal { -class MdFocusRing; -} // namespace internal - // A button class that implements the Material Design text button spec. class VIEWS_EXPORT MdTextButton : public LabelButton { public: - // Creates a normal STYLE_BUTTON LabelButton in pre-MD, or an MdTextButton - // in MD mode. - static LabelButton* CreateStandardButton(ButtonListener* listener, - const base::string16& text); // As above, but only creates an MdTextButton if MD is enabled in the // secondary UI (as opposed to just "top chrome"/"primary" UI). static LabelButton* CreateSecondaryUiButton(ButtonListener* listener, const base::string16& text); static LabelButton* CreateSecondaryUiBlueButton(ButtonListener* listener, const base::string16& text); - static MdTextButton* CreateMdButton(ButtonListener* listener, - const base::string16& text); + static MdTextButton* Create(ButtonListener* listener, + const base::string16& text); + + ~MdTextButton() override; - // Paint an MD-style focus ring on the given canvas at the given bounds. - static void PaintMdFocusRing(gfx::Canvas* canvas, - View* view, - int thickness, - SkAlpha alpha); + // See |is_prominent_|. + void SetProminent(bool is_prominent); - void SetCallToAction(bool cta); + // See |bg_color_override_|. + void SetBgColorOverride(const base::Optional<SkColor>& color); // LabelButton: - void Layout() override; void OnFocus() override; void OnBlur() override; void OnNativeThemeChanged(const ui::NativeTheme* theme) override; + std::unique_ptr<views::InkDropRipple> CreateInkDropRipple() const override; std::unique_ptr<views::InkDropHighlight> CreateInkDropHighlight() const override; SkColor GetInkDropBaseColor() const override; bool ShouldShowInkDropForFocus() const override; void SetEnabledTextColors(SkColor color) override; + void SetText(const base::string16& text) override; + void AdjustFontSize(int size_delta) override; void UpdateStyleToIndicateDefaultStatus() override; + void StateChanged() override; + + protected: + // LabelButton: + void SetFontList(const gfx::FontList& font_list) override; private: - MdTextButton(ButtonListener* listener); - ~MdTextButton() override; + explicit MdTextButton(ButtonListener* listener); + void UpdatePadding(); void UpdateColors(); - // The MD-style focus ring. This is not done via a FocusPainter - // because it needs to paint to a layer so it can extend beyond the bounds of - // |this|. - internal::MdFocusRing* focus_ring_; + // True if this button uses prominent styling (blue fill, etc.). + bool is_prominent_; - // True if this button uses call-to-action styling. - bool is_cta_; + // When set, this provides the background color. + base::Optional<SkColor> bg_color_override_; DISALLOW_COPY_AND_ASSIGN(MdTextButton); }; diff --git a/chromium/ui/views/controls/button/menu_button.cc b/chromium/ui/views/controls/button/menu_button.cc index b89184916bd..38fc310aa62 100644 --- a/chromium/ui/views/controls/button/menu_button.cc +++ b/chromium/ui/views/controls/button/menu_button.cc @@ -9,7 +9,6 @@ #include "ui/base/dragdrop/drag_drop_types.h" #include "ui/base/l10n/l10n_util.h" #include "ui/base/resource/resource_bundle.h" -#include "ui/base/ui_base_switches_util.h" #include "ui/display/screen.h" #include "ui/events/event.h" #include "ui/events/event_constants.h" @@ -146,8 +145,6 @@ bool MenuButton::Activate(const ui::Event* event) { increment_pressed_lock_called_ = nullptr; destroyed_flag_ = nullptr; - menu_closed_time_ = TimeTicks::Now(); - if (!increment_pressed_lock_called && pressed_lock_count_ == 0) { AnimateInkDrop(InkDropState::ACTION_TRIGGERED, ui::LocatedEvent::FromIfValid(event)); @@ -256,17 +253,15 @@ void MenuButton::OnGestureEvent(ui::GestureEvent* event) { SetState(Button::STATE_NORMAL); return; } - if (switches::IsTouchFeedbackEnabled()) { - if (event->type() == ui::ET_GESTURE_TAP_DOWN) { - event->SetHandled(); - if (pressed_lock_count_ == 0) - SetState(Button::STATE_HOVERED); - } else if (state() == Button::STATE_HOVERED && - (event->type() == ui::ET_GESTURE_TAP_CANCEL || - event->type() == ui::ET_GESTURE_END) && - pressed_lock_count_ == 0) { - SetState(Button::STATE_NORMAL); - } + if (event->type() == ui::ET_GESTURE_TAP_DOWN) { + event->SetHandled(); + if (pressed_lock_count_ == 0) + SetState(Button::STATE_HOVERED); + } else if (state() == Button::STATE_HOVERED && + (event->type() == ui::ET_GESTURE_TAP_CANCEL || + event->type() == ui::ET_GESTURE_END) && + pressed_lock_count_ == 0) { + SetState(Button::STATE_NORMAL); } } LabelButton::OnGestureEvent(event); @@ -390,6 +385,7 @@ void MenuButton::DecrementPressedLocked() { // If this was the last lock, manually reset state to the desired state. if (pressed_lock_count_ == 0) { + menu_closed_time_ = TimeTicks::Now(); ButtonState desired_state = STATE_NORMAL; if (should_disable_after_press_) { desired_state = STATE_DISABLED; diff --git a/chromium/ui/views/controls/button/radio_button.cc b/chromium/ui/views/controls/button/radio_button.cc index cf1008020ac..451df585bb2 100644 --- a/chromium/ui/views/controls/button/radio_button.cc +++ b/chromium/ui/views/controls/button/radio_button.cc @@ -67,40 +67,6 @@ RadioButton::RadioButton(const base::string16& label, int group_id) RadioButton::~RadioButton() { } -void RadioButton::SetChecked(bool checked) { - if (checked == RadioButton::checked()) - return; - if (checked) { - // We can't just get the root view here because sometimes the radio - // button isn't attached to a root view (e.g., if it's part of a tab page - // that is currently not active). - View* container = parent(); - while (container && container->parent()) - container = container->parent(); - if (container) { - Views other; - container->GetViewsInGroup(GetGroup(), &other); - for (Views::iterator i(other.begin()); i != other.end(); ++i) { - if (*i != this) { - if (strcmp((*i)->GetClassName(), kViewClassName)) { - NOTREACHED() << "radio-button-nt has same group as other non " - "radio-button-nt views."; - continue; - } - RadioButton* peer = static_cast<RadioButton*>(*i); - peer->SetChecked(false); - } - } - } - } - Checkbox::SetChecked(checked); -} - -void RadioButton::PaintFocusRing(gfx::Canvas* canvas, const SkPaint& paint) { - canvas->DrawCircle(gfx::PointF(image()->bounds().CenterPoint()), - image()->width() / 2 + .5f, paint); -} - const char* RadioButton::GetClassName() const { return kViewClassName; } @@ -152,16 +118,43 @@ ui::NativeTheme::Part RadioButton::GetThemePart() const { return ui::NativeTheme::kRadio; } -gfx::ImageSkia RadioButton::GetImage(ButtonState for_state) const { - if (!UseMd()) - return Checkbox::GetImage(for_state); +void RadioButton::SetChecked(bool checked) { + if (checked == RadioButton::checked()) + return; + if (checked) { + // We can't just get the root view here because sometimes the radio + // button isn't attached to a root view (e.g., if it's part of a tab page + // that is currently not active). + View* container = parent(); + while (container && container->parent()) + container = container->parent(); + if (container) { + Views other; + container->GetViewsInGroup(GetGroup(), &other); + for (Views::iterator i(other.begin()); i != other.end(); ++i) { + if (*i != this) { + if (strcmp((*i)->GetClassName(), kViewClassName)) { + NOTREACHED() << "radio-button-nt has same group as other non " + "radio-button-nt views."; + continue; + } + RadioButton* peer = static_cast<RadioButton*>(*i); + peer->SetChecked(false); + } + } + } + } + Checkbox::SetChecked(checked); +} + +void RadioButton::PaintFocusRing(gfx::Canvas* canvas, const SkPaint& paint) { + canvas->DrawCircle(gfx::RectF(image()->bounds()).CenterPoint(), + image()->width() / 2, paint); +} - return gfx::CreateVectorIcon( - checked() ? gfx::VectorIconId::RADIO_BUTTON_ACTIVE - : gfx::VectorIconId::RADIO_BUTTON_NORMAL, - 16, GetNativeTheme()->GetSystemColor( - checked() ? ui::NativeTheme::kColorId_FocusedBorderColor - : ui::NativeTheme::kColorId_UnfocusedBorderColor)); +gfx::VectorIconId RadioButton::GetVectorIconId() const { + return checked() ? gfx::VectorIconId::RADIO_BUTTON_ACTIVE + : gfx::VectorIconId::RADIO_BUTTON_NORMAL; } } // namespace views diff --git a/chromium/ui/views/controls/button/radio_button.h b/chromium/ui/views/controls/button/radio_button.h index e2858d37783..f28968abd5b 100644 --- a/chromium/ui/views/controls/button/radio_button.h +++ b/chromium/ui/views/controls/button/radio_button.h @@ -33,11 +33,11 @@ class VIEWS_EXPORT RadioButton : public Checkbox { // Overridden from LabelButton: ui::NativeTheme::Part GetThemePart() const override; - gfx::ImageSkia GetImage(ButtonState for_state) const override; // Overridden from Checkbox: void SetChecked(bool checked) override; void PaintFocusRing(gfx::Canvas* canvas, const SkPaint& paint) override; + gfx::VectorIconId GetVectorIconId() const override; private: DISALLOW_COPY_AND_ASSIGN(RadioButton); diff --git a/chromium/ui/views/controls/button/toggle_button.cc b/chromium/ui/views/controls/button/toggle_button.cc new file mode 100644 index 00000000000..30bf562a548 --- /dev/null +++ b/chromium/ui/views/controls/button/toggle_button.cc @@ -0,0 +1,147 @@ +// Copyright 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "ui/views/controls/button/toggle_button.h" + +#include "third_party/skia/include/core/SkDrawLooper.h" +#include "third_party/skia/include/core/SkPaint.h" +#include "ui/gfx/canvas.h" +#include "ui/gfx/color_palette.h" +#include "ui/gfx/color_utils.h" +#include "ui/views/animation/ink_drop_ripple.h" +#include "ui/views/border.h" + +namespace views { + +namespace { + +// Constants are measured in dip. +const int kTrackHeight = 12; +const int kTrackWidth = 28; +// Margins from edge of track to edge of view. +const int kTrackVerticalMargin = 5; +const int kTrackHorizontalMargin = 6; +// Margin from edge of thumb to closest edge of view. Note that the thumb +// margins must be sufficiently large to allow space for the shadow. +const int kThumbHorizontalMargin = 4; +// Margin from top/bottom edge of thumb to top/bottom edge of view. +const int kThumbVerticalMargin = 3; + +// TODO(estade): get the base color (black) from the theme? +const SkColor kTrackOffColor = + SkColorSetA(SK_ColorBLACK, gfx::kDisabledControlAlpha); + +} // namespace + +ToggleButton::ToggleButton(ButtonListener* listener) + : CustomButton(listener), is_on_(false), slide_animation_(this) { + slide_animation_.SetSlideDuration(80 /* ms */); + slide_animation_.SetTweenType(gfx::Tween::LINEAR); + SetBorder(Border::CreateEmptyBorder( + gfx::Insets(kTrackVerticalMargin, kTrackHorizontalMargin))); + SetInkDropMode(InkDropMode::ON); + set_has_ink_drop_action_on_click(true); +} + +ToggleButton::~ToggleButton() {} + +void ToggleButton::SetIsOn(bool is_on, bool animate) { + if (is_on_ == is_on) + return; + + is_on_ = is_on; + if (!animate) + slide_animation_.Reset(is_on_ ? 1.0 : 0.0); + else if (is_on_) + slide_animation_.Show(); + else + slide_animation_.Hide(); +} + +gfx::Size ToggleButton::GetPreferredSize() const { + gfx::Rect rect(0, 0, kTrackWidth, kTrackHeight); + if (border()) + rect.Inset(-border()->GetInsets()); + return rect.size(); +} + +void ToggleButton::OnPaint(gfx::Canvas* canvas) { + SkAlpha blend = + static_cast<SkAlpha>(SK_AlphaOPAQUE * slide_animation_.GetCurrentValue()); + + // Track. + gfx::RectF track_rect(GetContentsBounds()); + SkPaint track_paint; + track_paint.setAntiAlias(true); + const SkColor track_on_color = + SkColorSetA(GetNativeTheme()->GetSystemColor( + ui::NativeTheme::kColorId_ProminentButtonColor), + 0xFF / 2); + track_paint.setColor( + color_utils::AlphaBlend(track_on_color, kTrackOffColor, blend)); + canvas->DrawRoundRect(track_rect, track_rect.height() / 2, track_paint); + + // Thumb. + gfx::Rect thumb_bounds = GetThumbBounds(); + SkPaint thumb_paint; + std::vector<gfx::ShadowValue> shadows; + shadows.emplace_back(gfx::Vector2d(0, 1), 4.f, + SkColorSetA(SK_ColorBLACK, 0x99)); + thumb_paint.setLooper(gfx::CreateShadowDrawLooperCorrectBlur(shadows)); + thumb_paint.setStyle(SkPaint::kFill_Style); + thumb_paint.setAntiAlias(true); + const SkColor thumb_on_color = GetNativeTheme()->GetSystemColor( + ui::NativeTheme::kColorId_ProminentButtonColor); + // TODO(estade): get this color from the theme? + const SkColor thumb_off_color = SK_ColorWHITE; + thumb_paint.setColor( + color_utils::AlphaBlend(thumb_on_color, thumb_off_color, blend)); + canvas->DrawCircle(gfx::RectF(thumb_bounds).CenterPoint(), + thumb_bounds.height() / 2.f, thumb_paint); +} + +void ToggleButton::NotifyClick(const ui::Event& event) { + SetIsOn(!is_on(), true); + CustomButton::NotifyClick(event); +} + +void ToggleButton::OnNativeThemeChanged(const ui::NativeTheme* theme) { + SchedulePaint(); +} + +std::unique_ptr<InkDropRipple> ToggleButton::CreateInkDropRipple() const { + return CreateDefaultInkDropRipple(GetThumbBounds().CenterPoint()); +} + +SkColor ToggleButton::GetInkDropBaseColor() const { + return is_on() + ? GetNativeTheme()->GetSystemColor( + ui::NativeTheme::kColorId_ProminentButtonColor) + : kTrackOffColor; +} + +bool ToggleButton::ShouldShowInkDropHighlight() const { + return false; +} + +void ToggleButton::AnimationProgressed(const gfx::Animation* animation) { + if (animation == &slide_animation_) + SchedulePaint(); + else + CustomButton::AnimationProgressed(animation); +} + +gfx::Rect ToggleButton::GetThumbBounds() const { + gfx::Rect thumb_bounds = GetLocalBounds(); + thumb_bounds.Inset(gfx::Insets(kThumbVerticalMargin, kThumbHorizontalMargin)); + thumb_bounds.set_x(thumb_bounds.x() + + slide_animation_.GetCurrentValue() * + (thumb_bounds.width() - thumb_bounds.height())); + // The thumb is a circle, so the width should match the height. + thumb_bounds.set_width(thumb_bounds.height()); + thumb_bounds.set_x(GetMirroredXForRect(thumb_bounds)); + return thumb_bounds; +} + +} // namespace views diff --git a/chromium/ui/views/controls/button/toggle_button.h b/chromium/ui/views/controls/button/toggle_button.h new file mode 100644 index 00000000000..347d1d050a3 --- /dev/null +++ b/chromium/ui/views/controls/button/toggle_button.h @@ -0,0 +1,48 @@ +// Copyright 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef UI_VIEWS_CONTROLS_BUTTON_TOGGLE_BUTTON_H_ +#define UI_VIEWS_CONTROLS_BUTTON_TOGGLE_BUTTON_H_ + +#include "ui/gfx/animation/slide_animation.h" +#include "ui/views/controls/button/custom_button.h" + +namespace views { + +// This view presents a button that has two states: on and off. This is similar +// to a checkbox but has no text and looks more like a two-state horizontal +// slider. +class VIEWS_EXPORT ToggleButton : public CustomButton { + public: + explicit ToggleButton(ButtonListener* listener); + ~ToggleButton() override; + + void SetIsOn(bool is_on, bool animate); + bool is_on() const { return is_on_; } + + private: + // CustomButton: + gfx::Size GetPreferredSize() const override; + void OnPaint(gfx::Canvas* canvas) override; + void NotifyClick(const ui::Event& event) override; + void OnNativeThemeChanged(const ui::NativeTheme* theme) override; + std::unique_ptr<InkDropRipple> CreateInkDropRipple() const override; + SkColor GetInkDropBaseColor() const override; + bool ShouldShowInkDropHighlight() const override; + + // gfx::AnimationDelegate: + void AnimationProgressed(const gfx::Animation* animation) override; + + // Calculates the bounding box for the thumb (the circle). + gfx::Rect GetThumbBounds() const; + + bool is_on_; + gfx::SlideAnimation slide_animation_; + + DISALLOW_COPY_AND_ASSIGN(ToggleButton); +}; + +} // namespace views + +#endif // UI_VIEWS_CONTROLS_BUTTON_TOGGLE_BUTTON_H_ diff --git a/chromium/ui/views/controls/button/vector_icon_button.cc b/chromium/ui/views/controls/button/vector_icon_button.cc new file mode 100644 index 00000000000..6fc94971e63 --- /dev/null +++ b/chromium/ui/views/controls/button/vector_icon_button.cc @@ -0,0 +1,61 @@ +// Copyright 2015 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "ui/views/controls/button/vector_icon_button.h" + +#include "ui/gfx/color_palette.h" +#include "ui/gfx/color_utils.h" +#include "ui/gfx/paint_vector_icon.h" +#include "ui/gfx/vector_icons_public.h" +#include "ui/views/border.h" +#include "ui/views/controls/button/vector_icon_button_delegate.h" +#include "ui/views/painter.h" + +namespace views { + +namespace { + +// Extra space around the buttons to increase their event target size. +const int kButtonExtraTouchSize = 4; + +} // namespace + +VectorIconButton::VectorIconButton(VectorIconButtonDelegate* delegate) + : views::ImageButton(delegate), + delegate_(delegate), + id_(gfx::VectorIconId::VECTOR_ICON_NONE) { + SetInkDropMode(InkDropMode::ON); + set_has_ink_drop_action_on_click(true); + SetImageAlignment(views::ImageButton::ALIGN_CENTER, + views::ImageButton::ALIGN_MIDDLE); + SetFocusPainter(nullptr); +} + +VectorIconButton::~VectorIconButton() {} + +void VectorIconButton::SetIcon(gfx::VectorIconId id) { + id_ = id; + + if (!border()) { + SetBorder(views::Border::CreateEmptyBorder( + kButtonExtraTouchSize, kButtonExtraTouchSize, kButtonExtraTouchSize, + kButtonExtraTouchSize)); + } +} + +void VectorIconButton::OnThemeChanged() { + SkColor icon_color = + color_utils::DeriveDefaultIconColor(delegate_->GetVectorIconBaseColor()); + gfx::ImageSkia image = gfx::CreateVectorIcon(id_, icon_color); + SetImage(views::CustomButton::STATE_NORMAL, &image); + image = gfx::CreateVectorIcon(id_, SkColorSetA(icon_color, 0xff / 2)); + SetImage(views::CustomButton::STATE_DISABLED, &image); + set_ink_drop_base_color(icon_color); +} + +void VectorIconButton::OnNativeThemeChanged(const ui::NativeTheme* theme) { + OnThemeChanged(); +} + +} // namespace views diff --git a/chromium/ui/views/controls/button/vector_icon_button.h b/chromium/ui/views/controls/button/vector_icon_button.h new file mode 100644 index 00000000000..dd994051a50 --- /dev/null +++ b/chromium/ui/views/controls/button/vector_icon_button.h @@ -0,0 +1,43 @@ +// Copyright 2015 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef UI_VIEWS_CONTROLS_BUTTON_VECTOR_ICON_BUTTON_H_ +#define UI_VIEWS_CONTROLS_BUTTON_VECTOR_ICON_BUTTON_H_ + +#include "base/macros.h" +#include "ui/views/controls/button/image_button.h" + +namespace gfx { +enum class VectorIconId; +} + +namespace views { + +class VectorIconButtonDelegate; + +// A button that has an image and no text, with the image defined by a vector +// icon identifier. +class VIEWS_EXPORT VectorIconButton : public views::ImageButton { + public: + explicit VectorIconButton(VectorIconButtonDelegate* delegate); + ~VectorIconButton() override; + + // Sets the icon to display and provides a callback which should return the + // text color from which to derive this icon's color. + void SetIcon(gfx::VectorIconId id); + + // views::ImageButton: + void OnThemeChanged() override; + void OnNativeThemeChanged(const ui::NativeTheme* theme) override; + + private: + VectorIconButtonDelegate* delegate_; + gfx::VectorIconId id_; + + DISALLOW_COPY_AND_ASSIGN(VectorIconButton); +}; + +} // namespace views + +#endif // UI_VIEWS_CONTROLS_BUTTON_VECTOR_ICON_BUTTON_H_ diff --git a/chromium/ui/views/controls/button/vector_icon_button_delegate.cc b/chromium/ui/views/controls/button/vector_icon_button_delegate.cc new file mode 100644 index 00000000000..f2240cc4cd7 --- /dev/null +++ b/chromium/ui/views/controls/button/vector_icon_button_delegate.cc @@ -0,0 +1,13 @@ +// Copyright 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "ui/views/controls/button/vector_icon_button_delegate.h" + +namespace views { + +SkColor VectorIconButtonDelegate::GetVectorIconBaseColor() const { + return SK_ColorBLACK; +} + +} // namespace views diff --git a/chromium/ui/views/controls/button/vector_icon_button_delegate.h b/chromium/ui/views/controls/button/vector_icon_button_delegate.h new file mode 100644 index 00000000000..e4cccaa6790 --- /dev/null +++ b/chromium/ui/views/controls/button/vector_icon_button_delegate.h @@ -0,0 +1,25 @@ +// Copyright 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef UI_VIEWS_CONTROLS_BUTTON_VECTOR_ICON_DELEGATE_BUTTON_H_ +#define UI_VIEWS_CONTROLS_BUTTON_VECTOR_ICON_DELEGATE_BUTTON_H_ + +#include "third_party/skia/include/core/SkColor.h" +#include "ui/views/controls/button/button.h" +#include "ui/views/views_export.h" + +namespace views { + +// A ButtonListener that also provides a color to use for drawing an icon. +class VIEWS_EXPORT VectorIconButtonDelegate : public ButtonListener { + public: + virtual SkColor GetVectorIconBaseColor() const; + + protected: + ~VectorIconButtonDelegate() override {} +}; + +} // namespace views + +#endif // UI_VIEWS_CONTROLS_BUTTON_VECTOR_ICON_BUTTON_DELEGATE_H_ diff --git a/chromium/ui/views/controls/combobox/combobox.cc b/chromium/ui/views/controls/combobox/combobox.cc index e888c4dec06..cc474726ba9 100644 --- a/chromium/ui/views/controls/combobox/combobox.cc +++ b/chromium/ui/views/controls/combobox/combobox.cc @@ -14,6 +14,7 @@ #include "ui/accessibility/ax_view_state.h" #include "ui/base/default_style.h" #include "ui/base/ime/input_method.h" +#include "ui/base/material_design/material_design_controller.h" #include "ui/base/models/combobox_model.h" #include "ui/base/models/combobox_model_observer.h" #include "ui/base/resource/resource_bundle.h" @@ -27,12 +28,16 @@ #include "ui/native_theme/native_theme.h" #include "ui/native_theme/native_theme_aura.h" #include "ui/resources/grit/ui_resources.h" +#include "ui/views/animation/flood_fill_ink_drop_ripple.h" +#include "ui/views/animation/ink_drop_highlight.h" #include "ui/views/background.h" #include "ui/views/controls/button/custom_button.h" #include "ui/views/controls/button/label_button.h" #include "ui/views/controls/combobox/combobox_listener.h" +#include "ui/views/controls/focus_ring.h" #include "ui/views/controls/focusable_border.h" #include "ui/views/controls/menu/menu_config.h" +#include "ui/views/controls/menu/menu_model_adapter.h" #include "ui/views/controls/menu/menu_runner.h" #include "ui/views/controls/prefix_selector.h" #include "ui/views/controls/textfield/textfield.h" @@ -87,6 +92,10 @@ const int kFocusedPressedMenuButtonImages[] = #undef MENU_IMAGE_GRID +bool UseMd() { + return ui::MaterialDesignController::IsSecondaryUiMaterial(); +} + gfx::Rect PositionArrowWithinContainer(const gfx::Rect& container_bounds, const gfx::Size& arrow_size, Combobox::Style style) { @@ -106,22 +115,46 @@ gfx::Rect PositionArrowWithinContainer(const gfx::Rect& container_bounds, // The transparent button which holds a button state but is not rendered. class TransparentButton : public CustomButton { public: - TransparentButton(ButtonListener* listener) + TransparentButton(ButtonListener* listener, bool animate_state_change) : CustomButton(listener) { - SetAnimationDuration(LabelButton::kHoverAnimationDurationMs); + set_animate_on_state_change(animate_state_change); + if (animate_state_change) + SetAnimationDuration(LabelButton::kHoverAnimationDurationMs); SetFocusBehavior(FocusBehavior::NEVER); + set_notify_action(PlatformStyle::kMenuNotifyActivationAction); + + if (UseMd()) { + SetInkDropMode(PlatformStyle::kUseRipples ? InkDropMode::ON + : InkDropMode::OFF); + set_has_ink_drop_action_on_click(true); + } } ~TransparentButton() override {} bool OnMousePressed(const ui::MouseEvent& mouse_event) override { - parent()->RequestFocus(); - return true; + if (!UseMd()) + parent()->RequestFocus(); + return CustomButton::OnMousePressed(mouse_event); } double GetAnimationValue() const { return hover_animation().GetCurrentValue(); } + // Overridden from InkDropHost: + std::unique_ptr<InkDropRipple> CreateInkDropRipple() const override { + return std::unique_ptr<views::InkDropRipple>( + new views::FloodFillInkDropRipple( + GetLocalBounds(), GetInkDropCenterBasedOnLastEvent(), + GetNativeTheme()->GetSystemColor( + ui::NativeTheme::kColorId_LabelEnabledColor), + ink_drop_visible_opacity())); + } + + std::unique_ptr<InkDropHighlight> CreateInkDropHighlight() const override { + return nullptr; + } + private: DISALLOW_COPY_AND_ASSIGN(TransparentButton); }; @@ -244,15 +277,15 @@ void PaintArrowButton( const char Combobox::kViewClassName[] = "views/Combobox"; // Adapts a ui::ComboboxModel to a ui::MenuModel. -class Combobox::ComboboxMenuModelAdapter : public ui::MenuModel, - public ui::ComboboxModelObserver { +class Combobox::ComboboxMenuModel : public ui::MenuModel, + public ui::ComboboxModelObserver { public: - ComboboxMenuModelAdapter(Combobox* owner, ui::ComboboxModel* model) + ComboboxMenuModel(Combobox* owner, ui::ComboboxModel* model) : owner_(owner), model_(model) { model_->AddObserver(this); } - ~ComboboxMenuModelAdapter() override { model_->RemoveObserver(this); } + ~ComboboxMenuModel() override { model_->RemoveObserver(this); } private: bool UseCheckmarks() const { @@ -351,7 +384,7 @@ class Combobox::ComboboxMenuModelAdapter : public ui::MenuModel, Combobox* owner_; // Weak. Owns this. ui::ComboboxModel* model_; // Weak. - DISALLOW_COPY_AND_ASSIGN(ComboboxMenuModelAdapter); + DISALLOW_COPY_AND_ASSIGN(ComboboxMenuModel); }; //////////////////////////////////////////////////////////////////////////////// @@ -360,12 +393,12 @@ class Combobox::ComboboxMenuModelAdapter : public ui::MenuModel, Combobox::Combobox(ui::ComboboxModel* model, Style style) : model_(model), style_(style), - listener_(NULL), + listener_(nullptr), selected_index_(style == STYLE_ACTION ? 0 : model_->GetDefaultIndex()), invalid_(false), - menu_model_adapter_(new ComboboxMenuModelAdapter(this, model)), - text_button_(new TransparentButton(this)), - arrow_button_(new TransparentButton(this)), + menu_model_(new ComboboxMenuModel(this, model)), + text_button_(new TransparentButton(this, style_ == STYLE_ACTION)), + arrow_button_(new TransparentButton(this, style_ == STYLE_ACTION)), size_to_largest_label_(style_ == STYLE_NORMAL), weak_ptr_factory_(this) { ModelChanged(); @@ -376,11 +409,12 @@ Combobox::Combobox(ui::ComboboxModel* model, Style style) #endif UpdateBorder(); - arrow_image_ = PlatformStyle::CreateComboboxArrow(enabled(), style); - // set_background() takes ownership but takes a raw pointer. - std::unique_ptr<Background> b = - PlatformStyle::CreateComboboxBackground(GetArrowContainerWidth()); - set_background(b.release()); + if (UseMd()) { + // set_background() takes ownership but takes a raw pointer. + std::unique_ptr<Background> b = + PlatformStyle::CreateComboboxBackground(GetArrowContainerWidth()); + set_background(b.release()); + } // Initialize the button images. Button::ButtonState button_states[] = { @@ -406,6 +440,15 @@ Combobox::Combobox(ui::ComboboxModel* model, Style style) arrow_button_->SetVisible(true); AddChildView(text_button_); AddChildView(arrow_button_); + + // A layer is applied to make sure that canvas bounds are snapped to pixel + // boundaries (for the sake of drawing the arrow). + if (UseMd()) { + SetPaintToLayer(true); + layer()->SetFillsBoundsOpaquely(false); + } else { + arrow_image_ = PlatformStyle::CreateComboboxArrow(enabled(), style); + } } Combobox::~Combobox() { @@ -475,7 +518,7 @@ void Combobox::SetInvalid(bool invalid) { } void Combobox::Layout() { - PrefixDelegate::Layout(); + View::Layout(); int text_button_width = 0; int arrow_button_width = 0; @@ -498,8 +541,20 @@ void Combobox::Layout() { } void Combobox::OnEnabledChanged() { - PrefixDelegate::OnEnabledChanged(); - arrow_image_ = PlatformStyle::CreateComboboxArrow(enabled(), style_); + View::OnEnabledChanged(); + if (!UseMd()) + arrow_image_ = PlatformStyle::CreateComboboxArrow(enabled(), style_); +} + +void Combobox::OnNativeThemeChanged(const ui::NativeTheme* theme) { + if (!UseMd()) + return; + + set_background(Background::CreateBackgroundPainter( + true, Painter::CreateSolidRoundRectPainter( + theme->GetSystemColor( + ui::NativeTheme::kColorId_TextfieldDefaultBackground), + FocusableBorder::kCornerRadiusDp))); } int Combobox::GetRowCount() { @@ -595,22 +650,21 @@ bool Combobox::OnKeyPressed(const ui::KeyEvent& e) { new_index = GetAdjacentIndex(model(), -1, selected_index_); break; - // Click the button only when the button style mode. case ui::VKEY_SPACE: if (style_ == STYLE_ACTION) { // When pressing space, the click event will be raised after the key is // released. text_button_->SetState(Button::STATE_PRESSED); } else { - return false; + show_menu = true; } break; - // Click the button only when the button style mode. case ui::VKEY_RETURN: - if (style_ != STYLE_ACTION) - return false; - OnPerformAction(); + if (style_ == STYLE_ACTION) + OnPerformAction(); + else + show_menu = true; break; default: @@ -662,6 +716,8 @@ void Combobox::OnFocus() { View::OnFocus(); // Border renders differently when focused. SchedulePaint(); + if (UseMd()) + FocusRing::Install(this); } void Combobox::OnBlur() { @@ -672,6 +728,8 @@ void Combobox::OnBlur() { selector_->OnViewBlur(); // Border renders differently when focused. SchedulePaint(); + if (UseMd()) + FocusRing::Uninstall(this); } void Combobox::GetAccessibleState(ui::AXViewState* state) { @@ -686,7 +744,8 @@ void Combobox::ButtonPressed(Button* sender, const ui::Event& event) { if (!enabled()) return; - RequestFocus(); + if (!UseMd()) + RequestFocus(); if (sender == text_button_) { OnPerformAction(); @@ -732,8 +791,10 @@ void Combobox::PaintText(gfx::Canvas* canvas) { int y = insets.top(); int text_height = height() - insets.height(); SkColor text_color = GetNativeTheme()->GetSystemColor( - enabled() ? ui::NativeTheme::kColorId_LabelEnabledColor : - ui::NativeTheme::kColorId_LabelDisabledColor); + UseMd() ? (enabled() ? ui::NativeTheme::kColorId_TextfieldDefaultColor + : ui::NativeTheme::kColorId_TextfieldReadOnlyColor) + : (enabled() ? ui::NativeTheme::kColorId_LabelEnabledColor + : ui::NativeTheme::kColorId_LabelDisabledColor)); DCHECK_GE(selected_index_, 0); DCHECK_LT(selected_index_, model()->GetItemCount()); @@ -758,13 +819,41 @@ void Combobox::PaintText(gfx::Canvas* canvas) { PositionArrowWithinContainer(arrow_bounds, ArrowSize(), style_); AdjustBoundsForRTLUI(&arrow_bounds); - canvas->DrawImageInt(arrow_image_, arrow_bounds.x(), arrow_bounds.y()); + if (UseMd()) { + // Since this is a core piece of UI and vector icons don't handle fractional + // scale factors particularly well, manually draw an arrow and make sure it + // looks good at all scale factors. + float dsf = canvas->UndoDeviceScaleFactor(); + SkScalar x = std::ceil(arrow_bounds.x() * dsf); + SkScalar y = std::ceil(arrow_bounds.y() * dsf); + SkScalar height = std::floor(arrow_bounds.height() * dsf); + SkPath path; + // This epsilon makes sure that all the aliasing pixels are slightly more + // than half full. Otherwise, rounding issues cause some to be considered + // slightly less than half full and come out a little lighter. + const SkScalar kEpsilon = 0.0001f; + path.moveTo(x - kEpsilon, y); + path.rLineTo(height, height); + path.rLineTo(2 * kEpsilon, 0); + path.rLineTo(height, -height); + path.close(); + SkPaint paint; + SkColor arrow_color = GetNativeTheme()->GetSystemColor( + ui::NativeTheme::kColorId_ButtonEnabledColor); + if (!enabled()) + arrow_color = SkColorSetA(arrow_color, gfx::kDisabledControlAlpha); + paint.setColor(arrow_color); + paint.setAntiAlias(true); + canvas->DrawPath(path, paint); + } else { + canvas->DrawImageInt(arrow_image_, arrow_bounds.x(), arrow_bounds.y()); + } } void Combobox::PaintButtons(gfx::Canvas* canvas) { DCHECK(style_ == STYLE_ACTION); - gfx::ScopedRTLFlipCanvas scoped_canvas(canvas, bounds()); + gfx::ScopedRTLFlipCanvas scoped_canvas(canvas, width()); bool focused = HasFocus(); const std::vector<const gfx::ImageSkia*>& arrow_button_images = @@ -841,16 +930,22 @@ void Combobox::ShowDropDownMenu(ui::MenuSourceType source_type) { // Allow |menu_runner_| to be set by the testing API, but if this method is // ever invoked recursively, ensure the old menu is closed. if (!menu_runner_ || menu_runner_->IsRunning()) { + menu_model_adapter_.reset(new MenuModelAdapter( + menu_model_.get(), base::Bind(&Combobox::OnMenuClosed, + base::Unretained(this), original_state))); menu_runner_.reset( - new MenuRunner(menu_model_adapter_.get(), MenuRunner::COMBOBOX)); - } - if (menu_runner_->RunMenuAt(GetWidget(), nullptr, bounds, anchor_position, - source_type) == MenuRunner::MENU_DELETED) { - return; + new MenuRunner(menu_model_adapter_->CreateMenu(), + MenuRunner::COMBOBOX | MenuRunner::ASYNC)); } + menu_runner_->RunMenuAt(GetWidget(), nullptr, bounds, anchor_position, + source_type); +} + +void Combobox::OnMenuClosed(Button::ButtonState original_button_state) { menu_runner_.reset(); + menu_model_adapter_.reset(); if (arrow_button_) - arrow_button_->SetState(original_state); + arrow_button_->SetState(original_button_state); closed_time_ = base::Time::Now(); // Need to explicitly clear mouse handler so that events get sent @@ -873,7 +968,7 @@ void Combobox::OnPerformAction() { } gfx::Size Combobox::ArrowSize() const { - return arrow_image_.size(); + return UseMd() ? gfx::Size(8, 4) : arrow_image_.size(); } gfx::Size Combobox::GetContentSize() const { @@ -886,8 +981,7 @@ gfx::Size Combobox::GetContentSize() const { if (size_to_largest_label_ || i == selected_index_) { width = std::max( - width, - gfx::GetStringWidth(menu_model_adapter_->GetLabelAt(i), font_list)); + width, gfx::GetStringWidth(menu_model_->GetLabelAt(i), font_list)); } } return gfx::Size(width, font_list.GetHeight()); @@ -895,13 +989,16 @@ gfx::Size Combobox::GetContentSize() const { PrefixSelector* Combobox::GetPrefixSelector() { if (!selector_) - selector_.reset(new PrefixSelector(this)); + selector_.reset(new PrefixSelector(this, this)); return selector_.get(); } int Combobox::GetArrowContainerWidth() const { + const int kMdPaddingWidth = 8; + int arrow_pad = UseMd() ? kMdPaddingWidth + : PlatformStyle::kComboboxNormalArrowPadding; int padding = style_ == STYLE_NORMAL - ? PlatformStyle::kComboboxNormalArrowPadding * 2 + ? arrow_pad * 2 : kActionLeftPadding + kActionRightPadding; return ArrowSize().width() + padding; } diff --git a/chromium/ui/views/controls/combobox/combobox.h b/chromium/ui/views/controls/combobox/combobox.h index 5cd44549de0..12c0bec723f 100644 --- a/chromium/ui/views/controls/combobox/combobox.h +++ b/chromium/ui/views/controls/combobox/combobox.h @@ -28,6 +28,7 @@ class ComboboxTestApi; class ComboboxListener; class CustomButton; +class MenuModelAdapter; class MenuRunner; class Painter; class PrefixSelector; @@ -41,7 +42,9 @@ class PrefixSelector; // * STYLE_ACTION: clicking on the text notifies the listener. The menu can be // shown only by clicking on the arrow. The selected index is always reverted to // 0 after the listener is notified. -class VIEWS_EXPORT Combobox : public PrefixDelegate, public ButtonListener { +class VIEWS_EXPORT Combobox : public View, + public PrefixDelegate, + public ButtonListener { public: // The style of the combobox. enum Style { @@ -95,6 +98,7 @@ class VIEWS_EXPORT Combobox : public PrefixDelegate, public ButtonListener { void GetAccessibleState(ui::AXViewState* state) override; void Layout() override; void OnEnabledChanged() override; + void OnNativeThemeChanged(const ui::NativeTheme* theme) override; // Overridden from PrefixDelegate: int GetRowCount() override; @@ -113,7 +117,7 @@ class VIEWS_EXPORT Combobox : public PrefixDelegate, public ButtonListener { private: friend class test::ComboboxTestApi; - class ComboboxMenuModelAdapter; + class ComboboxMenuModel; // Updates the border according to the current state. void UpdateBorder(); @@ -130,6 +134,9 @@ class VIEWS_EXPORT Combobox : public PrefixDelegate, public ButtonListener { // Show the drop down list void ShowDropDownMenu(ui::MenuSourceType source_type); + // Cleans up after the menu as closed + void OnMenuClosed(Button::ButtonState original_button_state); + // Called when the selection is changed by the user. void OnPerformAction(); @@ -172,8 +179,8 @@ class VIEWS_EXPORT Combobox : public PrefixDelegate, public ButtonListener { // A helper used to select entries by keyboard input. std::unique_ptr<PrefixSelector> selector_; - // Adapts a ComboboxModel for use by a views MenuRunner. - std::unique_ptr<ui::MenuModel> menu_model_adapter_; + // The ComboboxModel for use by |menu_runner_|. + std::unique_ptr<ui::MenuModel> menu_model_; // Like MenuButton, we use a time object in order to keep track of when the // combobox was closed. The time is used for simulating menu behavior; that @@ -204,7 +211,8 @@ class VIEWS_EXPORT Combobox : public PrefixDelegate, public ButtonListener { // Set while the dropdown is showing. Ensures the menu is closed if |this| is // destroyed. - std::unique_ptr<views::MenuRunner> menu_runner_; + std::unique_ptr<MenuModelAdapter> menu_model_adapter_; + std::unique_ptr<MenuRunner> menu_runner_; // The image to be drawn for this combobox's arrow. gfx::ImageSkia arrow_image_; diff --git a/chromium/ui/views/controls/combobox/combobox_unittest.cc b/chromium/ui/views/controls/combobox/combobox_unittest.cc index 8d585c63a94..ec9ade9b90c 100644 --- a/chromium/ui/views/controls/combobox/combobox_unittest.cc +++ b/chromium/ui/views/controls/combobox/combobox_unittest.cc @@ -233,17 +233,25 @@ class ComboboxTest : public ViewsTestBase { return widget_->GetFocusManager()->GetFocusedView(); } - void PerformClick(const gfx::Point& point) { + void PerformMousePress(const gfx::Point& point) { ui::MouseEvent pressed_event = ui::MouseEvent( ui::ET_MOUSE_PRESSED, point, point, ui::EventTimeForNow(), ui::EF_LEFT_MOUSE_BUTTON, ui::EF_LEFT_MOUSE_BUTTON); widget_->OnMouseEvent(&pressed_event); + } + + void PerformMouseRelease(const gfx::Point& point) { ui::MouseEvent released_event = ui::MouseEvent( ui::ET_MOUSE_RELEASED, point, point, ui::EventTimeForNow(), ui::EF_LEFT_MOUSE_BUTTON, ui::EF_LEFT_MOUSE_BUTTON); widget_->OnMouseEvent(&released_event); } + void PerformClick(const gfx::Point& point) { + PerformMousePress(point); + PerformMouseRelease(point); + } + // We need widget to populate wrapper class. Widget* widget_; @@ -586,17 +594,31 @@ TEST_F(ComboboxTest, NotifyOnClickWithMouse) { // Click the right side (arrow button). The menu is shown. int menu_show_count = 0; test_api_->InstallTestMenuRunner(&menu_show_count); + const gfx::Point right_point(combobox_->x() + combobox_->width() - 1, + combobox_->y() + combobox_->height() / 2); + EXPECT_EQ(0, menu_show_count); - PerformClick(gfx::Point(combobox_->x() + combobox_->width() - 1, - combobox_->y() + combobox_->height() / 2)); - EXPECT_FALSE(listener.on_perform_action_called()); + +// On Mac, actions occur on mouse down. Otherwise mouse up. +#if defined(OS_MACOSX) + const int kActOnMouseDown = 1; +#else + const int kActOnMouseDown = 0; +#endif + + PerformMousePress(right_point); + EXPECT_EQ(kActOnMouseDown, menu_show_count); + PerformMouseRelease(right_point); EXPECT_EQ(1, menu_show_count); // Click the left side (text button). The click event is notified. + const gfx::Point left_point( + gfx::Point(combobox_->x() + 1, combobox_->y() + combobox_->height() / 2)); test_api_->InstallTestMenuRunner(&menu_show_count); - PerformClick(gfx::Point(combobox_->x() + 1, - combobox_->y() + combobox_->height() / 2)); - EXPECT_TRUE(listener.on_perform_action_called()); + + PerformMousePress(left_point); + PerformMouseRelease(left_point); + EXPECT_EQ(1, menu_show_count); // Unchanged. EXPECT_EQ(0, listener.perform_action_index()); } @@ -604,10 +626,14 @@ TEST_F(ComboboxTest, NotifyOnClickWithMouse) { TEST_F(ComboboxTest, ConsumingPressKeyEvents) { InitCombobox(nullptr, Combobox::STYLE_NORMAL); - EXPECT_FALSE(combobox_->OnKeyPressed( + int menu_show_count = 0; + test_api_->InstallTestMenuRunner(&menu_show_count); + EXPECT_TRUE(combobox_->OnKeyPressed( ui::KeyEvent(ui::ET_KEY_PRESSED, ui::VKEY_RETURN, ui::EF_NONE))); - EXPECT_FALSE(combobox_->OnKeyPressed( + EXPECT_EQ(1, menu_show_count); + EXPECT_TRUE(combobox_->OnKeyPressed( ui::KeyEvent(ui::ET_KEY_PRESSED, ui::VKEY_SPACE, ui::EF_NONE))); + EXPECT_EQ(2, menu_show_count); } TEST_F(ComboboxTest, ConsumingKeyPressEventsActionStyle) { diff --git a/chromium/ui/views/controls/focus_ring.cc b/chromium/ui/views/controls/focus_ring.cc new file mode 100644 index 00000000000..8e1702b3788 --- /dev/null +++ b/chromium/ui/views/controls/focus_ring.cc @@ -0,0 +1,84 @@ +// Copyright 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "ui/views/controls/focus_ring.h" + +#include "ui/gfx/canvas.h" +#include "ui/native_theme/native_theme.h" +#include "ui/views/controls/focusable_border.h" + +namespace views { + +namespace { + +// The stroke width of the focus border in dp. +constexpr float kFocusHaloThicknessDp = 2.f; + +// The focus indicator should hug the normal border, when present (as in the +// case of text buttons). Since it's drawn outside the parent view, we have to +// increase the rounding slightly. +constexpr float kFocusHaloCornerRadiusDp = + FocusableBorder::kCornerRadiusDp + kFocusHaloThicknessDp / 2.f; + +} // namespace + +const char FocusRing::kViewClassName[] = "FocusRing"; + +// static +void FocusRing::Install(views::View* parent) { + DCHECK(parent->HasFocus()); + View* ring = new FocusRing(); + parent->AddChildView(ring); + ring->Layout(); +} + +// static +void FocusRing::Uninstall(views::View* parent) { + for (int i = 0; i < parent->child_count(); ++i) { + if (parent->child_at(i)->GetClassName() == kViewClassName) { + delete parent->child_at(i); + return; + } + } + NOTREACHED(); +} + +const char* FocusRing::GetClassName() const { + return kViewClassName; +} + +bool FocusRing::CanProcessEventsWithinSubtree() const { + return false; +} + +void FocusRing::Layout() { + // The focus ring handles its own sizing, which is simply to fill the parent + // and extend a little beyond its borders. + gfx::Rect focus_bounds = parent()->GetLocalBounds(); + focus_bounds.Inset(gfx::Insets(-kFocusHaloThicknessDp)); + SetBoundsRect(focus_bounds); +} + +void FocusRing::OnPaint(gfx::Canvas* canvas) { + SkPaint paint; + paint.setAntiAlias(true); + paint.setColor(SkColorSetA(GetNativeTheme()->GetSystemColor( + ui::NativeTheme::kColorId_FocusedBorderColor), + 0x66)); + paint.setStyle(SkPaint::kStroke_Style); + paint.setStrokeWidth(kFocusHaloThicknessDp); + gfx::RectF rect(GetLocalBounds()); + rect.Inset(gfx::InsetsF(kFocusHaloThicknessDp / 2.f)); + canvas->DrawRoundRect(rect, kFocusHaloCornerRadiusDp, paint); +} + +FocusRing::FocusRing() { + // A layer is necessary to paint beyond the parent's bounds. + SetPaintToLayer(true); + layer()->SetFillsBoundsOpaquely(false); +} + +FocusRing::~FocusRing() {} + +} // namespace views diff --git a/chromium/ui/views/controls/focus_ring.h b/chromium/ui/views/controls/focus_ring.h new file mode 100644 index 00000000000..2438bc7b9dd --- /dev/null +++ b/chromium/ui/views/controls/focus_ring.h @@ -0,0 +1,40 @@ +// Copyright 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef UI_VIEWS_CONTROLS_FOCUS_RING_H_ +#define UI_VIEWS_CONTROLS_FOCUS_RING_H_ + +#include "ui/views/view.h" + +namespace views { + +// FocusRing is a View that is designed to act as an indicator of focus for its +// parent. It is a stand-alone view that paints to a layer which extends beyond +// the bounds of its parent view. +class FocusRing : public View { + public: + // Create a FocusRing and adds it to |parent|. + static void Install(views::View* parent); + + // Removes the FocusRing from |parent|. + static void Uninstall(views::View* parent); + + // View: + const char* GetClassName() const override; + bool CanProcessEventsWithinSubtree() const override; + void Layout() override; + void OnPaint(gfx::Canvas* canvas) override; + + private: + static const char kViewClassName[]; + + FocusRing(); + ~FocusRing() override; + + DISALLOW_COPY_AND_ASSIGN(FocusRing); +}; + +} // views + +#endif // UI_VIEWS_CONTROLS_FOCUS_RING_H_ diff --git a/chromium/ui/views/controls/focusable_border.cc b/chromium/ui/views/controls/focusable_border.cc index a4e76ff1ba2..fbe8cb2a4e4 100644 --- a/chromium/ui/views/controls/focusable_border.cc +++ b/chromium/ui/views/controls/focusable_border.cc @@ -9,7 +9,9 @@ #include "ui/base/material_design/material_design_controller.h" #include "ui/gfx/canvas.h" #include "ui/gfx/color_palette.h" +#include "ui/gfx/color_utils.h" #include "ui/gfx/geometry/insets.h" +#include "ui/gfx/scoped_canvas.h" #include "ui/gfx/skia_util.h" #include "ui/native_theme/native_theme.h" #include "ui/views/controls/textfield/textfield.h" @@ -41,22 +43,35 @@ void FocusableBorder::UseDefaultColor() { } void FocusableBorder::Paint(const View& view, gfx::Canvas* canvas) { + // In harmony, the focus indicator is a FocusRing. + if (ui::MaterialDesignController::IsSecondaryUiMaterial() && view.HasFocus()) + return; + SkPaint paint; paint.setStyle(SkPaint::kStroke_Style); paint.setColor(GetCurrentColor(view)); - SkPath path; if (ui::MaterialDesignController::IsSecondaryUiMaterial()) { - path.moveTo(Textfield::kTextPadding, view.height() - 1); - path.rLineTo(view.width() - Textfield::kTextPadding * 2, 0); - path.offset(0.5f, 0.5f); - paint.setStrokeWidth(SkIntToScalar(1)); + gfx::ScopedCanvas scoped(canvas); + float dsf = canvas->UndoDeviceScaleFactor(); + // Scale the rect and snap to pixel boundaries. + gfx::RectF rect(gfx::ScaleToEnclosingRect(view.GetLocalBounds(), dsf)); + rect.Inset(gfx::InsetsF(0.5f)); + SkPath path; + float corner_radius_px = kCornerRadiusDp * dsf; + path.addRoundRect(gfx::RectFToSkRect(rect), corner_radius_px, + corner_radius_px); + const int kStrokeWidthPx = 1; + paint.setStrokeWidth(SkIntToScalar(kStrokeWidthPx)); + paint.setAntiAlias(true); + canvas->DrawPath(path, paint); } else { + SkPath path; path.addRect(gfx::RectToSkRect(view.GetLocalBounds()), SkPath::kCW_Direction); paint.setStrokeWidth(SkIntToScalar(2)); + canvas->DrawPath(path, paint); } - canvas->DrawPath(path, paint); } gfx::Insets FocusableBorder::GetInsets() const { @@ -74,9 +89,17 @@ void FocusableBorder::SetInsets(int top, int left, int bottom, int right) { SkColor FocusableBorder::GetCurrentColor(const View& view) const { if (!use_default_color_) return override_color_; - return view.GetNativeTheme()->GetSystemColor( + + SkColor color = view.GetNativeTheme()->GetSystemColor( view.HasFocus() ? ui::NativeTheme::kColorId_FocusedBorderColor : ui::NativeTheme::kColorId_UnfocusedBorderColor); + if (ui::MaterialDesignController::IsSecondaryUiMaterial() && + !view.enabled()) { + return color_utils::BlendTowardOppositeLuma(color, + gfx::kDisabledControlAlpha); + } + + return color; } } // namespace views diff --git a/chromium/ui/views/controls/focusable_border.h b/chromium/ui/views/controls/focusable_border.h index 862049ab185..fd5084601d9 100644 --- a/chromium/ui/views/controls/focusable_border.h +++ b/chromium/ui/views/controls/focusable_border.h @@ -20,6 +20,8 @@ namespace views { // A Border class to draw a focused border around a field (e.g textfield). class VIEWS_EXPORT FocusableBorder : public Border { public: + static constexpr float kCornerRadiusDp = 2.f; + FocusableBorder(); ~FocusableBorder() override; diff --git a/chromium/ui/views/controls/label.cc b/chromium/ui/views/controls/label.cc index f03084ae6bd..0c4667dad17 100644 --- a/chromium/ui/views/controls/label.cc +++ b/chromium/ui/views/controls/label.cc @@ -28,15 +28,6 @@ #include "ui/native_theme/native_theme.h" namespace views { -namespace { - -const gfx::FontList& GetDefaultFontList() { - ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance(); - return rb.GetFontListWithDelta(ui::kLabelFontSizeDelta); -} - -} // namespace - // static const char Label::kViewClassName[] = "Label"; const int Label::kFocusBorderPadding = 1; @@ -54,6 +45,12 @@ Label::Label(const base::string16& text, const gfx::FontList& font_list) { Label::~Label() { } +// static +const gfx::FontList& Label::GetDefaultFontList() { + ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance(); + return rb.GetFontListWithDelta(ui::kLabelFontSizeDelta); +} + void Label::SetFontList(const gfx::FontList& font_list) { is_first_paint_text_ = true; render_text_->SetFontList(font_list); @@ -599,8 +596,8 @@ void Label::UpdateColorsFromTheme(const ui::NativeTheme* theme) { ui::NativeTheme::kColorId_LabelDisabledColor); } if (!background_color_set_) { - background_color_ = theme->GetSystemColor( - ui::NativeTheme::kColorId_LabelBackgroundColor); + background_color_ = + theme->GetSystemColor(ui::NativeTheme::kColorId_DialogBackground); } RecalculateColors(); } diff --git a/chromium/ui/views/controls/label.h b/chromium/ui/views/controls/label.h index 1663256bc8e..9421d1dc68f 100644 --- a/chromium/ui/views/controls/label.h +++ b/chromium/ui/views/controls/label.h @@ -27,6 +27,8 @@ class VIEWS_EXPORT Label : public View { Label(const base::string16& text, const gfx::FontList& font_list); ~Label() override; + static const gfx::FontList& GetDefaultFontList(); + // Gets or sets the fonts used by this label. const gfx::FontList& font_list() const { return render_text_->font_list(); } diff --git a/chromium/ui/views/controls/link.cc b/chromium/ui/views/controls/link.cc index 405625b6637..176330bf30d 100644 --- a/chromium/ui/views/controls/link.cc +++ b/chromium/ui/views/controls/link.cc @@ -30,9 +30,7 @@ Link::Link() : Link(base::string16()) {} Link::Link(const base::string16& title) : Label(title), requested_enabled_color_(gfx::kPlaceholderColor), - requested_enabled_color_set_(false), - requested_pressed_color_(gfx::kPlaceholderColor), - requested_pressed_color_set_(false) { + requested_enabled_color_set_(false) { Init(); } @@ -176,12 +174,6 @@ void Link::SetEnabledColor(SkColor color) { Label::SetEnabledColor(GetEnabledColor()); } -void Link::SetPressedColor(SkColor color) { - requested_pressed_color_set_ = true; - requested_pressed_color_ = color; - Label::SetEnabledColor(GetEnabledColor()); -} - void Link::SetUnderline(bool underline) { if (underline_ == underline) return; @@ -241,21 +233,16 @@ void Link::ConfigureFocus() { } SkColor Link::GetEnabledColor() { - // In material mode, there is no pressed effect, so always use the unpressed - // color. - if (!pressed_ || ui::MaterialDesignController::IsModeMaterial()) { - if (!requested_enabled_color_set_ && GetNativeTheme()) - return GetNativeTheme()->GetSystemColor( - ui::NativeTheme::kColorId_LinkEnabled); - + if (requested_enabled_color_set_) return requested_enabled_color_; - } - if (!requested_pressed_color_set_ && GetNativeTheme()) + if (GetNativeTheme()) { return GetNativeTheme()->GetSystemColor( - ui::NativeTheme::kColorId_LinkPressed); + pressed_ ? ui::NativeTheme::kColorId_LinkPressed + : ui::NativeTheme::kColorId_LinkEnabled); + } - return requested_pressed_color_; + return gfx::kPlaceholderColor; } } // namespace views diff --git a/chromium/ui/views/controls/link.h b/chromium/ui/views/controls/link.h index 58cdc08b92a..692c29e34fd 100644 --- a/chromium/ui/views/controls/link.h +++ b/chromium/ui/views/controls/link.h @@ -52,9 +52,8 @@ class VIEWS_EXPORT Link : public Label { void OnNativeThemeChanged(const ui::NativeTheme* theme) override; void SetEnabledColor(SkColor color) override; - void SetPressedColor(SkColor color); - // TODO(estade): almost all the places that call this pass false. With MD, - // false is already the default so those callsites can be removed. + // TODO(estade): almost all the places that call this pass false. With + // Harmony, false is already the default so those callsites can be removed. void SetUnderline(bool underline); static const char kViewClassName[]; diff --git a/chromium/ui/views/controls/md_slider.cc b/chromium/ui/views/controls/md_slider.cc new file mode 100644 index 00000000000..cb6f75f4c0b --- /dev/null +++ b/chromium/ui/views/controls/md_slider.cc @@ -0,0 +1,133 @@ +// Copyright 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "ui/views/controls/md_slider.h" + +#include "third_party/skia/include/core/SkColor.h" +#include "third_party/skia/include/core/SkPaint.h" +#include "ui/gfx/animation/slide_animation.h" +#include "ui/gfx/canvas.h" +#include "ui/gfx/geometry/point.h" +#include "ui/gfx/geometry/rect.h" +#include "ui/views/controls/slider.h" + +namespace views { + +// Color of slider at the active and the disabled state, respectively. +const SkColor kActiveColor = SkColorSetARGB(0xFF, 0x42, 0x85, 0xF4); +const SkColor kDisabledColor = SkColorSetARGB(0x42, 0x00, 0x00, 0x00); +const uint8_t kHighlightColorAlpha = 0x4D; + +// The thickness of the slider. +const int kLineThickness = 2; + +// The radius of the thumb and the highlighted thumb of the slider, +// respectively. +const float kThumbRadius = 6.f; +const float kThumbHighlightRadius = 10.f; + +// The stroke of the thumb when the slider is disabled. +const int kThumbStroke = 2; + +// Duration of the thumb highlight growing effect animation. +const int kSlideHighlightChangeDurationMs = 150; + +MdSlider::MdSlider(SliderListener* listener) + : Slider(listener), is_active_(true), thumb_highlight_radius_(0.f) { + SchedulePaint(); +} + +MdSlider::~MdSlider() {} + +void MdSlider::OnPaint(gfx::Canvas* canvas) { + Slider::OnPaint(canvas); + + // Paint the slider. + const gfx::Rect content = GetContentsBounds(); + const int width = content.width() - kThumbRadius * 2; + const int full = GetAnimatingValue() * width; + const int empty = width - full; + const int y = content.height() / 2 - kLineThickness / 2; + const int x = content.x() + full + kThumbRadius; + const SkColor current_thumb_color = + is_active_ ? kActiveColor : kDisabledColor; + canvas->FillRect(gfx::Rect(content.x(), y, full, kLineThickness), + current_thumb_color); + canvas->FillRect(gfx::Rect(x + kThumbRadius, y, empty, kLineThickness), + kDisabledColor); + + gfx::Point thumb_center(x, content.height() / 2); + + // Paint the thumb highlight if it exists. + if (is_active_ && thumb_highlight_radius_ > kThumbRadius) { + SkPaint highlight; + SkColor kHighlightColor = SkColorSetA(kActiveColor, kHighlightColorAlpha); + highlight.setColor(kHighlightColor); + highlight.setFlags(SkPaint::kAntiAlias_Flag); + canvas->DrawCircle(thumb_center, thumb_highlight_radius_, highlight); + } + + // Paint the thumb of the slider. + SkPaint paint; + paint.setColor(current_thumb_color); + paint.setFlags(SkPaint::kAntiAlias_Flag); + + if (!is_active_) { + paint.setStrokeWidth(kThumbStroke); + paint.setStyle(SkPaint::kStroke_Style); + } + canvas->DrawCircle( + thumb_center, + is_active_ ? kThumbRadius : (kThumbRadius - kThumbStroke / 2), paint); +} + +const char* MdSlider::GetClassName() const { + return "MdSlider"; +} + +void MdSlider::UpdateState(bool control_on) { + is_active_ = control_on; + SchedulePaint(); +} + +int MdSlider::GetThumbWidth() { + return kThumbRadius * 2; +} + +void MdSlider::SetHighlighted(bool is_highlighted) { + if (!highlight_animation_) { + if (!is_highlighted) + return; + + highlight_animation_.reset(new gfx::SlideAnimation(this)); + highlight_animation_->SetSlideDuration(kSlideHighlightChangeDurationMs); + } + if (is_highlighted) + highlight_animation_->Show(); + else + highlight_animation_->Hide(); +} + +void MdSlider::AnimationProgressed(const gfx::Animation* animation) { + if (animation != highlight_animation_.get()) { + Slider::AnimationProgressed(animation); + return; + } + thumb_highlight_radius_ = + animation->CurrentValueBetween(kThumbRadius, kThumbHighlightRadius); + SchedulePaint(); +} + +void MdSlider::AnimationEnded(const gfx::Animation* animation) { + if (animation != highlight_animation_.get()) { + Slider::AnimationEnded(animation); + return; + } + if (animation == highlight_animation_.get() && + !highlight_animation_->IsShowing()) { + highlight_animation_.reset(); + } +} + +} // namespace views diff --git a/chromium/ui/views/controls/md_slider.h b/chromium/ui/views/controls/md_slider.h new file mode 100644 index 00000000000..9a534f5736f --- /dev/null +++ b/chromium/ui/views/controls/md_slider.h @@ -0,0 +1,56 @@ +// Copyright 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef UI_VIEWS_CONTROLS_MD_SLIDER_H_ +#define UI_VIEWS_CONTROLS_MD_SLIDER_H_ + +#include "base/macros.h" +#include "ui/views/controls/slider.h" +#include "ui/views/view.h" +#include "ui/views/views_export.h" + +namespace gfx { +class SlideAnimation; +} + +namespace views { + +// TODO(yiyix): When material design is enabled by default, use +// MdSlider as the default slider implementation. (crbug.com/614453) +class VIEWS_EXPORT MdSlider : public Slider { + public: + explicit MdSlider(SliderListener* listener); + ~MdSlider() override; + + // ui::Slider: + void UpdateState(bool control_on) override; + + // views::View: + void OnPaint(gfx::Canvas* canvas) override; + const char* GetClassName() const override; + + protected: + // ui::Slider: + int GetThumbWidth() override; + void SetHighlighted(bool is_highlighted) override; + + private: + // gfx::AnimationDelegate: + void AnimationProgressed(const gfx::Animation* animation) override; + void AnimationEnded(const gfx::Animation* animation) override; + + // Record whether the slider is in the active state or the disabled state. + bool is_active_; + + // Animating value of the current radius of the thumb's highlight. + float thumb_highlight_radius_; + + std::unique_ptr<gfx::SlideAnimation> highlight_animation_; + + DISALLOW_COPY_AND_ASSIGN(MdSlider); +}; + +} // namespace views + +#endif // UI_VIEWS_CONTROLS_MD_SLIDER_H_ diff --git a/chromium/ui/views/controls/menu/menu_config.cc b/chromium/ui/views/controls/menu/menu_config.cc index 720a69aa707..001179c5198 100644 --- a/chromium/ui/views/controls/menu/menu_config.cc +++ b/chromium/ui/views/controls/menu/menu_config.cc @@ -6,13 +6,14 @@ #include "base/macros.h" #include "ui/views/controls/menu/menu_image_util.h" +#include "ui/views/round_rect_painter.h" namespace views { MenuConfig::MenuConfig() : arrow_color(SK_ColorBLACK), menu_vertical_border_size(3), - menu_horizontal_border_size(0), + menu_horizontal_border_size(views::RoundRectPainter::kBorderWidth), submenu_horizontal_inset(3), item_top_margin(4), item_bottom_margin(3), diff --git a/chromium/ui/views/controls/menu/menu_config_mac.mm b/chromium/ui/views/controls/menu/menu_config_mac.mm index 2e00665c377..fc66f894436 100644 --- a/chromium/ui/views/controls/menu/menu_config_mac.mm +++ b/chromium/ui/views/controls/menu/menu_config_mac.mm @@ -31,9 +31,8 @@ void MenuConfig::Init() { separator_height = 13; separator_upper_height = 7; separator_lower_height = 6; - separator_thickness = base::mac::IsOSMavericks() - ? kMenuSeparatorHeightMavericks - : kMenuSeparatorHeight; + separator_thickness = base::mac::IsOS10_9() ? kMenuSeparatorHeightMavericks + : kMenuSeparatorHeight; align_arrow_and_shortcut = true; icons_in_label = true; check_selected_combobox_item = true; diff --git a/chromium/ui/views/controls/menu/menu_controller.cc b/chromium/ui/views/controls/menu/menu_controller.cc index 13d3d3510a4..7281b18a38c 100644 --- a/chromium/ui/views/controls/menu/menu_controller.cc +++ b/chromium/ui/views/controls/menu/menu_controller.cc @@ -46,7 +46,7 @@ #endif #if defined(USE_AURA) -#include "ui/views/controls/menu/menu_key_event_handler.h" +#include "ui/views/controls/menu/menu_pre_target_handler.h" #endif using base::Time; @@ -420,7 +420,8 @@ MenuItemView* MenuController::Run(Widget* parent, } } - bool nested_menu = showing_; + // If we are already showing, this new menu is being nested. Such as context + // menus on top of normal menus. if (showing_) { // Only support nesting of blocking_run menus, nesting of // blocking/non-blocking shouldn't be needed. @@ -437,10 +438,16 @@ MenuItemView* MenuController::Run(Widget* parent, } else { showing_ = true; + if (owner_) + owner_->RemoveObserver(this); + owner_ = parent; + if (owner_) + owner_->AddObserver(this); + #if defined(USE_AURA) - // Only create a MenuKeyEventHandler for non-nested menus. Nested menus will - // use the existing one. - key_event_handler_.reset(new MenuKeyEventHandler); + // Only create a MenuPreTargetHandler for non-nested menus. Nested menus + // will use the existing one. + menu_pre_target_handler_.reset(new MenuPreTargetHandler(this, owner_)); #endif } @@ -449,12 +456,6 @@ MenuItemView* MenuController::Run(Widget* parent, state_ = State(); UpdateInitialLocation(bounds, position, context_menu); - if (owner_) - owner_->RemoveObserver(this); - owner_ = parent; - if (owner_) - owner_->AddObserver(this); - // Set the selection, which opens the initial menu. SetSelection(root, SELECTION_OPEN_SUBMENU | SELECTION_UPDATE_IMMEDIATELY); @@ -483,7 +484,7 @@ MenuItemView* MenuController::Run(Widget* parent, // appears totally broken. message_loop_depth_++; DCHECK_LE(message_loop_depth_, 2); - RunMessageLoop(nested_menu); + RunMessageLoop(); message_loop_depth_--; if (ViewsDelegate::GetInstance()) @@ -531,7 +532,19 @@ void MenuController::Cancel(ExitType type) { // WARNING: the call to MenuClosed deletes us. return; } - ExitAsyncRun(); + + // On Windows and Linux the destruction of this menu's Widget leads to the + // teardown of the platform specific drag-and-drop Widget. Do not shutdown + // while dragging, leave the Widget hidden until drag-and-drop has completed, + // at which point all menus will be destroyed. + // + // If |type| is EXIT_ALL we update the state of the menu to not showing. So + // that during the completion of a drag we are not incorrectly reporting the + // visual state. + if (!drag_in_progress_) + ExitAsyncRun(); + else if (type == EXIT_ALL) + showing_ = false; } void MenuController::AddNestedDelegate( @@ -1014,11 +1027,23 @@ void MenuController::OnDragComplete(bool should_close) { // the event target. current_mouse_pressed_state_ = 0; current_mouse_event_target_ = nullptr; - if (showing_ && should_close && GetActiveInstance() == this) { - CloseAllNestedMenus(); - Cancel(EXIT_ALL); - } else if (async_run_) { - ExitAsyncRun(); + + // Only attempt to close if the MenuHost said to. + if (should_close) { + if (showing_) { + // Close showing widgets. + if (GetActiveInstance() == this) { + CloseAllNestedMenus(); + Cancel(EXIT_ALL); + } + // The above may have deleted us. If not perform a full shutdown. + if (GetActiveInstance() == this) + ExitAsyncRun(); + } else if (exit_type_ == EXIT_ALL) { + // We may have been canceled during the drag. If so we still need to fully + // shutdown. + ExitAsyncRun(); + } } } @@ -1086,7 +1111,6 @@ void MenuController::OnWidgetDestroying(Widget* widget) { DCHECK_EQ(owner_, widget); owner_->RemoveObserver(this); owner_ = NULL; - message_loop_->ClearOwner(); } bool MenuController::IsCancelAllTimerRunningForTest() { @@ -1235,7 +1259,7 @@ void MenuController::StartDrag(SubmenuView* source, ui::DragDropTypes::DRAG_EVENT_SOURCE_MOUSE); // MenuController may have been deleted if |async_run_| so check for an active // instance before accessing member variables. - if (GetActiveInstance()) + if (GetActiveInstance() == this) did_initiate_drag_ = false; } @@ -1387,8 +1411,8 @@ MenuController::~MenuController() { StopCancelAllTimer(); } -void MenuController::RunMessageLoop(bool nested_menu) { - message_loop_->Run(this, owner_, nested_menu); +void MenuController::RunMessageLoop() { + message_loop_->Run(); } bool MenuController::SendAcceleratorToHotTrackedView() { @@ -1398,8 +1422,11 @@ bool MenuController::SendAcceleratorToHotTrackedView() { ui::Accelerator accelerator(ui::VKEY_RETURN, ui::EF_NONE); hot_view->AcceleratorPressed(accelerator); - CustomButton* button = static_cast<CustomButton*>(hot_view); - SetHotTrackedButton(button); + // An accelerator may have canceled the menu after activation. + if (GetActiveInstance()) { + CustomButton* button = static_cast<CustomButton*>(hot_view); + SetHotTrackedButton(button); + } return true; } @@ -2610,7 +2637,7 @@ MenuItemView* MenuController::ExitMenuRun() { } } else { #if defined(USE_AURA) - key_event_handler_.reset(); + menu_pre_target_handler_.reset(); #endif showing_ = false; diff --git a/chromium/ui/views/controls/menu/menu_controller.h b/chromium/ui/views/controls/menu/menu_controller.h index 5e34416f838..0434683e8b5 100644 --- a/chromium/ui/views/controls/menu/menu_controller.h +++ b/chromium/ui/views/controls/menu/menu_controller.h @@ -40,7 +40,7 @@ class SubmenuView; class View; #if defined(USE_AURA) -class MenuKeyEventHandler; +class MenuPreTargetHandler; #endif namespace internal { @@ -195,7 +195,6 @@ class VIEWS_EXPORT MenuController : public WidgetObserver { friend class internal::MenuRunnerImpl; friend class test::MenuControllerTest; friend class test::MenuControllerTestApi; - friend class MenuKeyEventHandler; friend class MenuHostRootView; friend class MenuItemView; friend class SubmenuView; @@ -318,10 +317,8 @@ class VIEWS_EXPORT MenuController : public WidgetObserver { ~MenuController() override; - // Runs the platform specific bits of the message loop. If |nested_menu| is - // true we're being asked to run a menu from within a menu (eg a context - // menu). - void RunMessageLoop(bool nested_menu); + // Runs the platform specific bits of the message loop. + void RunMessageLoop(); // Invokes AcceleratorPressed() on the hot tracked view if there is one. // Returns true if AcceleratorPressed() was invoked. @@ -700,7 +697,7 @@ class VIEWS_EXPORT MenuController : public WidgetObserver { std::unique_ptr<MenuMessageLoop> message_loop_; #if defined(USE_AURA) - std::unique_ptr<MenuKeyEventHandler> key_event_handler_; + std::unique_ptr<MenuPreTargetHandler> menu_pre_target_handler_; #endif DISALLOW_COPY_AND_ASSIGN(MenuController); diff --git a/chromium/ui/views/controls/menu/menu_controller_unittest.cc b/chromium/ui/views/controls/menu/menu_controller_unittest.cc index 42a2a252e08..cc2e943705b 100644 --- a/chromium/ui/views/controls/menu/menu_controller_unittest.cc +++ b/chromium/ui/views/controls/menu/menu_controller_unittest.cc @@ -5,9 +5,11 @@ #include "ui/views/controls/menu/menu_controller.h" #include "base/callback.h" +#include "base/logging.h" #include "base/macros.h" #include "base/single_thread_task_runner.h" #include "base/strings/utf_string_conversions.h" +#include "base/threading/thread_task_runner_handle.h" #include "build/build_config.h" #include "ui/aura/scoped_window_targeter.h" #include "ui/aura/window.h" @@ -21,6 +23,7 @@ #include "ui/gfx/geometry/rect.h" #include "ui/views/controls/menu/menu_controller_delegate.h" #include "ui/views/controls/menu/menu_delegate.h" +#include "ui/views/controls/menu/menu_host.h" #include "ui/views/controls/menu/menu_item_view.h" #include "ui/views/controls/menu/menu_message_loop.h" #include "ui/views/controls/menu/menu_scroll_view_container.h" @@ -31,7 +34,8 @@ #if defined(USE_AURA) #include "ui/aura/scoped_window_targeter.h" #include "ui/aura/window.h" -#include "ui/views/controls/menu/menu_key_event_handler.h" +#include "ui/views/controls/menu/menu_pre_target_handler.h" +#include "ui/wm/public/drag_drop_client.h" #endif #if defined(USE_X11) @@ -160,10 +164,7 @@ class TestMenuMessageLoop : public MenuMessageLoop { private: // MenuMessageLoop: - void Run(MenuController* controller, - Widget* owner, - bool nested_menu) override; - void ClearOwner() override; + void Run() override; std::unique_ptr<MenuMessageLoop> original_; bool is_running_; @@ -179,11 +180,9 @@ TestMenuMessageLoop::TestMenuMessageLoop( TestMenuMessageLoop::~TestMenuMessageLoop() {} -void TestMenuMessageLoop::Run(MenuController* controller, - Widget* owner, - bool nested_menu) { +void TestMenuMessageLoop::Run() { is_running_ = true; - original_->Run(controller, owner, nested_menu); + original_->Run(); } void TestMenuMessageLoop::QuitNow() { @@ -191,10 +190,61 @@ void TestMenuMessageLoop::QuitNow() { original_->QuitNow(); } -void TestMenuMessageLoop::ClearOwner() { - original_->ClearOwner(); +#if defined(USE_AURA) +// A DragDropClient which does not trigger a nested message loop. Instead a +// callback is triggered during StartDragAndDrop in order to allow testing. +class TestDragDropClient : public aura::client::DragDropClient { + public: + explicit TestDragDropClient(const base::Closure& callback) + : start_drag_and_drop_callback_(callback), drag_in_progress_(false) {} + ~TestDragDropClient() override {} + + // aura::client::DragDropClient: + int StartDragAndDrop(const ui::OSExchangeData& data, + aura::Window* root_window, + aura::Window* source_window, + const gfx::Point& screen_location, + int operation, + ui::DragDropTypes::DragEventSource source) override; + void DragUpdate(aura::Window* target, const ui::LocatedEvent& event) override; + void Drop(aura::Window* target, const ui::LocatedEvent& event) override; + void DragCancel() override; + bool IsDragDropInProgress() override; + + private: + base::Closure start_drag_and_drop_callback_; + bool drag_in_progress_; + + DISALLOW_COPY_AND_ASSIGN(TestDragDropClient); +}; + +int TestDragDropClient::StartDragAndDrop( + const ui::OSExchangeData& data, + aura::Window* root_window, + aura::Window* source_window, + const gfx::Point& screen_location, + int operation, + ui::DragDropTypes::DragEventSource source) { + drag_in_progress_ = true; + start_drag_and_drop_callback_.Run(); + return 0; } +void TestDragDropClient::DragUpdate(aura::Window* target, + const ui::LocatedEvent& event) {} +void TestDragDropClient::Drop(aura::Window* target, + const ui::LocatedEvent& event) { + drag_in_progress_ = false; +} +void TestDragDropClient::DragCancel() { + drag_in_progress_ = false; +} +bool TestDragDropClient::IsDragDropInProgress() { + return drag_in_progress_; +} + +#endif // defined(USE_AURA) + } // namespace class TestMenuItemViewShown : public MenuItemView { @@ -223,6 +273,7 @@ class MenuControllerTest : public ViewsTestBase { void SetUp() override { ViewsTestBase::SetUp(); Init(); + ASSERT_TRUE(base::MessageLoopForUI::IsCurrent()); } void TearDown() override { @@ -257,6 +308,31 @@ class MenuControllerTest : public ViewsTestBase { } #endif // defined(OS_LINUX) && defined(USE_X11) +#if defined(USE_AURA) + // Verifies that an open menu receives a cancel event, and closes. + void TestCancelEvent() { + EXPECT_EQ(MenuController::EXIT_NONE, menu_controller_->exit_type()); + ui::CancelModeEvent cancel_event; + event_generator_->Dispatch(&cancel_event); + EXPECT_EQ(MenuController::EXIT_ALL, menu_controller_->exit_type()); + } +#endif // defined(USE_AURA) + + // Verifies the state of the |menu_controller_| before destroying it. + void VerifyDragCompleteThenDestroy() { + EXPECT_FALSE(menu_controller()->drag_in_progress()); + EXPECT_EQ(MenuController::EXIT_ALL, menu_controller()->exit_type()); + DestroyMenuController(); + } + + // Setups |menu_controller_delegate_| to be destroyed when OnMenuClosed is + // called. + void TestDragCompleteThenDestroyOnMenuClosed() { + menu_controller_delegate_->set_on_menu_closed_callback( + base::Bind(&MenuControllerTest::VerifyDragCompleteThenDestroy, + base::Unretained(this))); + } + void TestAsynchronousNestedExitAll() { ASSERT_TRUE(test_message_loop_->is_running()); @@ -317,6 +393,23 @@ class MenuControllerTest : public ViewsTestBase { test_message_loop_->QuitNow(); } + // Tests destroying the active |menu_controller_| and replacing it with a new + // active instance. + void TestMenuControllerReplacementDuringDrag() { + DestroyMenuController(); + menu_item()->GetSubmenu()->Close(); + menu_controller_ = + new MenuController(true, menu_controller_delegate_.get()); + menu_controller_->owner_ = owner_.get(); + menu_controller_->showing_ = true; + } + + // Tests that the menu does not destroy itself when canceled during a drag. + void TestCancelAllDuringDrag() { + menu_controller_->CancelAll(); + EXPECT_EQ(0, menu_controller_delegate_->on_menu_closed_called()); + } + protected: void SetPendingStateItem(MenuItemView* item) { menu_controller_->pending_state_.item = item; @@ -378,6 +471,12 @@ class MenuControllerTest : public ViewsTestBase { bool IsShowing() { return menu_controller_->showing_; } + MenuHost* GetMenuHost(SubmenuView* submenu) { return submenu->host_; } + + void MenuHostOnDragWillStart(MenuHost* host) { host->OnDragWillStart(); } + + void MenuHostOnDragComplete(MenuHost* host) { host->OnDragComplete(); } + void SelectByChar(base::char16 character) { menu_controller_->SelectByChar(character); } @@ -404,12 +503,12 @@ class MenuControllerTest : public ViewsTestBase { void RunMenu() { #if defined(USE_AURA) - std::unique_ptr<MenuKeyEventHandler> key_event_handler( - new MenuKeyEventHandler); + std::unique_ptr<MenuPreTargetHandler> menu_pre_target_handler( + new MenuPreTargetHandler(menu_controller_, owner_.get())); #endif menu_controller_->message_loop_depth_++; - menu_controller_->RunMessageLoop(false); + menu_controller_->RunMessageLoop(); menu_controller_->message_loop_depth_--; } @@ -423,6 +522,15 @@ class MenuControllerTest : public ViewsTestBase { menu_controller_->message_loop_.reset(test_message_loop_); } + // Causes the |menu_controller_| to begin dragging. Use TestDragDropClient to + // avoid nesting message loops. + void StartDrag() { + const gfx::Point location; + menu_controller_->state_.item = menu_item()->GetSubmenu()->GetMenuItemAt(0); + menu_controller_->StartDrag( + menu_item()->GetSubmenu()->GetMenuItemAt(0)->CreateSubmenu(), location); + } + Widget* owner() { return owner_.get(); } ui::test::EventGenerator* event_generator() { return event_generator_.get(); } TestMenuItemViewShown* menu_item() { return menu_item_.get(); } @@ -451,6 +559,8 @@ class MenuControllerTest : public ViewsTestBase { menu_item()->GetSubmenu()->ShowAt(owner(), menu_item()->bounds(), false); } + void DestroyMenuItem() { menu_item_.reset(); } + CustomButton* GetHotButton() { return menu_controller_->hot_button_; } @@ -526,10 +636,9 @@ class MenuControllerTest : public ViewsTestBase { // Tests that an event targeter which blocks events will be honored by the menu // event dispatcher. TEST_F(MenuControllerTest, EventTargeter) { - base::MessageLoopForUI::current()->PostTask( - FROM_HERE, - base::Bind(&MenuControllerTest::TestEventTargeter, - base::Unretained(this))); + base::ThreadTaskRunnerHandle::Get()->PostTask( + FROM_HERE, base::Bind(&MenuControllerTest::TestEventTargeter, + base::Unretained(this))); RunMenu(); } #endif // defined(OS_LINUX) && defined(USE_X11) @@ -550,17 +659,13 @@ TEST_F(MenuControllerTest, TouchIdsReleasedCorrectly) { event_generator()->PressTouchId(1); event_generator()->ReleaseTouchId(0); - base::MessageLoopForUI::current()->PostTask( - FROM_HERE, - base::Bind(&MenuControllerTest::ReleaseTouchId, - base::Unretained(this), - 1)); + base::ThreadTaskRunnerHandle::Get()->PostTask( + FROM_HERE, base::Bind(&MenuControllerTest::ReleaseTouchId, + base::Unretained(this), 1)); - base::MessageLoopForUI::current()->PostTask( - FROM_HERE, - base::Bind(&MenuControllerTest::PressKey, - base::Unretained(this), - ui::VKEY_ESCAPE)); + base::ThreadTaskRunnerHandle::Get()->PostTask( + FROM_HERE, base::Bind(&MenuControllerTest::PressKey, + base::Unretained(this), ui::VKEY_ESCAPE)); RunMenu(); @@ -1006,17 +1111,49 @@ TEST_F(MenuControllerTest, AsynchronousPerformDrop) { TEST_F(MenuControllerTest, AsynchronousDragComplete) { MenuController* controller = menu_controller(); controller->SetAsyncRun(true); + TestDragCompleteThenDestroyOnMenuClosed(); controller->OnDragWillStart(); controller->OnDragComplete(true); - EXPECT_FALSE(controller->drag_in_progress()); TestMenuControllerDelegate* controller_delegate = menu_controller_delegate(); EXPECT_EQ(1, controller_delegate->on_menu_closed_called()); EXPECT_EQ(nullptr, controller_delegate->on_menu_closed_menu()); EXPECT_EQ(internal::MenuControllerDelegate::NOTIFY_DELEGATE, controller_delegate->on_menu_closed_notify_type()); - EXPECT_EQ(MenuController::EXIT_ALL, controller->exit_type()); +} + +// Tests that if Cancel is called during a drag, that OnMenuClosed is still +// notified when the drag completes. +TEST_F(MenuControllerTest, AsynchronousCancelDuringDrag) { + MenuController* controller = menu_controller(); + controller->SetAsyncRun(true); + TestDragCompleteThenDestroyOnMenuClosed(); + + controller->OnDragWillStart(); + controller->CancelAll(); + controller->OnDragComplete(true); + + TestMenuControllerDelegate* controller_delegate = menu_controller_delegate(); + EXPECT_EQ(1, controller_delegate->on_menu_closed_called()); + EXPECT_EQ(nullptr, controller_delegate->on_menu_closed_menu()); + EXPECT_EQ(internal::MenuControllerDelegate::NOTIFY_DELEGATE, + controller_delegate->on_menu_closed_notify_type()); +} + +// Tests that if a menu is destroyed while drag operations are occuring, that +// the MenuHost does not crash as the drag completes. +TEST_F(MenuControllerTest, AsynchronousDragHostDeleted) { + MenuController* controller = menu_controller(); + controller->SetAsyncRun(true); + + SubmenuView* submenu = menu_item()->GetSubmenu(); + submenu->ShowAt(owner(), menu_item()->bounds(), false); + MenuHost* host = GetMenuHost(submenu); + MenuHostOnDragWillStart(host); + submenu->Close(); + DestroyMenuItem(); + MenuHostOnDragComplete(host); } // Tets that an asynchronous menu nested within an asynchronous menu closes both @@ -1125,7 +1262,7 @@ TEST_F(MenuControllerTest, AsynchronousTouchEventRepostEvent) { TEST_F(MenuControllerTest, AsynchronousNestedExitAll) { InstallTestMenuMessageLoop(); - base::MessageLoopForUI::current()->task_runner()->PostTask( + base::ThreadTaskRunnerHandle::Get()->PostTask( FROM_HERE, base::Bind(&MenuControllerTest::TestAsynchronousNestedExitAll, base::Unretained(this))); @@ -1138,7 +1275,7 @@ TEST_F(MenuControllerTest, AsynchronousNestedExitAll) { TEST_F(MenuControllerTest, AsynchronousNestedExitOutermost) { InstallTestMenuMessageLoop(); - base::MessageLoopForUI::current()->task_runner()->PostTask( + base::ThreadTaskRunnerHandle::Get()->PostTask( FROM_HERE, base::Bind(&MenuControllerTest::TestAsynchronousNestedExitOutermost, base::Unretained(this))); @@ -1237,7 +1374,7 @@ TEST_F(MenuControllerTest, NestedMessageLoopDiesWithNestedMenu) { std::unique_ptr<TestMenuControllerDelegate> nested_delegate( new TestMenuControllerDelegate()); // This will nest an asynchronous menu, and then kill the nested message loop. - base::MessageLoopForUI::current()->task_runner()->PostTask( + base::ThreadTaskRunnerHandle::Get()->PostTask( FROM_HERE, base::Bind(&MenuControllerTest::TestNestedMessageLoopKillsItself, base::Unretained(this), nested_delegate.get())); @@ -1251,5 +1388,89 @@ TEST_F(MenuControllerTest, NestedMessageLoopDiesWithNestedMenu) { EXPECT_TRUE(nested_delegate->on_menu_closed_called()); } +#if defined(USE_AURA) +// Tests that when a synchronous menu receives a cancel event, that it closes. +TEST_F(MenuControllerTest, SynchronousCancelEvent) { + ExitMenuRun(); + // Post actual test to run once the menu has created a nested message loop. + base::ThreadTaskRunnerHandle::Get()->PostTask( + FROM_HERE, + base::Bind(&MenuControllerTest::TestCancelEvent, base::Unretained(this))); + int mouse_event_flags = 0; + MenuItemView* run_result = menu_controller()->Run( + owner(), nullptr, menu_item(), gfx::Rect(), MENU_ANCHOR_TOPLEFT, false, + false, &mouse_event_flags); + EXPECT_EQ(run_result, nullptr); +} + +// Tests that when an asynchronous menu receives a cancel event, that it closes. +TEST_F(MenuControllerTest, AsynchronousCancelEvent) { + ExitMenuRun(); + MenuController* controller = menu_controller(); + controller->SetAsyncRun(true); + + int mouse_event_flags = 0; + MenuItemView* run_result = + controller->Run(owner(), nullptr, menu_item(), gfx::Rect(), + MENU_ANCHOR_TOPLEFT, false, false, &mouse_event_flags); + EXPECT_EQ(run_result, nullptr); + TestCancelEvent(); +} + +// Tests that if a menu is ran without a widget, that MenuPreTargetHandler does +// not cause a crash. +TEST_F(MenuControllerTest, RunWithoutWidgetDoesntCrash) { + ExitMenuRun(); + MenuController* controller = menu_controller(); + controller->SetAsyncRun(true); + int mouse_event_flags = 0; + MenuItemView* run_result = + controller->Run(nullptr, nullptr, menu_item(), gfx::Rect(), + MENU_ANCHOR_TOPLEFT, false, false, &mouse_event_flags); + EXPECT_EQ(run_result, nullptr); +} + +// Tests that if a MenuController is destroying during drag/drop, and another +// MenuController becomes active, that the exiting of drag does not cause a +// crash. +TEST_F(MenuControllerTest, MenuControllerReplacedDuringDrag) { + // This test creates two native widgets, but expects the child native widget + // to be able to reach up and use the parent native widget's aura + // objects. https://crbug.com/614037 + if (IsMus()) + return; + + TestDragDropClient drag_drop_client( + base::Bind(&MenuControllerTest::TestMenuControllerReplacementDuringDrag, + base::Unretained(this))); + aura::client::SetDragDropClient(owner()->GetNativeWindow()->GetRootWindow(), + &drag_drop_client); + AddButtonMenuItems(); + StartDrag(); +} + +// Tests that if a CancelAll is called during drag-and-drop that it does not +// destroy the MenuController. On Windows and Linux this destruction also +// destroys the Widget used for drag-and-drop, thereby ending the drag. +TEST_F(MenuControllerTest, CancelAllDuringDrag) { + // This test creates two native widgets, but expects the child native widget + // to be able to reach up and use the parent native widget's aura + // objects. https://crbug.com/614037 + if (IsMus()) + return; + + MenuController* controller = menu_controller(); + controller->SetAsyncRun(true); + + TestDragDropClient drag_drop_client(base::Bind( + &MenuControllerTest::TestCancelAllDuringDrag, base::Unretained(this))); + aura::client::SetDragDropClient(owner()->GetNativeWindow()->GetRootWindow(), + &drag_drop_client); + AddButtonMenuItems(); + StartDrag(); +} + +#endif // defined(USE_AURA) + } // namespace test } // namespace views diff --git a/chromium/ui/views/controls/menu/menu_delegate.cc b/chromium/ui/views/controls/menu/menu_delegate.cc index 2e3b838ae5f..42ad8d6b735 100644 --- a/chromium/ui/views/controls/menu/menu_delegate.cc +++ b/chromium/ui/views/controls/menu/menu_delegate.cc @@ -23,8 +23,7 @@ const gfx::FontList* MenuDelegate::GetLabelFontList(int id) const { return NULL; } -bool MenuDelegate::GetShouldUseDisabledEmphasizedForegroundColor( - int command_id) const { +bool MenuDelegate::GetShouldUseNormalForegroundColor(int command_id) const { return false; } diff --git a/chromium/ui/views/controls/menu/menu_delegate.h b/chromium/ui/views/controls/menu/menu_delegate.h index cca690f2431..3b7cb7f6241 100644 --- a/chromium/ui/views/controls/menu/menu_delegate.h +++ b/chromium/ui/views/controls/menu/menu_delegate.h @@ -73,9 +73,9 @@ class VIEWS_EXPORT MenuDelegate { // The font for the menu item label. virtual const gfx::FontList* GetLabelFontList(int id) const; - // Whether this item should be displayed with a bolder color when disabled. - virtual bool GetShouldUseDisabledEmphasizedForegroundColor( - int command_id) const; + // Whether this item should be displayed with the normal text color, even if + // it's disabled. + virtual bool GetShouldUseNormalForegroundColor(int command_id) const; // Override the text color of a given menu item dependent on the // |command_id| and its |is_hovered| state. Returns true if it chooses to diff --git a/chromium/ui/views/controls/menu/menu_host.cc b/chromium/ui/views/controls/menu/menu_host.cc index d07d1efcc75..c6750a6e0cd 100644 --- a/chromium/ui/views/controls/menu/menu_host.cc +++ b/chromium/ui/views/controls/menu/menu_host.cc @@ -219,9 +219,13 @@ void MenuHost::OnDragWillStart() { } void MenuHost::OnDragComplete() { + // If we are being destroyed there is no guarantee that the menu items are + // available. + if (destroying_) + return; MenuController* menu_controller = submenu_->GetMenuItem()->GetMenuController(); - if (destroying_ || !menu_controller) + if (!menu_controller) return; bool should_close = true; diff --git a/chromium/ui/views/controls/menu/menu_host.h b/chromium/ui/views/controls/menu/menu_host.h index 1de12373563..06736d3d9fa 100644 --- a/chromium/ui/views/controls/menu/menu_host.h +++ b/chromium/ui/views/controls/menu/menu_host.h @@ -26,6 +26,10 @@ class PreMenuEventDispatchHandler; } // internal +namespace test { +class MenuControllerTest; +} // test + // SubmenuView uses a MenuHost to house the SubmenuView. // // SubmenuView owns the MenuHost. When SubmenuView is done with the MenuHost @@ -65,6 +69,8 @@ class MenuHost : public Widget { void ReleaseMenuHostCapture(); private: + friend class test::MenuControllerTest; + // Overridden from Widget: internal::RootView* CreateRootView() override; void OnMouseCaptureLost() override; diff --git a/chromium/ui/views/controls/menu/menu_item_view.cc b/chromium/ui/views/controls/menu/menu_item_view.cc index 8e3013b1197..73fc3039650 100644 --- a/chromium/ui/views/controls/menu/menu_item_view.cc +++ b/chromium/ui/views/controls/menu/menu_item_view.cc @@ -12,7 +12,6 @@ #include "base/strings/utf_string_conversions.h" #include "ui/accessibility/ax_view_state.h" #include "ui/base/l10n/l10n_util.h" -#include "ui/base/material_design/material_design_controller.h" #include "ui/base/models/menu_model.h" #include "ui/gfx/canvas.h" #include "ui/gfx/color_utils.h" @@ -526,7 +525,7 @@ void MenuItemView::ChildrenChanged() { } } - STLDeleteElements(&removed_items_); + base::STLDeleteElements(&removed_items_); } void MenuItemView::Layout() { @@ -599,7 +598,7 @@ MenuItemView::MenuItemView(MenuItemView* parent, MenuItemView::~MenuItemView() { delete submenu_; - STLDeleteElements(&removed_items_); + base::STLDeleteElements(&removed_items_); } const char* MenuItemView::GetClassName() const { @@ -784,12 +783,11 @@ void MenuItemView::PaintButton(gfx::Canvas* canvas, PaintButtonMode mode) { ui::NativeTheme::kColorId_SelectedMenuItemForegroundColor: ui::NativeTheme::kColorId_EnabledMenuItemForegroundColor; } else { - bool emphasized = delegate && - delegate->GetShouldUseDisabledEmphasizedForegroundColor( - GetCommand()); - color_id = emphasized ? - ui::NativeTheme::kColorId_DisabledEmphasizedMenuItemForegroundColor : - ui::NativeTheme::kColorId_DisabledMenuItemForegroundColor; + bool emphasized = + delegate && delegate->GetShouldUseNormalForegroundColor(GetCommand()); + color_id = emphasized + ? ui::NativeTheme::kColorId_EnabledMenuItemForegroundColor + : ui::NativeTheme::kColorId_DisabledMenuItemForegroundColor; } SkColor fg_color = native_theme->GetSystemColor(color_id); SkColor override_foreground_color; @@ -798,10 +796,7 @@ void MenuItemView::PaintButton(gfx::Canvas* canvas, PaintButtonMode mode) { &override_foreground_color)) { fg_color = override_foreground_color; } - SkColor icon_color = - render_selection && !ui::MaterialDesignController::IsModeMaterial() - ? fg_color - : color_utils::DeriveDefaultIconColor(fg_color); + SkColor icon_color = color_utils::DeriveDefaultIconColor(fg_color); // Render the check. if (type_ == CHECKBOX && delegate->IsItemChecked(GetCommand())) { diff --git a/chromium/ui/views/controls/menu/menu_key_event_handler.cc b/chromium/ui/views/controls/menu/menu_key_event_handler.cc deleted file mode 100644 index a0073f90395..00000000000 --- a/chromium/ui/views/controls/menu/menu_key_event_handler.cc +++ /dev/null @@ -1,27 +0,0 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "ui/views/controls/menu/menu_key_event_handler.h" - -#include "ui/aura/env.h" -#include "ui/views/controls/menu/menu_controller.h" -#include "ui/views/views_delegate.h" - -namespace views { - -MenuKeyEventHandler::MenuKeyEventHandler() { - aura::Env::GetInstanceDontCreate()->PrependPreTargetHandler(this); -} - -MenuKeyEventHandler::~MenuKeyEventHandler() { - aura::Env::GetInstanceDontCreate()->RemovePreTargetHandler(this); -} - -void MenuKeyEventHandler::OnKeyEvent(ui::KeyEvent* event) { - MenuController* menu_controller = MenuController::GetActiveInstance(); - DCHECK(menu_controller); - menu_controller->OnWillDispatchKeyEvent(event); -} - -} // namespace views diff --git a/chromium/ui/views/controls/menu/menu_key_event_handler.h b/chromium/ui/views/controls/menu/menu_key_event_handler.h deleted file mode 100644 index fd4fd036785..00000000000 --- a/chromium/ui/views/controls/menu/menu_key_event_handler.h +++ /dev/null @@ -1,33 +0,0 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef UI_VIEWS_CONTROLS_MENU_MENU_KEY_EVENT_HANDLER_H_ -#define UI_VIEWS_CONTROLS_MENU_MENU_KEY_EVENT_HANDLER_H_ - -#include "base/macros.h" -#include "ui/events/event_handler.h" -#include "ui/views/views_export.h" - -namespace ui { -class Accelerator; -} - -namespace views { - -// Handles key events while the menu is open. -class VIEWS_EXPORT MenuKeyEventHandler : public ui::EventHandler { - public: - MenuKeyEventHandler(); - ~MenuKeyEventHandler() override; - - // ui::EventHandler: - void OnKeyEvent(ui::KeyEvent* event) override; - - private: - DISALLOW_COPY_AND_ASSIGN(MenuKeyEventHandler); -}; - -} // namespace views - -#endif // UI_VIEWS_CONTROLS_MENU_MENU_KEY_EVENT_HANDLER_H_ diff --git a/chromium/ui/views/controls/menu/menu_message_loop.h b/chromium/ui/views/controls/menu/menu_message_loop.h index c6a02816524..e84b650b765 100644 --- a/chromium/ui/views/controls/menu/menu_message_loop.h +++ b/chromium/ui/views/controls/menu/menu_message_loop.h @@ -35,17 +35,11 @@ class MenuMessageLoop { gfx::NativeWindow window, const gfx::Point& screen_loc); - // Runs the platform specific bits of the message loop. If |nested_menu| is - // true we're being asked to run a menu from within a menu (eg a context - // menu). - virtual void Run(MenuController*, Widget* owner, bool nested_menu) = 0; + // Runs the platform specific bits of the message loop. + virtual void Run() = 0; // Quit an earlier call to Run(). virtual void QuitNow() = 0; - - // Clear any references to the owner widget that was passed into the previous - // call to Run(). - virtual void ClearOwner() = 0; }; } // namespace views diff --git a/chromium/ui/views/controls/menu/menu_message_loop_aura.cc b/chromium/ui/views/controls/menu/menu_message_loop_aura.cc index dab58be36c9..3bfdef98a46 100644 --- a/chromium/ui/views/controls/menu/menu_message_loop_aura.cc +++ b/chromium/ui/views/controls/menu/menu_message_loop_aura.cc @@ -15,7 +15,6 @@ #include "ui/events/platform/platform_event_source.h" #include "ui/events/platform/scoped_event_dispatcher.h" #include "ui/views/controls/menu/menu_controller.h" -#include "ui/views/controls/menu/menu_key_event_handler.h" #include "ui/views/widget/widget.h" #include "ui/wm/public/activation_change_observer.h" #include "ui/wm/public/activation_client.h" @@ -26,68 +25,6 @@ using aura::client::ScreenPositionClient; namespace views { -namespace { - -aura::Window* GetOwnerRootWindow(views::Widget* owner) { - return owner ? owner->GetNativeWindow()->GetRootWindow() : NULL; -} - -// ActivationChangeObserverImpl is used to observe activation changes and close -// the menu. Additionally it listens for the root window to be destroyed and -// cancel the menu as well. -class ActivationChangeObserverImpl - : public aura::client::ActivationChangeObserver, - public aura::WindowObserver, - public ui::EventHandler { - public: - ActivationChangeObserverImpl(MenuController* controller, aura::Window* root) - : controller_(controller), root_(root) { - aura::client::GetActivationClient(root_)->AddObserver(this); - root_->AddObserver(this); - root_->AddPreTargetHandler(this); - } - - ~ActivationChangeObserverImpl() override { Cleanup(); } - - // aura::client::ActivationChangeObserver: - void OnWindowActivated( - aura::client::ActivationChangeObserver::ActivationReason reason, - aura::Window* gained_active, - aura::Window* lost_active) override { - if (!controller_->drag_in_progress()) - controller_->CancelAll(); - } - - // aura::WindowObserver: - void OnWindowDestroying(aura::Window* window) override { Cleanup(); } - - // ui::EventHandler: - void OnCancelMode(ui::CancelModeEvent* event) override { - controller_->CancelAll(); - } - - private: - void Cleanup() { - if (!root_) - return; - // The ActivationClient may have been destroyed by the time we get here. - aura::client::ActivationClient* client = - aura::client::GetActivationClient(root_); - if (client) - client->RemoveObserver(this); - root_->RemovePreTargetHandler(this); - root_->RemoveObserver(this); - root_ = NULL; - } - - MenuController* controller_; - aura::Window* root_; - - DISALLOW_COPY_AND_ASSIGN(ActivationChangeObserverImpl); -}; - -} // namespace - // static MenuMessageLoop* MenuMessageLoop::Create() { return new MenuMessageLoopAura; @@ -115,16 +52,11 @@ void MenuMessageLoop::RepostEventToWindow(const ui::LocatedEvent* event, root->GetHost()->dispatcher()->RepostEvent(located_event.get()); } -MenuMessageLoopAura::MenuMessageLoopAura() : owner_(nullptr) {} +MenuMessageLoopAura::MenuMessageLoopAura() {} MenuMessageLoopAura::~MenuMessageLoopAura() {} -void MenuMessageLoopAura::Run(MenuController* controller, - Widget* owner, - bool nested_menu) { - // |owner_| may be NULL. - owner_ = owner; - aura::Window* root = GetOwnerRootWindow(owner_); +void MenuMessageLoopAura::Run() { // It is possible for the same MenuMessageLoopAura to start a nested // message-loop while it is already running a nested loop. So make sure the // quit-closure gets reset to the outer loop's quit-closure once the innermost @@ -132,12 +64,6 @@ void MenuMessageLoopAura::Run(MenuController* controller, base::AutoReset<base::Closure> reset_quit_closure(&message_loop_quit_, base::Closure()); - std::unique_ptr<ActivationChangeObserverImpl> observer; - if (root) { - if (!nested_menu) - observer.reset(new ActivationChangeObserverImpl(controller, root)); - } - base::MessageLoop* loop = base::MessageLoop::current(); base::MessageLoop::ScopedNestableTaskAllower allow(loop); base::RunLoop run_loop; @@ -158,8 +84,4 @@ void MenuMessageLoopAura::QuitNow() { #endif } -void MenuMessageLoopAura::ClearOwner() { - owner_ = NULL; -} - } // namespace views diff --git a/chromium/ui/views/controls/menu/menu_message_loop_aura.h b/chromium/ui/views/controls/menu/menu_message_loop_aura.h index 6ebe8393f80..e123380d134 100644 --- a/chromium/ui/views/controls/menu/menu_message_loop_aura.h +++ b/chromium/ui/views/controls/menu/menu_message_loop_aura.h @@ -23,18 +23,11 @@ class MenuMessageLoopAura : public MenuMessageLoop { MenuMessageLoopAura(); ~MenuMessageLoopAura() override; - // Overridden from MenuMessageLoop: - void Run(MenuController* controller, - Widget* owner, - bool nested_menu) override; + // MenuMessageLoop: + void Run() override; void QuitNow() override; - void ClearOwner() override; private: - // Owner of child windows. - // WARNING: this may be NULL. - Widget* owner_; - base::Closure message_loop_quit_; DISALLOW_COPY_AND_ASSIGN(MenuMessageLoopAura); diff --git a/chromium/ui/views/controls/menu/menu_message_loop_mac.cc b/chromium/ui/views/controls/menu/menu_message_loop_mac.cc index 6d3afb77acc..3d9bea95484 100644 --- a/chromium/ui/views/controls/menu/menu_message_loop_mac.cc +++ b/chromium/ui/views/controls/menu/menu_message_loop_mac.cc @@ -28,9 +28,7 @@ MenuMessageLoopMac::MenuMessageLoopMac() {} MenuMessageLoopMac::~MenuMessageLoopMac() {} -void MenuMessageLoopMac::Run(MenuController* controller, - Widget* owner, - bool nested_menu) { +void MenuMessageLoopMac::Run() { base::MessageLoopForUI* loop = base::MessageLoopForUI::current(); base::MessageLoop::ScopedNestableTaskAllower allow(loop); base::RunLoop run_loop; @@ -43,7 +41,4 @@ void MenuMessageLoopMac::QuitNow() { run_loop_->Quit(); } -void MenuMessageLoopMac::ClearOwner() { -} - } // namespace views diff --git a/chromium/ui/views/controls/menu/menu_message_loop_mac.h b/chromium/ui/views/controls/menu/menu_message_loop_mac.h index ed5cfdbf673..0f35a175784 100644 --- a/chromium/ui/views/controls/menu/menu_message_loop_mac.h +++ b/chromium/ui/views/controls/menu/menu_message_loop_mac.h @@ -20,12 +20,9 @@ class MenuMessageLoopMac : public MenuMessageLoop { MenuMessageLoopMac(); ~MenuMessageLoopMac() override; - // Overridden from MenuMessageLoop: - void Run(MenuController* controller, - Widget* owner, - bool nested_menu) override; + // MenuMessageLoop: + void Run() override; void QuitNow() override; - void ClearOwner() override; private: base::RunLoop* run_loop_ = nullptr; diff --git a/chromium/ui/views/controls/menu/menu_model_adapter.cc b/chromium/ui/views/controls/menu/menu_model_adapter.cc index b63f9866774..bc04dcb9e24 100644 --- a/chromium/ui/views/controls/menu/menu_model_adapter.cc +++ b/chromium/ui/views/controls/menu/menu_model_adapter.cc @@ -262,7 +262,7 @@ void MenuModelAdapter::WillHideMenu(MenuItemView* menu) { const std::map<MenuItemView*, ui::MenuModel*>::const_iterator map_iterator = menu_map_.find(menu); if (map_iterator != menu_map_.end()) { - map_iterator->second->MenuClosed(); + map_iterator->second->MenuWillClose(); return; } diff --git a/chromium/ui/views/controls/menu/menu_model_adapter_unittest.cc b/chromium/ui/views/controls/menu/menu_model_adapter_unittest.cc index 9fddfaf9317..c5df9547874 100644 --- a/chromium/ui/views/controls/menu/menu_model_adapter_unittest.cc +++ b/chromium/ui/views/controls/menu/menu_model_adapter_unittest.cc @@ -85,7 +85,7 @@ class MenuModelBase : public ui::MenuModel { void MenuWillShow() override {} - void MenuClosed() override {} + void MenuWillClose() override {} void SetMenuModelDelegate(ui::MenuModelDelegate* delegate) override {} diff --git a/chromium/ui/views/controls/menu/menu_pre_target_handler.cc b/chromium/ui/views/controls/menu/menu_pre_target_handler.cc new file mode 100644 index 00000000000..7ac7a3f5b47 --- /dev/null +++ b/chromium/ui/views/controls/menu/menu_pre_target_handler.cc @@ -0,0 +1,70 @@ +// Copyright 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "ui/views/controls/menu/menu_pre_target_handler.h" + +#include "ui/aura/env.h" +#include "ui/aura/window.h" +#include "ui/views/controls/menu/menu_controller.h" +#include "ui/views/widget/widget.h" +#include "ui/wm/public/activation_client.h" + +namespace views { + +namespace { + +aura::Window* GetOwnerRootWindow(views::Widget* owner) { + return owner ? owner->GetNativeWindow()->GetRootWindow() : nullptr; +} + +} // namespace + +MenuPreTargetHandler::MenuPreTargetHandler(MenuController* controller, + Widget* owner) + : controller_(controller), root_(GetOwnerRootWindow(owner)) { + aura::Env::GetInstanceDontCreate()->PrependPreTargetHandler(this); + if (root_) { + aura::client::GetActivationClient(root_)->AddObserver(this); + root_->AddObserver(this); + } +} + +MenuPreTargetHandler::~MenuPreTargetHandler() { + aura::Env::GetInstanceDontCreate()->RemovePreTargetHandler(this); + Cleanup(); +} + +void MenuPreTargetHandler::OnWindowActivated( + aura::client::ActivationChangeObserver::ActivationReason reason, + aura::Window* gained_active, + aura::Window* lost_active) { + if (!controller_->drag_in_progress()) + controller_->CancelAll(); +} + +void MenuPreTargetHandler::OnWindowDestroying(aura::Window* window) { + Cleanup(); +} + +void MenuPreTargetHandler::OnCancelMode(ui::CancelModeEvent* event) { + controller_->CancelAll(); +} + +void MenuPreTargetHandler::OnKeyEvent(ui::KeyEvent* event) { + controller_->OnWillDispatchKeyEvent(event); +} + +void MenuPreTargetHandler::Cleanup() { + if (!root_) + return; + // The ActivationClient may have been destroyed by the time we get here. + aura::client::ActivationClient* client = + aura::client::GetActivationClient(root_); + if (client) + client->RemoveObserver(this); + root_->RemoveObserver(this); + root_ = nullptr; +} + +} // namespace views diff --git a/chromium/ui/views/controls/menu/menu_pre_target_handler.h b/chromium/ui/views/controls/menu/menu_pre_target_handler.h new file mode 100644 index 00000000000..56bdd3596c7 --- /dev/null +++ b/chromium/ui/views/controls/menu/menu_pre_target_handler.h @@ -0,0 +1,58 @@ +// Copyright 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef UI_VIEWS_CONTROLS_MENU_PRE_TARGET_HANDLER_H_ +#define UI_VIEWS_CONTROLS_MENU_PRE_TARGET_HANDLER_H_ + +#include "ui/aura/window_observer.h" +#include "ui/events/event_handler.h" +#include "ui/views/views_export.h" +#include "ui/wm/public/activation_change_observer.h" + +namespace aura { +class Window; +} // namespace aura + +namespace views { + +class MenuController; +class Widget; + +// MenuPreTargetHandler is used to observe activation changes, cancel events, +// and root window destruction, to shutdown the menu. +// +// Additionally handles key events to provide accelerator support to menus. +class VIEWS_EXPORT MenuPreTargetHandler + : public aura::client::ActivationChangeObserver, + public aura::WindowObserver, + public ui::EventHandler { + public: + MenuPreTargetHandler(MenuController* controller, Widget* owner); + ~MenuPreTargetHandler() override; + + // aura::client:ActivationChangeObserver: + void OnWindowActivated( + aura::client::ActivationChangeObserver::ActivationReason reason, + aura::Window* gained_active, + aura::Window* lost_active) override; + + // aura::WindowObserver: + void OnWindowDestroying(aura::Window* window) override; + + // ui::EventHandler: + void OnCancelMode(ui::CancelModeEvent* event) override; + void OnKeyEvent(ui::KeyEvent* event) override; + + private: + void Cleanup(); + + MenuController* controller_; + aura::Window* root_; + + DISALLOW_COPY_AND_ASSIGN(MenuPreTargetHandler); +}; + +} // namespace views + +#endif // UI_VIEWS_CONTROLS_MENU_PRE_TARGET_HANDLER_H_ diff --git a/chromium/ui/views/controls/menu/menu_runner_cocoa_unittest.mm b/chromium/ui/views/controls/menu/menu_runner_cocoa_unittest.mm index 09449d24c86..924256ff346 100644 --- a/chromium/ui/views/controls/menu/menu_runner_cocoa_unittest.mm +++ b/chromium/ui/views/controls/menu/menu_runner_cocoa_unittest.mm @@ -35,10 +35,6 @@ class TestModel : public ui::SimpleMenuModel { return command_id == model_->checked_command_; } bool IsCommandIdEnabled(int command_id) const override { return true; } - bool GetAcceleratorForCommandId(int command_id, - ui::Accelerator* accelerator) override { - return false; - } void ExecuteCommand(int command_id, int event_flags) override {} void MenuWillShow(SimpleMenuModel* source) override { diff --git a/chromium/ui/views/controls/menu/menu_scroll_view_container.cc b/chromium/ui/views/controls/menu/menu_scroll_view_container.cc index 9350283fde1..a126820718b 100644 --- a/chromium/ui/views/controls/menu/menu_scroll_view_container.cc +++ b/chromium/ui/views/controls/menu/menu_scroll_view_container.cc @@ -283,10 +283,9 @@ void MenuScrollViewContainer::CreateDefaultBorder() { ? kBorderPaddingDueToRoundedCorners : 0; - int top = menu_config.menu_vertical_border_size + padding; - int left = menu_config.menu_horizontal_border_size + padding; - int bottom = menu_config.menu_vertical_border_size + padding; - int right = menu_config.menu_horizontal_border_size + padding; + const int vertical_inset = menu_config.menu_vertical_border_size + padding; + const int horizontal_inset = + menu_config.menu_horizontal_border_size + padding; if (menu_config.use_outer_border) { SkColor color = GetNativeTheme() @@ -294,11 +293,12 @@ void MenuScrollViewContainer::CreateDefaultBorder() { ui::NativeTheme::kColorId_MenuBorderColor) : gfx::kPlaceholderColor; SetBorder(views::Border::CreateBorderPainter( - base::WrapUnique( - new views::RoundRectPainter(color, menu_config.corner_radius)), - gfx::Insets(top, left, bottom, right))); + base::MakeUnique<views::RoundRectPainter>(color, + menu_config.corner_radius), + gfx::Insets(vertical_inset, horizontal_inset))); } else { - SetBorder(Border::CreateEmptyBorder(top, left, bottom, right)); + SetBorder(Border::CreateEmptyBorder(vertical_inset, horizontal_inset, + vertical_inset, horizontal_inset)); } } diff --git a/chromium/ui/views/controls/menu/native_menu_win.cc b/chromium/ui/views/controls/menu/native_menu_win.cc index e205fda62f9..b737cbf974a 100644 --- a/chromium/ui/views/controls/menu/native_menu_win.cc +++ b/chromium/ui/views/controls/menu/native_menu_win.cc @@ -57,7 +57,7 @@ NativeMenuWin::NativeMenuWin(ui::MenuModel* model, HWND system_menu_for) NativeMenuWin::~NativeMenuWin() { if (destroyed_flag_) *destroyed_flag_ = true; - STLDeleteContainerPointers(items_.begin(), items_.end()); + base::STLDeleteContainerPointers(items_.begin(), items_.end()); DestroyMenu(menu_); } diff --git a/chromium/ui/views/controls/menu/submenu_view.cc b/chromium/ui/views/controls/menu/submenu_view.cc index 1e2de11bd03..a04d83292a6 100644 --- a/chromium/ui/views/controls/menu/submenu_view.cc +++ b/chromium/ui/views/controls/menu/submenu_view.cc @@ -47,7 +47,7 @@ SubmenuView::SubmenuView(MenuItemView* parent) resize_open_menu_(false), scroll_animator_(new ScrollAnimator(this)), roundoff_error_(0), - prefix_selector_(this) { + prefix_selector_(this, this) { DCHECK(parent); // We'll delete ourselves, otherwise the ScrollView would delete us on close. set_owned_by_client(); diff --git a/chromium/ui/views/controls/menu/submenu_view.h b/chromium/ui/views/controls/menu/submenu_view.h index 1f929750bbc..2e00de54eb2 100644 --- a/chromium/ui/views/controls/menu/submenu_view.h +++ b/chromium/ui/views/controls/menu/submenu_view.h @@ -21,6 +21,10 @@ class MenuHost; class MenuItemView; class MenuScrollViewContainer; +namespace test { +class MenuControllerTest; +} // test + // SubmenuView is the parent of all menu items. // // SubmenuView has the following responsibilities: @@ -36,7 +40,8 @@ class MenuScrollViewContainer; // MenuScrollViewContainer handles showing as much of the SubmenuView as the // screen allows. If the SubmenuView is taller than the screen, scroll buttons // are provided that allow the user to see all the menu items. -class VIEWS_EXPORT SubmenuView : public PrefixDelegate, +class VIEWS_EXPORT SubmenuView : public View, + public PrefixDelegate, public ScrollDelegate { public: // The submenu's class name. @@ -168,6 +173,8 @@ class VIEWS_EXPORT SubmenuView : public PrefixDelegate, void ChildPreferredSizeChanged(View* child) override; private: + friend class test::MenuControllerTest; + void SchedulePaintForDropIndicator(MenuItemView* item, MenuDelegate::DropPosition position); diff --git a/chromium/ui/views/controls/message_box_view.h b/chromium/ui/views/controls/message_box_view.h index 2d83532c898..af6905dafab 100644 --- a/chromium/ui/views/controls/message_box_view.h +++ b/chromium/ui/views/controls/message_box_view.h @@ -63,6 +63,9 @@ class VIEWS_EXPORT MessageBoxView : public View { // Returns user entered data in the prompt field. base::string16 GetInputText(); + // Returns true if this message box has a visible checkbox, false otherwise. + bool HasCheckBox() const { return !!checkbox_; } + // Returns true if a checkbox is selected, false otherwise. (And false if // the message box has no checkbox.) bool IsCheckBoxSelected(); diff --git a/chromium/ui/views/controls/non_md_slider.cc b/chromium/ui/views/controls/non_md_slider.cc new file mode 100644 index 00000000000..1778ddc755e --- /dev/null +++ b/chromium/ui/views/controls/non_md_slider.cc @@ -0,0 +1,106 @@ +// Copyright 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "ui/views/controls/non_md_slider.h" + +#include "ui/base/resource/resource_bundle.h" +#include "ui/gfx/canvas.h" +#include "ui/gfx/geometry/point.h" +#include "ui/gfx/geometry/rect.h" +#include "ui/resources/grit/ui_resources.h" +#include "ui/views/controls/slider.h" +#include "ui/views/resources/grit/views_resources.h" + +namespace { +const int kBarImagesActive[] = { + IDR_SLIDER_ACTIVE_LEFT, IDR_SLIDER_ACTIVE_CENTER, IDR_SLIDER_PRESSED_CENTER, + IDR_SLIDER_PRESSED_RIGHT, +}; + +const int kBarImagesDisabled[] = { + IDR_SLIDER_DISABLED_LEFT, IDR_SLIDER_DISABLED_CENTER, + IDR_SLIDER_DISABLED_CENTER, IDR_SLIDER_DISABLED_RIGHT, +}; + +// The image chunks. +enum BorderElements { + LEFT, + CENTER_LEFT, + CENTER_RIGHT, + RIGHT, +}; +} // namespace + +namespace views { + +NonMdSlider::NonMdSlider(SliderListener* listener) + : Slider(listener), + bar_active_images_(kBarImagesActive), + bar_disabled_images_(kBarImagesDisabled) { + UpdateSliderAppearance(true); +} + +NonMdSlider::~NonMdSlider() {} + +void NonMdSlider::OnPaint(gfx::Canvas* canvas) { + Slider::OnPaint(canvas); + gfx::Rect content = GetContentsBounds(); + float value = GetAnimatingValue(); + + // Inset the slider bar a little bit, so that the left or the right end of + // the slider bar will not be exposed under the thumb button when the thumb + // button slides to the left most or right most position. + const int kBarInsetX = 2; + int bar_width = content.width() - kBarInsetX * 2; + int bar_cy = content.height() / 2 - bar_height_ / 2; + + int w = content.width() - thumb_->width(); + int full = value * w; + int middle = std::max(full, images_[LEFT]->width()); + + canvas->Save(); + canvas->Translate(gfx::Vector2d(kBarInsetX, bar_cy)); + canvas->DrawImageInt(*images_[LEFT], 0, 0); + canvas->DrawImageInt(*images_[RIGHT], bar_width - images_[RIGHT]->width(), 0); + canvas->TileImageInt(*images_[CENTER_LEFT], images_[LEFT]->width(), 0, + middle - images_[LEFT]->width(), bar_height_); + canvas->TileImageInt(*images_[CENTER_RIGHT], middle, 0, + bar_width - middle - images_[RIGHT]->width(), + bar_height_); + canvas->Restore(); + + // Paint slider thumb. + int button_cx = content.x() + full; + int thumb_y = content.height() / 2 - thumb_->height() / 2; + canvas->DrawImageInt(*thumb_, button_cx, thumb_y); +} + +const char* NonMdSlider::GetClassName() const { + return "NonMdSlider"; +} + +void NonMdSlider::UpdateState(bool control_on) { + UpdateSliderAppearance(control_on); +} + +void NonMdSlider::UpdateSliderAppearance(bool control_on) { + ResourceBundle& rb = ResourceBundle::GetSharedInstance(); + if (control_on) { + thumb_ = rb.GetImageNamed(IDR_SLIDER_ACTIVE_THUMB).ToImageSkia(); + for (int i = 0; i < 4; ++i) + images_[i] = rb.GetImageNamed(bar_active_images_[i]).ToImageSkia(); + } else { + thumb_ = rb.GetImageNamed(IDR_SLIDER_DISABLED_THUMB).ToImageSkia(); + for (int i = 0; i < 4; ++i) + images_[i] = rb.GetImageNamed(bar_disabled_images_[i]).ToImageSkia(); + } + bar_height_ = images_[LEFT]->height(); + SchedulePaint(); +} + +int NonMdSlider::GetThumbWidth() { + return thumb_->width(); +} + +} // namespace views diff --git a/chromium/ui/views/controls/non_md_slider.h b/chromium/ui/views/controls/non_md_slider.h new file mode 100644 index 00000000000..fa6bfc9ca07 --- /dev/null +++ b/chromium/ui/views/controls/non_md_slider.h @@ -0,0 +1,49 @@ +// Copyright 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef UI_VIEWS_CONTROLS_NON_MD_SLIDER_H_ +#define UI_VIEWS_CONTROLS_NON_MD_SLIDER_H_ + +#include "base/macros.h" +#include "ui/views/controls/slider.h" +#include "ui/views/view.h" +#include "ui/views/views_export.h" + +namespace views { +class Slider; + +class VIEWS_EXPORT NonMdSlider : public Slider { + public: + explicit NonMdSlider(SliderListener* listener); + ~NonMdSlider() override; + + // ui::Slider: + void UpdateState(bool control_on) override; + + // views::View: + void OnPaint(gfx::Canvas* canvas) override; + const char* GetClassName() const override; + + protected: + // ui::Slider: + int GetThumbWidth() override; + + private: + void UpdateSliderAppearance(bool control_on); + + // Array used to hold active slider states and disabled slider states images. + const int* bar_active_images_; + const int* bar_disabled_images_; + const gfx::ImageSkia* thumb_; + + // Array used to hold current state of slider images. + const gfx::ImageSkia* images_[4]; + int bar_height_; + + DISALLOW_COPY_AND_ASSIGN(NonMdSlider); +}; + +} // namespace views + +#endif // UI_VIEWS_CONTROLS_NON_MD_SLIDER_H_ diff --git a/chromium/ui/views/controls/prefix_delegate.h b/chromium/ui/views/controls/prefix_delegate.h index a6d27ebe7e0..cd3930a6da2 100644 --- a/chromium/ui/views/controls/prefix_delegate.h +++ b/chromium/ui/views/controls/prefix_delegate.h @@ -6,11 +6,12 @@ #define UI_VIEWS_CONTROLS_PREFIX_DELEGATE_H_ #include "ui/views/view.h" +#include "ui/views/views_export.h" namespace views { // An interface used to expose lists of items for selection by text input. -class VIEWS_EXPORT PrefixDelegate : public View { +class VIEWS_EXPORT PrefixDelegate { public: // Returns the total number of selectable items. virtual int GetRowCount() = 0; @@ -24,9 +25,6 @@ class VIEWS_EXPORT PrefixDelegate : public View { // Returns the item at the specified row. virtual base::string16 GetTextForRow(int row) = 0; - - protected: - ~PrefixDelegate() override {} }; } // namespace views diff --git a/chromium/ui/views/controls/prefix_selector.cc b/chromium/ui/views/controls/prefix_selector.cc index bd57addeaec..b21ff34e09e 100644 --- a/chromium/ui/views/controls/prefix_selector.cc +++ b/chromium/ui/views/controls/prefix_selector.cc @@ -9,6 +9,7 @@ #include "ui/base/ime/text_input_type.h" #include "ui/gfx/range/range.h" #include "ui/views/controls/prefix_delegate.h" +#include "ui/views/view.h" #include "ui/views/widget/widget.h" namespace views { @@ -27,8 +28,8 @@ void ConvertRectToScreen(const views::View* src, gfx::Rect* r) { } // namespace -PrefixSelector::PrefixSelector(PrefixDelegate* delegate) - : prefix_delegate_(delegate) { +PrefixSelector::PrefixSelector(PrefixDelegate* delegate, View* host_view) + : prefix_delegate_(delegate), host_view_(host_view) { } PrefixSelector::~PrefixSelector() { @@ -77,10 +78,10 @@ bool PrefixSelector::CanComposeInline() const { } gfx::Rect PrefixSelector::GetCaretBounds() const { - gfx::Rect rect(prefix_delegate_->GetVisibleBounds().origin(), gfx::Size()); + gfx::Rect rect(host_view_->GetVisibleBounds().origin(), gfx::Size()); // TextInputClient::GetCaretBounds is expected to return a value in screen // coordinates. - ConvertRectToScreen(prefix_delegate_, &rect); + ConvertRectToScreen(host_view_, &rect); return rect; } diff --git a/chromium/ui/views/controls/prefix_selector.h b/chromium/ui/views/controls/prefix_selector.h index 9476855534c..76f61419bdf 100644 --- a/chromium/ui/views/controls/prefix_selector.h +++ b/chromium/ui/views/controls/prefix_selector.h @@ -17,12 +17,13 @@ namespace views { class PrefixDelegate; +class View; // PrefixSelector is used to change the selection in a view as the user // types characters. class VIEWS_EXPORT PrefixSelector : public ui::TextInputClient { public: - explicit PrefixSelector(PrefixDelegate* delegate); + PrefixSelector(PrefixDelegate* delegate, View* host_view); ~PrefixSelector() override; // Invoked from the view when it loses focus. @@ -71,6 +72,8 @@ class VIEWS_EXPORT PrefixSelector : public ui::TextInputClient { PrefixDelegate* prefix_delegate_; + View* host_view_; + // Time OnTextInput() was last invoked. base::TimeTicks time_of_last_key_; diff --git a/chromium/ui/views/controls/prefix_selector_unittest.cc b/chromium/ui/views/controls/prefix_selector_unittest.cc index 8619c76229c..7f026e82dfd 100644 --- a/chromium/ui/views/controls/prefix_selector_unittest.cc +++ b/chromium/ui/views/controls/prefix_selector_unittest.cc @@ -16,7 +16,7 @@ using base::ASCIIToUTF16; namespace views { -class TestPrefixDelegate : public PrefixDelegate { +class TestPrefixDelegate : public View, public PrefixDelegate { public: TestPrefixDelegate() : selected_row_(0) { rows_.push_back(ASCIIToUTF16("aardvark")); @@ -45,7 +45,7 @@ class TestPrefixDelegate : public PrefixDelegate { class PrefixSelectorTest : public ViewsTestBase { public: PrefixSelectorTest() { - selector_.reset(new PrefixSelector(&delegate_)); + selector_.reset(new PrefixSelector(&delegate_, &delegate_)); } ~PrefixSelectorTest() override { // Explicitly release |selector_| here which can happen before releasing @@ -58,7 +58,7 @@ class PrefixSelectorTest : public ViewsTestBase { TestPrefixDelegate delegate_; private: - DISALLOW_COPY_AND_ASSIGN(PrefixSelectorTest); + DISALLOW_COPY_AND_ASSIGN(PrefixSelectorTest); }; TEST_F(PrefixSelectorTest, PrefixSelect) { diff --git a/chromium/ui/views/controls/progress_bar.cc b/chromium/ui/views/controls/progress_bar.cc index 0278e5b51b0..8533d62fa99 100644 --- a/chromium/ui/views/controls/progress_bar.cc +++ b/chromium/ui/views/controls/progress_bar.cc @@ -14,306 +14,196 @@ #include "third_party/skia/include/core/SkXfermode.h" #include "third_party/skia/include/effects/SkGradientShader.h" #include "ui/accessibility/ax_view_state.h" +#include "ui/gfx/animation/linear_animation.h" #include "ui/gfx/canvas.h" +#include "ui/gfx/color_utils.h" +#include "ui/native_theme/native_theme.h" -namespace { - -// Progress bar's border width. -const int kBorderWidth = 1; - -// Corner radius for the progress bar's border. -const int kCornerRadius = 2; - -// The width of the highlight at the right of the progress bar. -const int kHighlightWidth = 18; - -const SkColor kBackgroundColor = SkColorSetRGB(230, 230, 230); -const SkColor kBackgroundBorderColor = SkColorSetRGB(208, 208, 208); -const SkColor kBarBorderColor = SkColorSetRGB(65, 137, 237); -const SkColor kBarTopColor = SkColorSetRGB(110, 188, 249); -const SkColor kBarColorStart = SkColorSetRGB(86, 167, 247); -const SkColor kBarColorEnd = SkColorSetRGB(76, 148, 245); -const SkColor kBarHighlightEnd = SkColorSetRGB(114, 206, 251); -const SkColor kDisabledBarBorderColor = SkColorSetRGB(191, 191, 191); -const SkColor kDisabledBarColorStart = SkColorSetRGB(224, 224, 224); -const SkColor kDisabledBarColorEnd = SkColorSetRGB(212, 212, 212); - -void AddRoundRectPathWithPadding(int x, int y, - int w, int h, - int corner_radius, - SkScalar padding, - SkPath* path) { - DCHECK(path); - SkRect rect; - rect.set( - SkIntToScalar(x) + padding, SkIntToScalar(y) + padding, - SkIntToScalar(x + w) - padding, SkIntToScalar(y + h) - padding); - path->addRoundRect( - rect, - SkIntToScalar(corner_radius) - padding, - SkIntToScalar(corner_radius) - padding); -} - -void AddRoundRectPath(int x, int y, - int w, int h, - int corner_radius, - SkPath* path) { - AddRoundRectPathWithPadding(x, y, w, h, corner_radius, SK_ScalarHalf, path); -} +namespace views { -void FillRoundRect(gfx::Canvas* canvas, - int x, int y, - int w, int h, - int corner_radius, - const SkColor colors[], - const SkScalar points[], - int count, - bool gradient_horizontal) { - SkPath path; - AddRoundRectPath(x, y, w, h, corner_radius, &path); - SkPaint paint; - paint.setStyle(SkPaint::kFill_Style); - paint.setFlags(SkPaint::kAntiAlias_Flag); - - SkPoint p[2]; - p[0].iset(x, y); - if (gradient_horizontal) { - p[1].iset(x + w, y); - } else { - p[1].iset(x, y + h); - } - paint.setShader(SkGradientShader::MakeLinear(p, colors, points, count, - SkShader::kClamp_TileMode)); +namespace { - canvas->DrawPath(path, paint); -} +// In DP, the amount to round the corners of the progress bar (both bg and +// fg, aka slice). +const int kCornerRadius = 3; -void FillRoundRect(gfx::Canvas* canvas, - int x, int y, - int w, int h, - int corner_radius, - SkColor gradient_start_color, - SkColor gradient_end_color, - bool gradient_horizontal) { - if (gradient_start_color != gradient_end_color) { - SkColor colors[2] = { gradient_start_color, gradient_end_color }; - FillRoundRect(canvas, x, y, w, h, corner_radius, - colors, NULL, 2, gradient_horizontal); +// Adds a rectangle to the path. The corners will be rounded if there is room. +void AddPossiblyRoundRectToPath(const gfx::Rect& rectangle, SkPath* path) { + if (rectangle.height() < kCornerRadius) { + path->addRect(gfx::RectToSkRect(rectangle)); } else { - SkPath path; - AddRoundRectPath(x, y, w, h, corner_radius, &path); - SkPaint paint; - paint.setStyle(SkPaint::kFill_Style); - paint.setFlags(SkPaint::kAntiAlias_Flag); - paint.setColor(gradient_start_color); - canvas->DrawPath(path, paint); + path->addRoundRect(gfx::RectToSkRect(rectangle), kCornerRadius, + kCornerRadius); } } -void StrokeRoundRect(gfx::Canvas* canvas, - int x, int y, - int w, int h, - int corner_radius, - SkColor stroke_color, - int stroke_width) { - SkPath path; - AddRoundRectPath(x, y, w, h, corner_radius, &path); - SkPaint paint; - paint.setShader(NULL); - paint.setColor(stroke_color); - paint.setStyle(SkPaint::kStroke_Style); - paint.setFlags(SkPaint::kAntiAlias_Flag); - paint.setStrokeWidth(SkIntToScalar(stroke_width)); - canvas->DrawPath(path, paint); -} - } // namespace -namespace views { - // static const char ProgressBar::kViewClassName[] = "ProgressBar"; -ProgressBar::ProgressBar() - : min_display_value_(0.0), - max_display_value_(1.0), - current_value_(0.0) { -} +ProgressBar::ProgressBar(int preferred_height) + : preferred_height_(preferred_height) {} ProgressBar::~ProgressBar() { } -double ProgressBar::GetNormalizedValue() const { - const double capped_value = std::min( - std::max(current_value_, min_display_value_), max_display_value_); - return (capped_value - min_display_value_) / - (max_display_value_ - min_display_value_); +void ProgressBar::GetAccessibleState(ui::AXViewState* state) { + state->role = ui::AX_ROLE_PROGRESS_INDICATOR; + state->AddStateFlag(ui::AX_STATE_READ_ONLY); } -void ProgressBar::SetDisplayRange(double min_display_value, - double max_display_value) { - if (min_display_value != min_display_value_ || - max_display_value != max_display_value_) { - DCHECK(min_display_value < max_display_value); - min_display_value_ = min_display_value; - max_display_value_ = max_display_value; - SchedulePaint(); - } +gfx::Size ProgressBar::GetPreferredSize() const { + // The width will typically be ignored. + gfx::Size pref_size(1, preferred_height_); + gfx::Insets insets = GetInsets(); + pref_size.Enlarge(insets.width(), insets.height()); + return pref_size; +} + +const char* ProgressBar::GetClassName() const { + return kViewClassName; +} + +void ProgressBar::OnPaint(gfx::Canvas* canvas) { + if (IsIndeterminate()) + return OnPaintIndeterminate(canvas); + + gfx::Rect content_bounds = GetContentsBounds(); + + // Draw background. + SkPath background_path; + AddPossiblyRoundRectToPath(content_bounds, &background_path); + SkPaint background_paint; + background_paint.setStyle(SkPaint::kFill_Style); + background_paint.setFlags(SkPaint::kAntiAlias_Flag); + background_paint.setColor(GetBackgroundColor()); + canvas->DrawPath(background_path, background_paint); + + // Draw slice. + SkPath slice_path; + const int slice_width = static_cast<int>( + content_bounds.width() * std::min(current_value_, 1.0) + 0.5); + if (slice_width < 1) + return; + + gfx::Rect slice_bounds = content_bounds; + slice_bounds.set_width(slice_width); + AddPossiblyRoundRectToPath(slice_bounds, &slice_path); + + SkPaint slice_paint; + slice_paint.setStyle(SkPaint::kFill_Style); + slice_paint.setFlags(SkPaint::kAntiAlias_Flag); + slice_paint.setColor(GetForegroundColor()); + canvas->DrawPath(slice_path, slice_paint); } void ProgressBar::SetValue(double value) { - if (value != current_value_) { - current_value_ = value; + double adjusted_value = (value < 0.0 || value > 1.0) ? -1.0 : value; + + if (adjusted_value == current_value_) + return; + + current_value_ = adjusted_value; + if (IsIndeterminate()) { + indeterminate_bar_animation_.reset(new gfx::LinearAnimation(this)); + indeterminate_bar_animation_->SetDuration(2000); // In milliseconds. + indeterminate_bar_animation_->Start(); + } else { + indeterminate_bar_animation_.reset(); SchedulePaint(); } } -void ProgressBar::SetTooltipText(const base::string16& tooltip_text) { - tooltip_text_ = tooltip_text; +SkColor ProgressBar::GetForegroundColor() const { + return GetNativeTheme()->GetSystemColor( + ui::NativeTheme::kColorId_ProminentButtonColor); } -bool ProgressBar::GetTooltipText(const gfx::Point& p, - base::string16* tooltip) const { - DCHECK(tooltip); - *tooltip = tooltip_text_; - return !tooltip_text_.empty(); +SkColor ProgressBar::GetBackgroundColor() const { + // The default foreground is GoogleBlue500, and the default background is + // that color but 80% lighter. + return color_utils::BlendTowardOppositeLuma(GetForegroundColor(), 0xCC); } -void ProgressBar::GetAccessibleState(ui::AXViewState* state) { - state->role = ui::AX_ROLE_PROGRESS_INDICATOR; - state->AddStateFlag(ui::AX_STATE_READ_ONLY); +void ProgressBar::AnimationProgressed(const gfx::Animation* animation) { + DCHECK_EQ(animation, indeterminate_bar_animation_.get()); + DCHECK(IsIndeterminate()); + SchedulePaint(); } -gfx::Size ProgressBar::GetPreferredSize() const { - gfx::Size pref_size(100, 11); - gfx::Insets insets = GetInsets(); - pref_size.Enlarge(insets.width(), insets.height()); - return pref_size; +void ProgressBar::AnimationEnded(const gfx::Animation* animation) { + DCHECK_EQ(animation, indeterminate_bar_animation_.get()); + // Restarts animation. + if (IsIndeterminate()) + indeterminate_bar_animation_->Start(); } -const char* ProgressBar::GetClassName() const { - return kViewClassName; +bool ProgressBar::IsIndeterminate() { + return current_value_ < 0.0; } -void ProgressBar::OnPaint(gfx::Canvas* canvas) { +void ProgressBar::OnPaintIndeterminate(gfx::Canvas* canvas) { gfx::Rect content_bounds = GetContentsBounds(); - int bar_left = content_bounds.x(); - int bar_top = content_bounds.y(); - int bar_width = content_bounds.width(); - int bar_height = content_bounds.height(); - - const int progress_width = - static_cast<int>(bar_width * GetNormalizedValue() + 0.5); // Draw background. - FillRoundRect(canvas, - bar_left, bar_top, bar_width, bar_height, - kCornerRadius, - kBackgroundColor, kBackgroundColor, - false); - StrokeRoundRect(canvas, - bar_left, bar_top, - bar_width, bar_height, - kCornerRadius, - kBackgroundBorderColor, - kBorderWidth); - - if (progress_width > 1) { - // Draw inner if wide enough. - if (progress_width > kBorderWidth * 2) { - canvas->Save(); - - SkPath inner_path; - AddRoundRectPathWithPadding( - bar_left, bar_top, progress_width, bar_height, - kCornerRadius, - 0, - &inner_path); - canvas->ClipPath(inner_path, false); - - const SkColor bar_colors[] = { - kBarTopColor, - kBarTopColor, - kBarColorStart, - kBarColorEnd, - kBarColorEnd, - }; - // We want a thin 1-pixel line for kBarTopColor. - SkScalar scalar_height = SkIntToScalar(bar_height); - SkScalar highlight_width = 1 / scalar_height; - SkScalar border_width = kBorderWidth / scalar_height; - const SkScalar bar_points[] = { - 0, - border_width, - border_width + highlight_width, - SK_Scalar1 - border_width, - SK_Scalar1, - }; - - const SkColor disabled_bar_colors[] = { - kDisabledBarColorStart, - kDisabledBarColorStart, - kDisabledBarColorEnd, - kDisabledBarColorEnd, - }; - - const SkScalar disabled_bar_points[] = { - 0, - border_width, - SK_Scalar1 - border_width, - SK_Scalar1 - }; - - // Do not start from (kBorderWidth, kBorderWidth) because it makes gaps - // between the inner and the border. - FillRoundRect(canvas, - bar_left, bar_top, - progress_width, bar_height, - kCornerRadius, - enabled() ? bar_colors : disabled_bar_colors, - enabled() ? bar_points : disabled_bar_points, - enabled() ? arraysize(bar_colors) : - arraysize(disabled_bar_colors), - false); - - if (enabled()) { - // Draw the highlight to the right. - const SkColor highlight_colors[] = { - SkColorSetA(kBarHighlightEnd, 0), - kBarHighlightEnd, - kBarHighlightEnd, - }; - const SkScalar highlight_points[] = { - 0, SK_Scalar1 - kBorderWidth / scalar_height, SK_Scalar1, - }; - SkPaint paint; - paint.setStyle(SkPaint::kFill_Style); - paint.setFlags(SkPaint::kAntiAlias_Flag); - - SkPoint p[2]; - int highlight_left = - std::max(0, progress_width - kHighlightWidth - kBorderWidth); - p[0].iset(highlight_left, 0); - p[1].iset(progress_width, 0); - paint.setShader(SkGradientShader::MakeLinear( - p, highlight_colors, highlight_points, arraysize(highlight_colors), - SkShader::kClamp_TileMode)); - paint.setXfermodeMode(SkXfermode::kSrcOver_Mode); - canvas->DrawRect(gfx::Rect(highlight_left, 0, - kHighlightWidth + kBorderWidth, bar_height), - paint); - } - - canvas->Restore(); - } - - // Draw bar stroke - StrokeRoundRect(canvas, - bar_left, bar_top, progress_width, bar_height, - kCornerRadius, - enabled() ? kBarBorderColor : kDisabledBarBorderColor, - kBorderWidth); + SkPath background_path; + AddPossiblyRoundRectToPath(content_bounds, &background_path); + SkPaint background_paint; + background_paint.setStyle(SkPaint::kFill_Style); + background_paint.setFlags(SkPaint::kAntiAlias_Flag); + background_paint.setColor(GetBackgroundColor()); + canvas->DrawPath(background_path, background_paint); + + // Draw slice. + SkPath slice_path; + double time = indeterminate_bar_animation_->GetCurrentValue(); + + // The animation spec corresponds to the material design lite's parameter. + // (cf. https://github.com/google/material-design-lite/) + double bar1_left; + double bar1_width; + double bar2_left; + double bar2_width; + if (time < 0.50) { + bar1_left = time / 2; + bar1_width = time * 1.5; + bar2_left = 0; + bar2_width = 0; + } else if (time < 0.75) { + bar1_left = time * 3 - 1.25; + bar1_width = 0.75 - (time - 0.5) * 3; + bar2_left = 0; + bar2_width = time - 0.5; + } else { + bar1_left = 1; + bar1_width = 0; + bar2_left = (time - 0.75) * 4; + bar2_width = 0.25 - (time - 0.75); } + + int bar1_x = static_cast<int>(content_bounds.width() * bar1_left); + int bar1_w = + std::min(static_cast<int>(content_bounds.width() * bar1_width + 0.5), + content_bounds.width() - bar1_x); + int bar2_x = static_cast<int>(content_bounds.width() * bar2_left); + int bar2_w = + std::min(static_cast<int>(content_bounds.width() * bar2_width + 0.5), + content_bounds.width() - bar2_x); + + gfx::Rect slice_bounds = content_bounds; + slice_bounds.set_x(content_bounds.x() + bar1_x); + slice_bounds.set_width(bar1_w); + AddPossiblyRoundRectToPath(slice_bounds, &slice_path); + slice_bounds.set_x(content_bounds.x() + bar2_x); + slice_bounds.set_width(bar2_w); + AddPossiblyRoundRectToPath(slice_bounds, &slice_path); + + SkPaint slice_paint; + slice_paint.setStyle(SkPaint::kFill_Style); + slice_paint.setFlags(SkPaint::kAntiAlias_Flag); + slice_paint.setColor(GetForegroundColor()); + canvas->DrawPath(slice_path, slice_paint); } } // namespace views diff --git a/chromium/ui/views/controls/progress_bar.h b/chromium/ui/views/controls/progress_bar.h index 3fb5fb7f7f2..aa09845c7a8 100644 --- a/chromium/ui/views/controls/progress_bar.h +++ b/chromium/ui/views/controls/progress_bar.h @@ -7,58 +7,60 @@ #include "base/compiler_specific.h" #include "base/macros.h" +#include "ui/gfx/animation/animation_delegate.h" #include "ui/views/view.h" +namespace gfx { +class LinearAnimation; +} + namespace views { // Progress bar is a control that indicates progress visually. -class VIEWS_EXPORT ProgressBar : public View { +class VIEWS_EXPORT ProgressBar : public View, public gfx::AnimationDelegate { public: - // The value range defaults to [0.0, 1.0]. - ProgressBar(); + // The preferred height parameter makes it easier to use a ProgressBar with + // layout managers that size to preferred size. + explicit ProgressBar(int preferred_height = 5); ~ProgressBar() override; - double current_value() const { return current_value_; } - - // Gets a normalized current value in [0.0, 1.0] range based on current value - // range and the min/max display value range. - double GetNormalizedValue() const; + // Overridden from View: + void GetAccessibleState(ui::AXViewState* state) override; + gfx::Size GetPreferredSize() const override; + const char* GetClassName() const override; + void OnPaint(gfx::Canvas* canvas) override; - // Sets the inclusive range of values to be displayed. Values outside of the - // range will be capped when displayed. - void SetDisplayRange(double min_display_value, double max_display_value); + double current_value() const { return current_value_; } - // Sets the current value. Values outside of the range [min_display_value_, - // max_display_value_] will be stored unmodified and capped for display. + // Sets the current value. Values outside of the display range of 0.0-1.0 will + // be displayed with an infinite loading animation. void SetValue(double value); - // Sets the tooltip text. Default behavior for a progress bar is to show no - // tooltip on mouse hover. Calling this lets you set a custom tooltip. To - // revert to default behavior, call this with an empty string. - void SetTooltipText(const base::string16& tooltip_text); + protected: + // The color of the progress portion. + SkColor GetForegroundColor() const; + // The color of the portion that displays potential progress. + SkColor GetBackgroundColor() const; - // Overridden from View: - bool GetTooltipText(const gfx::Point& p, - base::string16* tooltip) const override; - void GetAccessibleState(ui::AXViewState* state) override; + int preferred_height() const { return preferred_height_; } private: static const char kViewClassName[]; - // Overridden from View: - gfx::Size GetPreferredSize() const override; - const char* GetClassName() const override; - void OnPaint(gfx::Canvas* canvas) override; + // gfx::AnimationDelegate: + void AnimationProgressed(const gfx::Animation* animation) override; + void AnimationEnded(const gfx::Animation* animation) override; + + bool IsIndeterminate(); + void OnPaintIndeterminate(gfx::Canvas* canvas); - // Inclusive range used when displaying values. - double min_display_value_; - double max_display_value_; + // Current progress to display, should be in the range 0.0 to 1.0. + double current_value_ = 0.0; - // Current value. May be outside of [min_display_value_, max_display_value_]. - double current_value_; + // In DP, the preferred height of this progress bar. + const int preferred_height_; - // Tooltip text. - base::string16 tooltip_text_; + std::unique_ptr<gfx::LinearAnimation> indeterminate_bar_animation_; DISALLOW_COPY_AND_ASSIGN(ProgressBar); }; diff --git a/chromium/ui/views/controls/progress_bar_unittest.cc b/chromium/ui/views/controls/progress_bar_unittest.cc index b9067b20c9b..5b416bf7135 100644 --- a/chromium/ui/views/controls/progress_bar_unittest.cc +++ b/chromium/ui/views/controls/progress_bar_unittest.cc @@ -10,20 +10,9 @@ namespace views { -TEST(ProgressBarTest, TooltipTextProperty) { - ProgressBar bar; - base::string16 tooltip = base::ASCIIToUTF16("Some text"); - EXPECT_FALSE(bar.GetTooltipText(gfx::Point(), &tooltip)); - EXPECT_EQ(base::string16(), tooltip); - base::string16 tooltip_text = base::ASCIIToUTF16("My progress"); - bar.SetTooltipText(tooltip_text); - EXPECT_TRUE(bar.GetTooltipText(gfx::Point(), &tooltip)); - EXPECT_EQ(tooltip_text, tooltip); -} - TEST(ProgressBarTest, Accessibility) { ProgressBar bar; - bar.SetValue(62); + bar.SetValue(0.62); ui::AXViewState state; bar.GetAccessibleState(&state); diff --git a/chromium/ui/views/controls/scroll_view.cc b/chromium/ui/views/controls/scroll_view.cc index a3e1e9787a2..4e970225471 100644 --- a/chromium/ui/views/controls/scroll_view.cc +++ b/chromium/ui/views/controls/scroll_view.cc @@ -4,14 +4,16 @@ #include "ui/views/controls/scroll_view.h" +#include "base/feature_list.h" #include "base/logging.h" #include "base/macros.h" #include "ui/events/event.h" #include "ui/gfx/canvas.h" #include "ui/native_theme/native_theme.h" +#include "ui/views/background.h" #include "ui/views/border.h" #include "ui/views/style/platform_style.h" -#include "ui/views/widget/root_view.h" +#include "ui/views/widget/widget.h" namespace views { @@ -19,6 +21,15 @@ const char ScrollView::kViewClassName[] = "ScrollView"; namespace { +const base::Feature kToolkitViewsScrollWithLayers { + "ToolkitViewsScrollWithLayers", +#if defined(OS_MACOSX) + base::FEATURE_ENABLED_BY_DEFAULT +#else + base::FEATURE_DISABLED_BY_DEFAULT +#endif +}; + // Subclass of ScrollView that resets the border when the theme changes. class ScrollViewWithBorder : public views::ScrollView { public: @@ -64,15 +75,31 @@ int CheckScrollBounds(int viewport_size, int content_size, int current_pos) { } // Make sure the content is not scrolled out of bounds -void CheckScrollBounds(View* viewport, View* view) { +void ConstrainScrollToBounds(View* viewport, View* view) { if (!view) return; - int x = CheckScrollBounds(viewport->width(), view->width(), -view->x()); - int y = CheckScrollBounds(viewport->height(), view->height(), -view->y()); + // Note that even when ScrollView::ScrollsWithLayers() is true, the header row + // scrolls by repainting. + const bool scrolls_with_layers = viewport->layer() != nullptr; + if (scrolls_with_layers) { + DCHECK(view->layer()); + DCHECK_EQ(0, view->x()); + DCHECK_EQ(0, view->y()); + } + gfx::ScrollOffset offset = scrolls_with_layers + ? view->layer()->CurrentScrollOffset() + : gfx::ScrollOffset(-view->x(), -view->y()); + + int x = CheckScrollBounds(viewport->width(), view->width(), offset.x()); + int y = CheckScrollBounds(viewport->height(), view->height(), offset.y()); - // This is no op if bounds are the same - view->SetBounds(-x, -y, view->width(), view->height()); + if (scrolls_with_layers) { + view->layer()->SetScrollOffset(gfx::ScrollOffset(x, y)); + } else { + // This is no op if bounds are the same + view->SetBounds(-x, -y, view->width(), view->height()); + } } // Used by ScrollToPosition() to make sure the new position fits within the @@ -105,9 +132,18 @@ class ScrollView::Viewport : public View { View* contents = child_at(0); gfx::Rect scroll_rect(rect); - scroll_rect.Offset(-contents->x(), -contents->y()); - static_cast<ScrollView*>(parent())->ScrollContentsRegionToBeVisible( - scroll_rect); + + ScrollView* scroll_view = static_cast<ScrollView*>(parent()); + if (scroll_view->ScrollsWithLayers()) { + // With layer scrolling, there's no need to "undo" the offset done in the + // child's View::ScrollRectToVisible() before it calls this. + DCHECK_EQ(0, contents->x()); + DCHECK_EQ(0, contents->y()); + } else { + scroll_rect.Offset(-contents->x(), -contents->y()); + } + + scroll_view->ScrollContentsRegionToBeVisible(scroll_rect); } void ChildPreferredSizeChanged(View* child) override { @@ -129,6 +165,7 @@ ScrollView::ScrollView() corner_view_(new ScrollCornerView()), min_height_(-1), max_height_(-1), + background_color_(SK_ColorTRANSPARENT), hide_horizontal_scrollbar_(false) { set_notify_enter_exit_on_child(true); @@ -142,6 +179,10 @@ ScrollView::ScrollView() vert_sb_->SetVisible(false); vert_sb_->set_controller(this); corner_view_->SetVisible(false); + + if (!base::FeatureList::IsEnabled(kToolkitViewsScrollWithLayers)) + return; + EnableViewPortLayer(); } ScrollView::~ScrollView() { @@ -158,6 +199,18 @@ ScrollView* ScrollView::CreateScrollViewWithBorder() { } void ScrollView::SetContents(View* a_view) { + // Protect against clients passing a contents view that has its own Layer. + DCHECK(!a_view->layer()); + if (ScrollsWithLayers()) { + if (!a_view->background() && background_color_ != SK_ColorTRANSPARENT) { + a_view->set_background( + Background::CreateSolidBackground(background_color_)); + } + a_view->SetPaintToLayer(true); + a_view->layer()->SetScrollable( + contents_viewport_->layer(), + base::Bind(&ScrollView::OnLayerScrolled, base::Unretained(this))); + } SetHeaderOrContents(contents_viewport_, a_view, &contents_); } @@ -165,11 +218,23 @@ void ScrollView::SetHeader(View* header) { SetHeaderOrContents(header_viewport_, header, &header_); } +void ScrollView::SetBackgroundColor(SkColor color) { + background_color_ = color; + contents_viewport_->set_background( + Background::CreateSolidBackground(background_color_)); + if (contents_ && ScrollsWithLayers() && + background_color_ != SK_ColorTRANSPARENT) { + contents_->set_background( + Background::CreateSolidBackground(background_color_)); + } +} + gfx::Rect ScrollView::GetVisibleRect() const { if (!contents_) return gfx::Rect(); - return gfx::Rect(-contents_->x(), -contents_->y(), - contents_viewport_->width(), contents_viewport_->height()); + gfx::ScrollOffset offset = CurrentOffset(); + return gfx::Rect(offset.x(), offset.y(), contents_viewport_->width(), + contents_viewport_->height()); } void ScrollView::ClipHeightTo(int min_height, int max_height) { @@ -224,8 +289,9 @@ int ScrollView::GetHeightForWidth(int width) const { } void ScrollView::Layout() { + gfx::Rect available_rect = GetContentsBounds(); if (is_bounded()) { - int content_width = width(); + int content_width = available_rect.width(); int content_height = contents()->GetHeightForWidth(content_width); if (content_height > height()) { content_width = std::max(content_width - GetScrollBarWidth(), 0); @@ -243,7 +309,7 @@ void ScrollView::Layout() { // this default behavior, the inner view has to calculate the available space, // used ComputeScrollBarsVisibility() to use the same calculation that is done // here and sets its bound to fit within. - gfx::Rect viewport_bounds = GetContentsBounds(); + gfx::Rect viewport_bounds = available_rect; const int contents_x = viewport_bounds.x(); const int contents_y = viewport_bounds.y(); if (viewport_bounds.IsEmpty()) { @@ -329,13 +395,22 @@ void ScrollView::Layout() { if (should_layout_contents && contents_) contents_->Layout(); + // Even when |contents_| needs to scroll, it can still be narrower or wider + // the viewport. So ensure the scrolling layer can fill the viewport, so that + // events will correctly hit it, and overscroll looks correct. + if (contents_ && ScrollsWithLayers()) { + gfx::Size container_size = contents_ ? contents_->size() : gfx::Size(); + container_size.SetToMax(viewport_bounds.size()); + contents_->SetBoundsRect(gfx::Rect(container_size)); + } + header_viewport_->SetBounds(contents_x, contents_y, viewport_bounds.width(), header_height); if (header_) header_->Layout(); - CheckScrollBounds(header_viewport_, header_); - CheckScrollBounds(contents_viewport_, contents_); + ConstrainScrollToBounds(header_viewport_, header_); + ConstrainScrollToBounds(contents_viewport_, contents_); SchedulePaint(); UpdateScrollBarPositions(); } @@ -379,6 +454,14 @@ void ScrollView::OnMouseExited(const ui::MouseEvent& event) { vert_sb_->OnMouseExitedScrollView(event); } +void ScrollView::OnScrollEvent(ui::ScrollEvent* event) { +#if defined(OS_MACOSX) + // TODO(tapted): Send |event| to a cc::InputHandler. For now, there's nothing + // to do because Widget::OnScrollEvent() will automatically process an + // unhandled ScrollEvent as a MouseWheelEvent. +#endif +} + void ScrollView::OnGestureEvent(ui::GestureEvent* event) { // If the event happened on one of the scrollbars, then those events are // sent directly to the scrollbars. Otherwise, only scroll events are sent to @@ -406,24 +489,24 @@ void ScrollView::ScrollToPosition(ScrollBar* source, int position) { if (!contents_) return; + gfx::ScrollOffset offset = CurrentOffset(); if (source == horiz_sb_ && horiz_sb_->visible()) { - position = AdjustPosition(contents_->x(), position, contents_->width(), + position = AdjustPosition(offset.x(), position, contents_->width(), contents_viewport_->width()); - if (-contents_->x() == position) + if (offset.x() == position) return; - contents_->SetX(-position); - if (header_) { - header_->SetX(-position); - header_->SchedulePaintInRect(header_->GetVisibleBounds()); - } + offset.set_x(position); } else if (source == vert_sb_ && vert_sb_->visible()) { - position = AdjustPosition(contents_->y(), position, contents_->height(), + position = AdjustPosition(offset.y(), position, contents_->height(), contents_viewport_->height()); - if (-contents_->y() == position) + if (offset.y() == position) return; - contents_->SetY(-position); + offset.set_y(position); } - contents_->SchedulePaintInRect(contents_->GetVisibleBounds()); + ScrollToOffset(offset); + + if (!ScrollsWithLayers()) + contents_->SchedulePaintInRect(contents_->GetVisibleBounds()); } int ScrollView::GetScrollIncrement(ScrollBar* source, bool is_page, @@ -504,10 +587,7 @@ void ScrollView::ScrollContentsRegionToBeVisible(const gfx::Rect& rect) { (vis_rect.y() > y) ? y : std::max(0, max_y - contents_viewport_->height()); - contents_->SetX(-new_x); - if (header_) - header_->SetX(-new_x); - contents_->SetY(-new_y); + ScrollToOffset(gfx::ScrollOffset(new_x, new_y)); UpdateScrollBarPositions(); } @@ -515,6 +595,12 @@ void ScrollView::ComputeScrollBarsVisibility(const gfx::Size& vp_size, const gfx::Size& content_size, bool* horiz_is_shown, bool* vert_is_shown) const { + if (hide_horizontal_scrollbar_) { + *horiz_is_shown = false; + *vert_is_shown = content_size.height() > vp_size.height(); + return; + } + // Try to fit both ways first, then try vertical bar only, then horizontal // bar only, then defaults to both shown. if (content_size.width() <= vp_size.width() && @@ -531,9 +617,6 @@ void ScrollView::ComputeScrollBarsVisibility(const gfx::Size& vp_size, *horiz_is_shown = true; *vert_is_shown = true; } - - if (hide_horizontal_scrollbar_) - *horiz_is_shown = false; } // Make sure that a single scrollbar is created and visible as needed @@ -555,17 +638,67 @@ void ScrollView::UpdateScrollBarPositions() { if (!contents_) return; + const gfx::ScrollOffset offset = CurrentOffset(); if (horiz_sb_->visible()) { int vw = contents_viewport_->width(); int cw = contents_->width(); - int origin = contents_->x(); - horiz_sb_->Update(vw, cw, -origin); + horiz_sb_->Update(vw, cw, offset.x()); } if (vert_sb_->visible()) { int vh = contents_viewport_->height(); int ch = contents_->height(); - int origin = contents_->y(); - vert_sb_->Update(vh, ch, -origin); + vert_sb_->Update(vh, ch, offset.y()); + } +} + +gfx::ScrollOffset ScrollView::CurrentOffset() const { + return ScrollsWithLayers() + ? contents_->layer()->CurrentScrollOffset() + : gfx::ScrollOffset(-contents_->x(), -contents_->y()); +} + +void ScrollView::ScrollToOffset(const gfx::ScrollOffset& offset) { + if (ScrollsWithLayers()) { + contents_->layer()->SetScrollOffset(offset); + + // TODO(tapted): Remove this call to OnLayerScrolled(). It's unnecessary, + // but will only be invoked (asynchronously) when a Compositor is present + // and commits a frame, which isn't true in some tests. + // See http://crbug.com/637521. + OnLayerScrolled(); + } else { + contents_->SetPosition(gfx::Point(-offset.x(), -offset.y())); + ScrollHeader(); + } +} + +bool ScrollView::ScrollsWithLayers() const { + // Just check for the presence of a layer since it's cheaper than querying the + // Feature flag each time. + return contents_viewport_->layer() != nullptr; +} + +void ScrollView::EnableViewPortLayer() { + background_color_ = SK_ColorWHITE; + contents_viewport_->set_background( + Background::CreateSolidBackground(background_color_)); + contents_viewport_->SetPaintToLayer(true); + contents_viewport_->layer()->SetMasksToBounds(true); +} + +void ScrollView::OnLayerScrolled() { + UpdateScrollBarPositions(); + ScrollHeader(); +} + +void ScrollView::ScrollHeader() { + if (!header_) + return; + + int x_offset = CurrentOffset().x(); + if (header_->x() != -x_offset) { + header_->SetX(-x_offset); + header_->SchedulePaintInRect(header_->GetVisibleBounds()); } } diff --git a/chromium/ui/views/controls/scroll_view.h b/chromium/ui/views/controls/scroll_view.h index da384801d96..4ec2221d55d 100644 --- a/chromium/ui/views/controls/scroll_view.h +++ b/chromium/ui/views/controls/scroll_view.h @@ -12,7 +12,14 @@ #include "base/macros.h" #include "ui/views/controls/scrollbar/scroll_bar.h" +namespace gfx { +class ScrollOffset; +} + namespace views { +namespace test { +class ScrollViewTestApi; +} ///////////////////////////////////////////////////////////////////////////// // @@ -48,6 +55,11 @@ class VIEWS_EXPORT ScrollView : public View, public ScrollBarController { // Sets the header, deleting the previous header. void SetHeader(View* header); + // Sets the background color. The default is white when scrolling with layers, + // otherwise transparent. An opaque color when scrolling with layers ensures + // fonts can be drawn with subpixel antialiasing. + void SetBackgroundColor(SkColor color); + // Returns the visible region of the content View. gfx::Rect GetVisibleRect() const; @@ -84,6 +96,7 @@ class VIEWS_EXPORT ScrollView : public View, public ScrollBarController { bool OnMouseWheel(const ui::MouseWheelEvent& e) override; void OnMouseEntered(const ui::MouseEvent& event) override; void OnMouseExited(const ui::MouseEvent& event) override; + void OnScrollEvent(ui::ScrollEvent* event) override; void OnGestureEvent(ui::GestureEvent* event) override; const char* GetClassName() const override; @@ -93,8 +106,13 @@ class VIEWS_EXPORT ScrollView : public View, public ScrollBarController { bool is_page, bool is_positive) override; + // TODO(djacobo): Remove this method when http://crbug.com/656198 is closed. + // Force |contents_viewport_| to enable a Layer(). + void EnableViewPortLayer(); + private: - FRIEND_TEST_ALL_PREFIXES(ScrollViewTest, CornerViewVisibility); + friend class test::ScrollViewTestApi; + class Viewport; // Used internally by SetHeader() and SetContents() to reset the view. Sets @@ -121,6 +139,20 @@ class VIEWS_EXPORT ScrollView : public View, public ScrollBarController { // Update the scrollbars positions given viewport and content sizes. void UpdateScrollBarPositions(); + // Helpers to get and set the current scroll offset (either from the ui::Layer + // or from the |contents_| origin offset). + gfx::ScrollOffset CurrentOffset() const; + void ScrollToOffset(const gfx::ScrollOffset& offset); + + // Whether the ScrollView scrolls using ui::Layer APIs. + bool ScrollsWithLayers() const; + + // Callback entrypoint when hosted Layers are scrolled by the Compositor. + void OnLayerScrolled(); + + // Horizontally scrolls the header (if any) to match the contents. + void ScrollHeader(); + // The current contents and its viewport. |contents_| is contained in // |contents_viewport_|. View* contents_; @@ -145,6 +177,10 @@ class VIEWS_EXPORT ScrollView : public View, public ScrollBarController { int min_height_; int max_height_; + // The background color given to the viewport (for overscroll), and to the + // contents when scrolling with layers. + SkColor background_color_; + // If true, never show the horizontal scrollbar (even if the contents is wider // than the viewport). bool hide_horizontal_scrollbar_; diff --git a/chromium/ui/views/controls/scroll_view_unittest.cc b/chromium/ui/views/controls/scroll_view_unittest.cc index d90784ca906..a5421892f11 100644 --- a/chromium/ui/views/controls/scroll_view_unittest.cc +++ b/chromium/ui/views/controls/scroll_view_unittest.cc @@ -5,16 +5,66 @@ #include "ui/views/controls/scroll_view.h" #include "base/macros.h" +#include "base/run_loop.h" +#include "base/test/test_timeouts.h" +#include "base/threading/thread_task_runner_handle.h" #include "testing/gtest/include/gtest/gtest.h" #include "ui/views/border.h" +#include "ui/views/controls/scrollbar/base_scroll_bar_thumb.h" +#include "ui/views/controls/scrollbar/native_scroll_bar.h" +#include "ui/views/controls/scrollbar/native_scroll_bar_views.h" #include "ui/views/controls/scrollbar/overlay_scroll_bar.h" #include "ui/views/test/test_views.h" +#include "ui/views/test/widget_test.h" #if defined(OS_MACOSX) #include "ui/base/test/scoped_preferred_scroller_style_mac.h" #endif +enum ScrollBarOrientation { HORIZONTAL, VERTICAL }; + namespace views { +namespace test { + +class ScrollViewTestApi { + public: + explicit ScrollViewTestApi(ScrollView* scroll_view) + : scroll_view_(scroll_view) {} + + BaseScrollBar* GetBaseScrollBar(ScrollBarOrientation orientation) { + ScrollBar* scroll_bar = orientation == VERTICAL ? scroll_view_->vert_sb_ + : scroll_view_->horiz_sb_; + if (scroll_bar->GetClassName() == NativeScrollBar::kViewClassName) { + return static_cast<NativeScrollBarViews*>( + static_cast<NativeScrollBar*>(scroll_bar)->native_wrapper_); + } + return static_cast<BaseScrollBar*>(scroll_bar); + } + + const base::Timer& GetScrollBarTimer(ScrollBarOrientation orientation) { + return GetBaseScrollBar(orientation)->repeater_.timer_for_testing(); + } + + BaseScrollBarThumb* GetScrollBarThumb(ScrollBarOrientation orientation) { + return GetBaseScrollBar(orientation)->thumb_; + } + + gfx::Point IntegralViewOffset() { + return gfx::Point() - gfx::ScrollOffsetToFlooredVector2d(CurrentOffset()); + } + + gfx::ScrollOffset CurrentOffset() { return scroll_view_->CurrentOffset(); } + + View* corner_view() { return scroll_view_->corner_view_; } + View* contents_viewport() { return scroll_view_->contents_viewport_; } + + private: + ScrollView* scroll_view_; + + DISALLOW_COPY_AND_ASSIGN(ScrollViewTestApi); +}; + +} // namespace test namespace { @@ -22,8 +72,6 @@ const int kWidth = 100; const int kMinHeight = 50; const int kMaxHeight = 100; -enum ScrollBarOrientation { HORIZONTAL, VERTICAL }; - // View implementation that allows setting the preferred size. class CustomView : public View { public: @@ -34,6 +82,8 @@ class CustomView : public View { PreferredSizeChanged(); } + const gfx::Point last_location() const { return last_location_; } + gfx::Size GetPreferredSize() const override { return preferred_size_; } void Layout() override { @@ -47,8 +97,14 @@ class CustomView : public View { SetBounds(x(), y(), width, height); } + bool OnMousePressed(const ui::MouseEvent& event) override { + last_location_ = event.location(); + return true; + } + private: gfx::Size preferred_size_; + gfx::Point last_location_; DISALLOW_COPY_AND_ASSIGN(CustomView); }; @@ -67,8 +123,115 @@ void CheckScrollbarVisibility(const ScrollView& scroll_view, } } +ui::MouseEvent TestLeftMouseAt(const gfx::Point& location, ui::EventType type) { + return ui::MouseEvent(type, location, location, base::TimeTicks(), + ui::EF_LEFT_MOUSE_BUTTON, ui::EF_LEFT_MOUSE_BUTTON); +} + } // namespace +using test::ScrollViewTestApi; + +// Test harness that includes a Widget to help test ui::Event handling. +class WidgetScrollViewTest : public test::WidgetTest, + public ui::CompositorObserver { + public: + static const int kDefaultHeight = 100; + static const int kDefaultWidth = 100; + + WidgetScrollViewTest() { +#if defined(OS_MACOSX) + // Disable scrollbar hiding (i.e. disable overlay scrollbars) by default. + scroller_style_.reset(new ui::test::ScopedPreferredScrollerStyle(false)); +#endif + } + + // Adds a ScrollView with the given |contents_view| and does layout. + ScrollView* AddScrollViewWithContents(View* contents, + bool commit_layers = true) { + const gfx::Rect default_bounds(50, 50, kDefaultWidth, kDefaultHeight); + widget_ = CreateTopLevelFramelessPlatformWidget(); + + ScrollView* scroll_view = new ScrollView(); + scroll_view->SetContents(contents); + + widget_->SetBounds(default_bounds); + widget_->Show(); + + widget_->SetContentsView(scroll_view); + scroll_view->Layout(); + + widget_->GetCompositor()->AddObserver(this); + + // Ensure the Compositor has committed layer changes before attempting to + // use them for impl-side scrolling. Note that simply RunUntilIdle() works + // when tests are run in isolation, but compositor scheduling can interact + // between test runs in the general case. + if (commit_layers) + WaitForCommit(); + return scroll_view; + } + + // Adds a ScrollView with a contents view of the given |size| and does layout. + ScrollView* AddScrollViewWithContentSize(const gfx::Size& contents_size, + bool commit_layers = true) { + View* contents = new View; + contents->SetSize(contents_size); + return AddScrollViewWithContents(contents, commit_layers); + } + + // Wait for a commit to be observed on the compositor. + void WaitForCommit() { + base::RunLoop run_loop; + quit_closure_ = run_loop.QuitClosure(); + base::ThreadTaskRunnerHandle::Get()->PostDelayedTask( + FROM_HERE, quit_closure_, TestTimeouts::action_timeout()); + run_loop.Run(); + EXPECT_TRUE(quit_closure_.is_null()) << "Timed out waiting for a commit."; + } + + void TestClickAt(const gfx::Point& location) { + ui::MouseEvent press(TestLeftMouseAt(location, ui::ET_MOUSE_PRESSED)); + ui::MouseEvent release(TestLeftMouseAt(location, ui::ET_MOUSE_RELEASED)); + widget_->OnMouseEvent(&press); + widget_->OnMouseEvent(&release); + } + + // testing::Test: + void TearDown() override { + widget_->GetCompositor()->RemoveObserver(this); + if (widget_) + widget_->CloseNow(); + WidgetTest::TearDown(); + } + + private: + // ui::CompositorObserver: + void OnCompositingDidCommit(ui::Compositor* compositor) override { + quit_closure_.Run(); + quit_closure_.Reset(); + } + void OnCompositingStarted(ui::Compositor* compositor, + base::TimeTicks start_time) override {} + void OnCompositingEnded(ui::Compositor* compositor) override {} + void OnCompositingAborted(ui::Compositor* compositor) override {} + void OnCompositingLockStateChanged(ui::Compositor* compositor) override {} + void OnCompositingShuttingDown(ui::Compositor* compositor) override {} + + Widget* widget_ = nullptr; + + base::Closure quit_closure_; + +#if defined(OS_MACOSX) + std::unique_ptr<ui::test::ScopedPreferredScrollerStyle> scroller_style_; +#endif + + DISALLOW_COPY_AND_ASSIGN(WidgetScrollViewTest); +}; + +const int WidgetScrollViewTest::kDefaultHeight; +const int WidgetScrollViewTest::kDefaultWidth; + // Verifies the viewport is sized to fit the available space. TEST(ScrollViewTest, ViewportSizedToFit) { ScrollView scroll_view; @@ -79,6 +242,23 @@ TEST(ScrollViewTest, ViewportSizedToFit) { EXPECT_EQ("0,0 100x100", contents->parent()->bounds().ToString()); } +// Verifies the viewport and content is sized to fit the available space for +// bounded scroll view. +TEST(ScrollViewTest, BoundedViewportSizedToFit) { + ScrollView scroll_view; + View* contents = new View; + scroll_view.SetContents(contents); + scroll_view.ClipHeightTo(100, 200); + scroll_view.SetBoundsRect(gfx::Rect(0, 0, 100, 100)); + scroll_view.SetBorder(Border::CreateSolidBorder(2, 0)); + scroll_view.Layout(); + EXPECT_EQ("2,2 96x96", contents->parent()->bounds().ToString()); + + // Make sure the width of |contents| is set properly not to overflow the + // viewport. + EXPECT_EQ(96, contents->width()); +} + // Verifies the scrollbars are added as necessary. // If on Mac, test the non-overlay scrollbars. TEST(ScrollViewTest, ScrollBars) { @@ -208,10 +388,25 @@ TEST(ScrollViewTest, Header) { EXPECT_EQ("0,0 100x0", header->parent()->bounds().ToString()); EXPECT_EQ("0,0 100x100", contents->parent()->bounds().ToString()); + // With layered scrolling, ScrollView::Layout() will impose a size on the + // contents that fills the viewport. Since the test view doesn't have its own + // Layout, reset it in this case so that adding a header doesn't shift the + // contents down and require scrollbars. + if (contents->layer()) { + EXPECT_EQ("0,0 100x100", contents->bounds().ToString()); + contents->SetBoundsRect(gfx::Rect()); + } + EXPECT_EQ("0,0 0x0", contents->bounds().ToString()); + // Get the header a height of 20. header->SetPreferredSize(gfx::Size(10, 20)); EXPECT_EQ("0,0 100x20", header->parent()->bounds().ToString()); EXPECT_EQ("0,20 100x80", contents->parent()->bounds().ToString()); + if (contents->layer()) { + EXPECT_EQ("0,0 100x80", contents->bounds().ToString()); + contents->SetBoundsRect(gfx::Rect()); + } + EXPECT_EQ("0,0 0x0", contents->bounds().ToString()); // Remove the header. scroll_view.SetHeader(NULL); @@ -289,6 +484,7 @@ TEST(ScrollViewTest, ScrollBarsWithHeader) { // Verifies the header scrolls horizontally with the content. TEST(ScrollViewTest, HeaderScrollsWithContent) { ScrollView scroll_view; + ScrollViewTestApi test_api(&scroll_view); CustomView* contents = new CustomView; scroll_view.SetContents(contents); contents->SetPreferredSize(gfx::Size(500, 500)); @@ -298,44 +494,53 @@ TEST(ScrollViewTest, HeaderScrollsWithContent) { header->SetPreferredSize(gfx::Size(500, 20)); scroll_view.SetBoundsRect(gfx::Rect(0, 0, 100, 100)); - EXPECT_EQ("0,0", contents->bounds().origin().ToString()); + EXPECT_EQ("0,0", test_api.IntegralViewOffset().ToString()); EXPECT_EQ("0,0", header->bounds().origin().ToString()); // Scroll the horizontal scrollbar. ASSERT_TRUE(scroll_view.horizontal_scroll_bar()); scroll_view.ScrollToPosition( const_cast<ScrollBar*>(scroll_view.horizontal_scroll_bar()), 1); - EXPECT_EQ("-1,0", contents->bounds().origin().ToString()); + EXPECT_EQ("-1,0", test_api.IntegralViewOffset().ToString()); EXPECT_EQ("-1,0", header->bounds().origin().ToString()); // Scrolling the vertical scrollbar shouldn't effect the header. ASSERT_TRUE(scroll_view.vertical_scroll_bar()); scroll_view.ScrollToPosition( const_cast<ScrollBar*>(scroll_view.vertical_scroll_bar()), 1); - EXPECT_EQ("-1,-1", contents->bounds().origin().ToString()); + EXPECT_EQ("-1,-1", test_api.IntegralViewOffset().ToString()); EXPECT_EQ("-1,0", header->bounds().origin().ToString()); } // Verifies ScrollRectToVisible() on the child works. TEST(ScrollViewTest, ScrollRectToVisible) { +#if defined(OS_MACOSX) + ui::test::ScopedPreferredScrollerStyle scroller_style_override(false); +#endif ScrollView scroll_view; + ScrollViewTestApi test_api(&scroll_view); CustomView* contents = new CustomView; scroll_view.SetContents(contents); contents->SetPreferredSize(gfx::Size(500, 1000)); scroll_view.SetBoundsRect(gfx::Rect(0, 0, 100, 100)); scroll_view.Layout(); - EXPECT_EQ("0,0", contents->bounds().origin().ToString()); + EXPECT_EQ("0,0", test_api.IntegralViewOffset().ToString()); // Scroll to y=405 height=10, this should make the y position of the content // at (405 + 10) - viewport_height (scroll region bottom aligned). contents->ScrollRectToVisible(gfx::Rect(0, 405, 10, 10)); - const int viewport_height = contents->parent()->height(); - EXPECT_EQ(-(415 - viewport_height), contents->y()); + const int viewport_height = test_api.contents_viewport()->height(); + + // Expect there to be a horizontal scrollbar, making the viewport shorter. + EXPECT_LT(viewport_height, 100); + + gfx::ScrollOffset offset = test_api.CurrentOffset(); + EXPECT_EQ(415 - viewport_height, offset.y()); // Scroll to the current y-location and 10x10; should do nothing. - contents->ScrollRectToVisible(gfx::Rect(0, -contents->y(), 10, 10)); - EXPECT_EQ(-(415 - viewport_height), contents->y()); + contents->ScrollRectToVisible(gfx::Rect(0, offset.y(), 10, 10)); + EXPECT_EQ(415 - viewport_height, test_api.CurrentOffset().y()); } // Verifies ClipHeightTo() uses the height of the content when it is between the @@ -361,23 +566,29 @@ TEST(ScrollViewTest, ClipHeightToNormalContentHeight) { } // Verifies ClipHeightTo() uses the minimum height when the content is shorter -// thamn the minimum height value. +// than the minimum height value. TEST(ScrollViewTest, ClipHeightToShortContentHeight) { ScrollView scroll_view; scroll_view.ClipHeightTo(kMinHeight, kMaxHeight); const int kShortContentHeight = 10; - scroll_view.SetContents( - new views::StaticSizedView(gfx::Size(kWidth, kShortContentHeight))); + View* contents = + new views::StaticSizedView(gfx::Size(kWidth, kShortContentHeight)); + scroll_view.SetContents(contents); EXPECT_EQ(gfx::Size(kWidth, kMinHeight), scroll_view.GetPreferredSize()); scroll_view.SizeToPreferredSize(); scroll_view.Layout(); - EXPECT_EQ(gfx::Size(kWidth, kShortContentHeight), - scroll_view.contents()->size()); + // Layered scrolling requires the contents to fill the viewport. + if (contents->layer()) { + EXPECT_EQ(gfx::Size(kWidth, kMinHeight), scroll_view.contents()->size()); + } else { + EXPECT_EQ(gfx::Size(kWidth, kShortContentHeight), + scroll_view.contents()->size()); + } EXPECT_EQ(gfx::Size(kWidth, kMinHeight), scroll_view.size()); } @@ -436,7 +647,7 @@ TEST(ScrollViewTest, CornerViewVisibility) { View* contents = new View; scroll_view.SetContents(contents); scroll_view.SetBoundsRect(gfx::Rect(0, 0, 100, 100)); - View* corner_view = scroll_view.corner_view_; + View* corner_view = ScrollViewTestApi(&scroll_view).corner_view(); // Corner view should be visible when both scrollbars are visible. contents->SetBounds(0, 0, 200, 200); @@ -532,4 +743,161 @@ TEST(ScrollViewTest, CocoaOverlayScrollBars) { } #endif +// Test that increasing the size of the viewport "below" scrolled content causes +// the content to scroll up so that it still fills the viewport. +TEST(ScrollViewTest, ConstrainScrollToBounds) { + ScrollView scroll_view; + ScrollViewTestApi test_api(&scroll_view); + + View* contents = new View; + contents->SetBoundsRect(gfx::Rect(0, 0, 300, 300)); + scroll_view.SetContents(contents); + scroll_view.SetBoundsRect(gfx::Rect(0, 0, 100, 100)); + scroll_view.Layout(); + + EXPECT_EQ(gfx::ScrollOffset(), test_api.CurrentOffset()); + + // Scroll as far as it goes and query location to discount scroll bars. + contents->ScrollRectToVisible(gfx::Rect(300, 300, 1, 1)); + const gfx::ScrollOffset fully_scrolled = test_api.CurrentOffset(); + EXPECT_NE(gfx::ScrollOffset(), fully_scrolled); + + // Making the viewport 55 pixels taller should scroll up the same amount. + scroll_view.SetBoundsRect(gfx::Rect(0, 0, 100, 155)); + scroll_view.Layout(); + EXPECT_EQ(fully_scrolled.y() - 55, test_api.CurrentOffset().y()); + EXPECT_EQ(fully_scrolled.x(), test_api.CurrentOffset().x()); + + // And 77 pixels wider should scroll left. Also make it short again: the y- + // offset from the last change should remain. + scroll_view.SetBoundsRect(gfx::Rect(0, 0, 177, 100)); + scroll_view.Layout(); + EXPECT_EQ(fully_scrolled.y() - 55, test_api.CurrentOffset().y()); + EXPECT_EQ(fully_scrolled.x() - 77, test_api.CurrentOffset().x()); +} + +// Test scrolling behavior when clicking on the scroll track. +TEST_F(WidgetScrollViewTest, ScrollTrackScrolling) { + // Set up with a vertical scroller. + ScrollView* scroll_view = + AddScrollViewWithContentSize(gfx::Size(10, kDefaultHeight * 5)); + ScrollViewTestApi test_api(scroll_view); + BaseScrollBar* scroll_bar = test_api.GetBaseScrollBar(VERTICAL); + View* thumb = test_api.GetScrollBarThumb(VERTICAL); + + // Click in the middle of the track, ensuring it's below the thumb. + const gfx::Point location = scroll_bar->bounds().CenterPoint(); + EXPECT_GT(location.y(), thumb->bounds().bottom()); + ui::MouseEvent press(TestLeftMouseAt(location, ui::ET_MOUSE_PRESSED)); + ui::MouseEvent release(TestLeftMouseAt(location, ui::ET_MOUSE_RELEASED)); + + const base::Timer& timer = test_api.GetScrollBarTimer(VERTICAL); + EXPECT_FALSE(timer.IsRunning()); + + EXPECT_EQ(0, scroll_view->GetVisibleRect().y()); + scroll_bar->OnMouseEvent(&press); + + // Clicking the scroll track should scroll one "page". + EXPECT_EQ(kDefaultHeight, scroll_view->GetVisibleRect().y()); + + // While the mouse is pressed, timer should trigger more scroll events. + EXPECT_TRUE(timer.IsRunning()); + + // Upon release timer should stop (and scroll position should remain). + scroll_bar->OnMouseEvent(&release); + EXPECT_FALSE(timer.IsRunning()); + EXPECT_EQ(kDefaultHeight, scroll_view->GetVisibleRect().y()); +} + +// Test that LocatedEvents are transformed correctly when scrolling. +TEST_F(WidgetScrollViewTest, EventLocation) { + // Set up with both scrollers. + CustomView* contents = new CustomView; + contents->SetPreferredSize(gfx::Size(kDefaultHeight * 5, kDefaultHeight * 5)); + AddScrollViewWithContents(contents); + + const gfx::Point location_in_widget(10, 10); + + // Click without scrolling. + TestClickAt(location_in_widget); + EXPECT_EQ(location_in_widget, contents->last_location()); + + // Scroll down a page. + contents->ScrollRectToVisible( + gfx::Rect(0, kDefaultHeight, 1, kDefaultHeight)); + TestClickAt(location_in_widget); + EXPECT_EQ(gfx::Point(10, 10 + kDefaultHeight), contents->last_location()); + + // Scroll right a page (and back up). + contents->ScrollRectToVisible(gfx::Rect(kDefaultWidth, 0, kDefaultWidth, 1)); + TestClickAt(location_in_widget); + EXPECT_EQ(gfx::Point(10 + kDefaultWidth, 10), contents->last_location()); + + // Scroll both directions. + contents->ScrollRectToVisible( + gfx::Rect(kDefaultWidth, kDefaultHeight, kDefaultWidth, kDefaultHeight)); + TestClickAt(location_in_widget); + EXPECT_EQ(gfx::Point(10 + kDefaultWidth, 10 + kDefaultHeight), + contents->last_location()); +} + +// Test that views scroll offsets are in sync with the layer scroll offsets. +TEST_F(WidgetScrollViewTest, ScrollOffsetUsingLayers) { + // Set up with a vertical scroller, but don't commit the layer changes yet. + ScrollView* scroll_view = + AddScrollViewWithContentSize(gfx::Size(10, kDefaultHeight * 5), false); + ScrollViewTestApi test_api(scroll_view); + + EXPECT_EQ(gfx::ScrollOffset(0, 0), test_api.CurrentOffset()); + + // UI code may request a scroll before layer changes are committed. + gfx::Rect offset(0, kDefaultHeight * 2, 1, kDefaultHeight); + scroll_view->contents()->ScrollRectToVisible(offset); + EXPECT_EQ(gfx::ScrollOffset(0, offset.y()), test_api.CurrentOffset()); + + // The following only makes sense when layered scrolling is enabled. + View* container = scroll_view->contents(); +#if defined(OS_MACOSX) + // Sanity check: Mac should always scroll with layers. + EXPECT_TRUE(container->layer()); +#endif + if (!container->layer()) + return; + + // Container and viewport should have layers. + EXPECT_TRUE(container->layer()); + EXPECT_TRUE(test_api.contents_viewport()->layer()); + + // In a Widget, so there should be a compositor. + ui::Compositor* compositor = container->layer()->GetCompositor(); + EXPECT_TRUE(compositor); + + // But setting on the impl side should fail since the layer isn't committed. + int layer_id = container->layer()->cc_layer_for_testing()->id(); + EXPECT_FALSE(compositor->ScrollLayerTo(layer_id, gfx::ScrollOffset(0, 0))); + EXPECT_EQ(gfx::ScrollOffset(0, offset.y()), test_api.CurrentOffset()); + + WaitForCommit(); + EXPECT_EQ(gfx::ScrollOffset(0, offset.y()), test_api.CurrentOffset()); + + // Upon commit, the impl side should report the same value too. + gfx::ScrollOffset impl_offset; + EXPECT_TRUE(compositor->GetScrollOffsetForLayer(layer_id, &impl_offset)); + EXPECT_EQ(gfx::ScrollOffset(0, offset.y()), impl_offset); + + // Now impl-side scrolling should work, and also update the ScrollView. + offset.set_y(kDefaultHeight * 3); + EXPECT_TRUE( + compositor->ScrollLayerTo(layer_id, gfx::ScrollOffset(0, offset.y()))); + EXPECT_EQ(gfx::ScrollOffset(0, offset.y()), test_api.CurrentOffset()); + + // Scroll via ScrollView API. Should be reflected on the impl side. + offset.set_y(kDefaultHeight * 4); + scroll_view->contents()->ScrollRectToVisible(offset); + EXPECT_EQ(gfx::ScrollOffset(0, offset.y()), test_api.CurrentOffset()); + + EXPECT_TRUE(compositor->GetScrollOffsetForLayer(layer_id, &impl_offset)); + EXPECT_EQ(gfx::ScrollOffset(0, offset.y()), impl_offset); +} + } // namespace views diff --git a/chromium/ui/views/controls/scrollbar/base_scroll_bar.cc b/chromium/ui/views/controls/scrollbar/base_scroll_bar.cc index 28da57db9a8..82d852944b6 100644 --- a/chromium/ui/views/controls/scrollbar/base_scroll_bar.cc +++ b/chromium/ui/views/controls/scrollbar/base_scroll_bar.cc @@ -290,8 +290,9 @@ void BaseScrollBar::ShowContextMenuForView(View* source, views::MenuItemView* menu = new views::MenuItemView(this); // MenuRunner takes ownership of |menu|. - menu_runner_.reset(new MenuRunner( - menu, MenuRunner::HAS_MNEMONICS | views::MenuRunner::CONTEXT_MENU)); + menu_runner_.reset(new MenuRunner(menu, MenuRunner::HAS_MNEMONICS | + views::MenuRunner::CONTEXT_MENU | + views::MenuRunner::ASYNC)); menu->AppendDelegateMenuItem(ScrollBarContextMenuCommand_ScrollHere); menu->AppendSeparator(); menu->AppendDelegateMenuItem(ScrollBarContextMenuCommand_ScrollStart); @@ -302,13 +303,8 @@ void BaseScrollBar::ShowContextMenuForView(View* source, menu->AppendSeparator(); menu->AppendDelegateMenuItem(ScrollBarContextMenuCommand_ScrollPrev); menu->AppendDelegateMenuItem(ScrollBarContextMenuCommand_ScrollNext); - if (menu_runner_->RunMenuAt(GetWidget(), - NULL, - gfx::Rect(p, gfx::Size()), - MENU_ANCHOR_TOPLEFT, - source_type) == MenuRunner::MENU_DELETED) { - return; - } + menu_runner_->RunMenuAt(GetWidget(), nullptr, gfx::Rect(p, gfx::Size()), + MENU_ANCHOR_TOPLEFT, source_type); } /////////////////////////////////////////////////////////////////////////////// diff --git a/chromium/ui/views/controls/scrollbar/base_scroll_bar.h b/chromium/ui/views/controls/scrollbar/base_scroll_bar.h index 6d6944259d3..b56663dc825 100644 --- a/chromium/ui/views/controls/scrollbar/base_scroll_bar.h +++ b/chromium/ui/views/controls/scrollbar/base_scroll_bar.h @@ -15,6 +15,9 @@ #include "ui/views/repeat_controller.h" namespace views { +namespace test { +class ScrollViewTestApi; +} class BaseScrollBarThumb; class MenuRunner; @@ -114,6 +117,8 @@ class VIEWS_EXPORT BaseScrollBar : public ScrollBar, virtual int GetScrollIncrement(bool is_page, bool is_positive); private: + friend class test::ScrollViewTestApi; + FRIEND_TEST_ALL_PREFIXES(NativeScrollBarTest, ScrollBarFitsToBottom); FRIEND_TEST_ALL_PREFIXES(NativeScrollBarTest, ThumbFullLengthOfTrack); int GetThumbSizeForTest(); diff --git a/chromium/ui/views/controls/scrollbar/cocoa_scroll_bar.mm b/chromium/ui/views/controls/scrollbar/cocoa_scroll_bar.mm index f11ea1a237d..3c6bb9b607f 100644 --- a/chromium/ui/views/controls/scrollbar/cocoa_scroll_bar.mm +++ b/chromium/ui/views/controls/scrollbar/cocoa_scroll_bar.mm @@ -290,6 +290,7 @@ bool CocoaScrollBar::OnMousePressed(const ui::MouseEvent& event) { void CocoaScrollBar::OnMouseReleased(const ui::MouseEvent& event) { ResetOverlayScrollbar(); + BaseScrollBar::OnMouseReleased(event); } void CocoaScrollBar::OnMouseEntered(const ui::MouseEvent& event) { diff --git a/chromium/ui/views/controls/scrollbar/native_scroll_bar.h b/chromium/ui/views/controls/scrollbar/native_scroll_bar.h index c9d96af77e7..c94089514c3 100644 --- a/chromium/ui/views/controls/scrollbar/native_scroll_bar.h +++ b/chromium/ui/views/controls/scrollbar/native_scroll_bar.h @@ -18,6 +18,9 @@ class NativeTheme; } namespace views { +namespace test { +class ScrollViewTestApi; +} class NativeScrollBarWrapper; @@ -38,6 +41,7 @@ class VIEWS_EXPORT NativeScrollBar : public ScrollBar { private: friend class NativeScrollBarTest; + friend class test::ScrollViewTestApi; FRIEND_TEST_ALL_PREFIXES(NativeScrollBarTest, Scrolling); // Overridden from View. diff --git a/chromium/ui/views/controls/single_split_view.cc b/chromium/ui/views/controls/single_split_view.cc deleted file mode 100644 index 354d3e14bb9..00000000000 --- a/chromium/ui/views/controls/single_split_view.cc +++ /dev/null @@ -1,264 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "ui/views/controls/single_split_view.h" - -#include "build/build_config.h" -#include "ui/accessibility/ax_view_state.h" -#include "ui/base/cursor/cursor.h" -#include "ui/gfx/canvas.h" -#include "ui/views/background.h" -#include "ui/views/border.h" -#include "ui/views/controls/single_split_view_listener.h" -#include "ui/views/native_cursor.h" - -#if defined(OS_WIN) -#include "skia/ext/skia_utils_win.h" -#endif - -namespace views { - -// static -const char SingleSplitView::kViewClassName[] = "SingleSplitView"; - -// Size of the divider in pixels. -static const int kDividerSize = 4; - -SingleSplitView::SingleSplitView(View* leading, - View* trailing, - Orientation orientation, - SingleSplitViewListener* listener) - : is_horizontal_(orientation == HORIZONTAL_SPLIT), - divider_offset_(-1), - resize_leading_on_bounds_change_(true), - resize_disabled_(false), - listener_(listener) { - AddChildView(leading); - AddChildView(trailing); -#if defined(OS_WIN) - set_background( - views::Background::CreateSolidBackground( - skia::COLORREFToSkColor(GetSysColor(COLOR_3DFACE)))); -#endif -} - -void SingleSplitView::Layout() { - gfx::Rect leading_bounds; - gfx::Rect trailing_bounds; - CalculateChildrenBounds(GetContentsBounds(), &leading_bounds, - &trailing_bounds); - - if (has_children()) { - if (child_at(0)->visible()) - child_at(0)->SetBoundsRect(leading_bounds); - if (child_count() > 1) { - if (child_at(1)->visible()) - child_at(1)->SetBoundsRect(trailing_bounds); - } - } - - // Invoke super's implementation so that the children are layed out. - View::Layout(); -} - -const char* SingleSplitView::GetClassName() const { - return kViewClassName; -} - -void SingleSplitView::GetAccessibleState(ui::AXViewState* state) { - state->role = ui::AX_ROLE_GROUP; - state->name = accessible_name_; -} - -gfx::Size SingleSplitView::GetPreferredSize() const { - int width = 0; - int height = 0; - for (int i = 0; i < 2 && i < child_count(); ++i) { - const View* view = child_at(i); - gfx::Size pref = view->GetPreferredSize(); - if (is_horizontal_) { - width += pref.width(); - height = std::max(height, pref.height()); - } else { - width = std::max(width, pref.width()); - height += pref.height(); - } - } - if (is_horizontal_) - width += GetDividerSize(); - else - height += GetDividerSize(); - width += GetInsets().width(); - height += GetInsets().height(); - return gfx::Size(width, height); -} - -gfx::NativeCursor SingleSplitView::GetCursor(const ui::MouseEvent& event) { - if (!IsPointInDivider(event.location())) - return gfx::kNullCursor; - return is_horizontal_ ? GetNativeEastWestResizeCursor() - : GetNativeNorthSouthResizeCursor(); -} - -int SingleSplitView::GetDividerSize() const { - bool both_visible = child_count() > 1 && child_at(0)->visible() && - child_at(1)->visible(); - return both_visible && !resize_disabled_ ? kDividerSize : 0; -} - -void SingleSplitView::CalculateChildrenBounds( - const gfx::Rect& bounds, - gfx::Rect* leading_bounds, - gfx::Rect* trailing_bounds) const { - bool is_leading_visible = has_children() && child_at(0)->visible(); - bool is_trailing_visible = child_count() > 1 && child_at(1)->visible(); - - if (!is_leading_visible && !is_trailing_visible) { - *leading_bounds = gfx::Rect(); - *trailing_bounds = gfx::Rect(); - return; - } - - int divider_at; - - if (!is_trailing_visible) { - divider_at = GetPrimaryAxisSize(bounds.width(), bounds.height()); - } else if (!is_leading_visible) { - divider_at = 0; - } else { - divider_at = CalculateDividerOffset(divider_offset_, bounds, bounds); - divider_at = NormalizeDividerOffset(divider_at, bounds); - } - - int divider_size = GetDividerSize(); - - if (is_horizontal_) { - *leading_bounds = - gfx::Rect(bounds.x(), bounds.y(), divider_at, bounds.height()); - *trailing_bounds = - gfx::Rect(divider_at + divider_size + bounds.x(), bounds.y(), - std::max(0, bounds.width() - divider_at - divider_size), - bounds.height()); - } else { - *leading_bounds = - gfx::Rect(bounds.x(), bounds.y(), bounds.width(), divider_at); - *trailing_bounds = gfx::Rect( - bounds.x(), divider_at + divider_size + bounds.y(), bounds.width(), - std::max(0, bounds.height() - divider_at - divider_size)); - } -} - -void SingleSplitView::SetAccessibleName(const base::string16& name) { - accessible_name_ = name; -} - -bool SingleSplitView::OnMousePressed(const ui::MouseEvent& event) { - if (!IsPointInDivider(event.location())) - return false; - drag_info_.initial_mouse_offset = GetPrimaryAxisSize(event.x(), event.y()); - drag_info_.initial_divider_offset = - NormalizeDividerOffset(divider_offset_, GetContentsBounds()); - return true; -} - -bool SingleSplitView::OnMouseDragged(const ui::MouseEvent& event) { - if (child_count() < 2) - return false; - - int delta_offset = GetPrimaryAxisSize(event.x(), event.y()) - - drag_info_.initial_mouse_offset; - if (is_horizontal_ && base::i18n::IsRTL()) - delta_offset *= -1; - // Honor the first child's minimum size when resizing. - gfx::Size min = child_at(0)->GetMinimumSize(); - int new_size = std::max(GetPrimaryAxisSize(min.width(), min.height()), - drag_info_.initial_divider_offset + delta_offset); - - // Honor the second child's minimum size, and don't let the view - // get bigger than our width. - min = child_at(1)->GetMinimumSize(); - new_size = std::min(GetPrimaryAxisSize() - kDividerSize - - GetPrimaryAxisSize(min.width(), min.height()), new_size); - - if (new_size != divider_offset_) { - set_divider_offset(new_size); - if (!listener_ || listener_->SplitHandleMoved(this)) - Layout(); - } - return true; -} - -void SingleSplitView::OnMouseCaptureLost() { - if (child_count() < 2) - return; - - if (drag_info_.initial_divider_offset != divider_offset_) { - set_divider_offset(drag_info_.initial_divider_offset); - if (!listener_ || listener_->SplitHandleMoved(this)) - Layout(); - } -} - -void SingleSplitView::OnBoundsChanged(const gfx::Rect& previous_bounds) { - gfx::Rect previous_content_bounds = previous_bounds; - previous_content_bounds.Inset(GetInsets()); - divider_offset_ = CalculateDividerOffset( - divider_offset_, previous_content_bounds, GetContentsBounds()); -} - -bool SingleSplitView::IsPointInDivider(const gfx::Point& p) { - if (resize_disabled_) - return false; - - if (child_count() < 2) - return false; - - if (!child_at(0)->visible() || !child_at(1)->visible()) - return false; - - int divider_relative_offset; - if (is_horizontal_) { - divider_relative_offset = - p.x() - child_at(base::i18n::IsRTL() ? 1 : 0)->width(); - } else { - divider_relative_offset = p.y() - child_at(0)->height(); - } - return (divider_relative_offset >= 0 && - divider_relative_offset < GetDividerSize()); -} - -int SingleSplitView::CalculateDividerOffset( - int divider_offset, - const gfx::Rect& previous_bounds, - const gfx::Rect& new_bounds) const { - if (resize_leading_on_bounds_change_ && divider_offset != -1) { - // We do not update divider_offset on minimize (to zero) and on restore - // (to largest value). As a result we get back to the original value upon - // window restore. - bool is_minimize_or_restore = - previous_bounds.height() == 0 || new_bounds.height() == 0; - if (!is_minimize_or_restore) { - if (is_horizontal_) - divider_offset += new_bounds.width() - previous_bounds.width(); - else - divider_offset += new_bounds.height() - previous_bounds.height(); - - if (divider_offset < 0) - divider_offset = GetDividerSize(); - } - } - return divider_offset; -} - -int SingleSplitView::NormalizeDividerOffset(int divider_offset, - const gfx::Rect& bounds) const { - int primary_axis_size = GetPrimaryAxisSize(bounds.width(), bounds.height()); - if (divider_offset < 0) - // primary_axis_size may < GetDividerSize during initial layout. - return std::max(0, (primary_axis_size - GetDividerSize()) / 2); - return std::min(divider_offset, - std::max(primary_axis_size - GetDividerSize(), 0)); -} - -} // namespace views diff --git a/chromium/ui/views/controls/single_split_view.h b/chromium/ui/views/controls/single_split_view.h deleted file mode 100644 index 2cc21ea8a84..00000000000 --- a/chromium/ui/views/controls/single_split_view.h +++ /dev/null @@ -1,148 +0,0 @@ -// Copyright (c) 2011 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef UI_VIEWS_CONTROLS_SINGLE_SPLIT_VIEW_H_ -#define UI_VIEWS_CONTROLS_SINGLE_SPLIT_VIEW_H_ - -#include "base/gtest_prod_util.h" -#include "base/macros.h" -#include "ui/views/view.h" - -namespace views { - -class SingleSplitViewListener; - -// SingleSplitView lays out two views next to each other, either horizontally -// or vertically. A splitter exists between the two views that the user can -// drag around to resize the views. -// SingleSplitViewListener's SplitHandleMoved notification helps to monitor user -// initiated layout changes. -class VIEWS_EXPORT SingleSplitView : public View { - public: - enum Orientation { - HORIZONTAL_SPLIT, - VERTICAL_SPLIT - }; - - static const char kViewClassName[]; - - SingleSplitView(View* leading, - View* trailing, - Orientation orientation, - SingleSplitViewListener* listener); - - void Layout() override; - const char* GetClassName() const override; - - void GetAccessibleState(ui::AXViewState* state) override; - - // SingleSplitView's preferred size is the sum of the preferred widths - // and the max of the heights. - gfx::Size GetPreferredSize() const override; - - // Overriden to return a resize cursor when over the divider. - gfx::NativeCursor GetCursor(const ui::MouseEvent& event) override; - - Orientation orientation() const { - return is_horizontal_ ? HORIZONTAL_SPLIT : VERTICAL_SPLIT; - } - - void set_orientation(Orientation orientation) { - is_horizontal_ = orientation == HORIZONTAL_SPLIT; - } - - void set_divider_offset(int divider_offset) { - divider_offset_ = divider_offset; - } - int divider_offset() const { return divider_offset_; } - - int GetDividerSize() const; - - void set_resize_disabled(bool resize_disabled) { - resize_disabled_ = resize_disabled; - } - bool is_resize_disabled() const { return resize_disabled_; } - - // Sets whether the leading component is resized when the split views size - // changes. The default is true. A value of false results in the trailing - // component resizing on a bounds change. - void set_resize_leading_on_bounds_change(bool resize) { - resize_leading_on_bounds_change_ = resize; - } - - // Calculates ideal leading and trailing view bounds according to the given - // split view |bounds|, current divider offset and children visiblity. - // Does not change children view bounds. - void CalculateChildrenBounds(const gfx::Rect& bounds, - gfx::Rect* leading_bounds, - gfx::Rect* trailing_bounds) const; - - void SetAccessibleName(const base::string16& name); - - protected: - // View overrides. - bool OnMousePressed(const ui::MouseEvent& event) override; - bool OnMouseDragged(const ui::MouseEvent& event) override; - void OnMouseCaptureLost() override; - void OnBoundsChanged(const gfx::Rect& previous_bounds) override; - - private: - // This test calls OnMouse* functions. - FRIEND_TEST_ALL_PREFIXES(SingleSplitViewTest, MouseDrag); - - // Returns true if |x| or |y| is over the divider. - bool IsPointInDivider(const gfx::Point& p); - - // Calculates the new |divider_offset| based on the changes of split view - // bounds. - int CalculateDividerOffset(int divider_offset, - const gfx::Rect& previous_bounds, - const gfx::Rect& new_bounds) const; - - // Returns divider offset within primary axis size range for given split - // view |bounds|. - int NormalizeDividerOffset(int divider_offset, const gfx::Rect& bounds) const; - - // Returns width in case of horizontal split and height otherwise. - int GetPrimaryAxisSize() const { - return GetPrimaryAxisSize(width(), height()); - } - - int GetPrimaryAxisSize(int h, int v) const { - return is_horizontal_ ? h : v; - } - - // Used to track drag info. - struct DragInfo { - // The initial coordinate of the mouse when the user started the drag. - int initial_mouse_offset; - // The initial position of the divider when the user started the drag. - int initial_divider_offset; - }; - - DragInfo drag_info_; - - // Orientation of the split view. - bool is_horizontal_; - - // Position of the divider. - int divider_offset_; - - bool resize_leading_on_bounds_change_; - - // Whether resizing is disabled. - bool resize_disabled_; - - // Listener to notify about user initiated handle movements. Not owned. - SingleSplitViewListener* listener_; - - // The accessible name of this view. - base::string16 accessible_name_; - - DISALLOW_COPY_AND_ASSIGN(SingleSplitView); -}; - -} // namespace views - -#endif // UI_VIEWS_CONTROLS_SINGLE_SPLIT_VIEW_H_ diff --git a/chromium/ui/views/controls/single_split_view_listener.h b/chromium/ui/views/controls/single_split_view_listener.h deleted file mode 100644 index 0d589c9cfb8..00000000000 --- a/chromium/ui/views/controls/single_split_view_listener.h +++ /dev/null @@ -1,30 +0,0 @@ -// Copyright (c) 2011 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef UI_VIEWS_CONTROLS_SINGLE_SPLIT_VIEW_LISTENER_H_ -#define UI_VIEWS_CONTROLS_SINGLE_SPLIT_VIEW_LISTENER_H_ - -#include "ui/views/views_export.h" - -namespace views { - -class SingleSplitView; - -// An interface implemented by objects that want to be notified when the -// splitter moves. -class VIEWS_EXPORT SingleSplitViewListener { - public: - // Invoked when split handle is moved by the user. |sender|'s divider_offset - // is already set to the new value, but Layout has not happened yet. - // Returns false if the layout has been handled by the listener, returns - // true if |sender| should do it by itself. - virtual bool SplitHandleMoved(SingleSplitView* sender) = 0; - - protected: - virtual ~SingleSplitViewListener() {} -}; - -} // namespace views - -#endif // UI_VIEWS_CONTROLS_SINGLE_SPLIT_VIEW_LISTENER_H_ diff --git a/chromium/ui/views/controls/single_split_view_unittest.cc b/chromium/ui/views/controls/single_split_view_unittest.cc deleted file mode 100644 index d2a588e0c74..00000000000 --- a/chromium/ui/views/controls/single_split_view_unittest.cc +++ /dev/null @@ -1,251 +0,0 @@ -// Copyright (c) 2011 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "ui/views/controls/single_split_view.h" - -#include <stddef.h> - -#include "base/logging.h" -#include "base/macros.h" -#include "testing/gtest/include/gtest/gtest.h" -#include "ui/events/event_utils.h" -#include "ui/views/border.h" -#include "ui/views/controls/single_split_view_listener.h" - -namespace { - -static void VerifySplitViewLayout(const views::SingleSplitView& split) { - ASSERT_EQ(2, split.child_count()); - - const views::View* leading = split.child_at(0); - const views::View* trailing = split.child_at(1); - - if (split.bounds().IsEmpty()) { - EXPECT_TRUE(leading->bounds().IsEmpty()); - EXPECT_TRUE(trailing->bounds().IsEmpty()); - return; - } - - EXPECT_FALSE(leading->bounds().IsEmpty()); - EXPECT_FALSE(trailing->bounds().IsEmpty()); - EXPECT_FALSE(leading->bounds().Intersects(trailing->bounds())); - - if (split.orientation() == views::SingleSplitView::HORIZONTAL_SPLIT) { - EXPECT_EQ(leading->bounds().height(), - split.bounds().height() - split.GetInsets().height()); - EXPECT_EQ(trailing->bounds().height(), - split.bounds().height() - split.GetInsets().height()); - EXPECT_LT(leading->bounds().width() + trailing->bounds().width(), - split.bounds().width() - split.GetInsets().width()); - } else if (split.orientation() == views::SingleSplitView::VERTICAL_SPLIT) { - EXPECT_EQ(leading->bounds().width(), - split.bounds().width() - split.GetInsets().width()); - EXPECT_EQ(trailing->bounds().width(), - split.bounds().width() - split.GetInsets().width()); - EXPECT_LT(leading->bounds().height() + trailing->bounds().height(), - split.bounds().height() - split.GetInsets().height()); - } else { - NOTREACHED(); - } -} - -class SingleSplitViewListenerImpl : public views::SingleSplitViewListener { - public: - SingleSplitViewListenerImpl() : count_(0) {} - - bool SplitHandleMoved(views::SingleSplitView* sender) override { - ++count_; - return false; - } - - int count() const { return count_; } - - private: - int count_; - - DISALLOW_COPY_AND_ASSIGN(SingleSplitViewListenerImpl); -}; - -class MinimumSizedView: public views::View { - public: - MinimumSizedView(gfx::Size min_size) : min_size_(min_size) {} - - private: - gfx::Size min_size_; - gfx::Size GetMinimumSize() const override; -}; - -gfx::Size MinimumSizedView::GetMinimumSize() const { - return min_size_; -} - -} // namespace - -namespace views { - -TEST(SingleSplitViewTest, Resize) { - // Test cases to iterate through for horizontal and vertical split views. - struct TestCase { - // Split view resize policy for this test case. - bool resize_leading_on_bounds_change; - // Split view size to set. - int primary_axis_size; - int secondary_axis_size; - // Expected divider offset. - int divider_offset; - } test_cases[] = { - // The initial split size is 100x100, divider at 33. - { true, 100, 100, 33 }, - // Grow the split view, leading view should grow. - { true, 1000, 100, 933 }, - // Shrink the split view, leading view should shrink. - { true, 200, 100, 133 }, - // Minimize the split view, divider should not move. - { true, 0, 0, 133 }, - // Restore the split view, divider should not move. - { false, 500, 100, 133 }, - // Resize the split view by secondary axis, divider should not move. - { false, 500, 600, 133 } - }; - - SingleSplitView::Orientation orientations[] = { - SingleSplitView::HORIZONTAL_SPLIT, - SingleSplitView::VERTICAL_SPLIT - }; - - int borders[][4] = { - {0, 0, 0, 0}, {5, 5, 5, 5}, {10, 5, 5, 0}, - }; - - for (size_t orientation = 0; orientation < arraysize(orientations); - ++orientation) { - for (size_t border = 0; border < arraysize(borders); ++border) { - // Create a split view. - SingleSplitView split(new View(), new View(), orientations[orientation], - NULL); - - // Set initial size and divider offset. - EXPECT_EQ(test_cases[0].primary_axis_size, - test_cases[0].secondary_axis_size); - split.SetBounds(0, 0, test_cases[0].primary_axis_size, - test_cases[0].secondary_axis_size); - - if (border != 0) - split.SetBorder( - Border::CreateEmptyBorder(borders[border][0], borders[border][1], - borders[border][2], borders[border][3])); - - split.set_divider_offset(test_cases[0].divider_offset); - split.Layout(); - - // Run all test cases. - for (size_t i = 0; i < arraysize(test_cases); ++i) { - split.set_resize_leading_on_bounds_change( - test_cases[i].resize_leading_on_bounds_change); - if (split.orientation() == SingleSplitView::HORIZONTAL_SPLIT) { - split.SetBounds(0, 0, test_cases[i].primary_axis_size, - test_cases[i].secondary_axis_size); - } else { - split.SetBounds(0, 0, test_cases[i].secondary_axis_size, - test_cases[i].primary_axis_size); - } - - EXPECT_EQ(test_cases[i].divider_offset, split.divider_offset()); - VerifySplitViewLayout(split); - } - - // Special cases, one of the child views is hidden. - split.child_at(0)->SetVisible(false); - split.Layout(); - - EXPECT_EQ(split.GetContentsBounds().size(), split.child_at(1)->size()); - - split.child_at(0)->SetVisible(true); - split.child_at(1)->SetVisible(false); - split.Layout(); - - EXPECT_EQ(split.GetContentsBounds().size(), split.child_at(0)->size()); - } - } -} - -TEST(SingleSplitViewTest, MouseDrag) { - const int kMinimumChildSize = 25; - MinimumSizedView *child0 = - new MinimumSizedView(gfx::Size(5, kMinimumChildSize)); - MinimumSizedView *child1 = - new MinimumSizedView(gfx::Size(5, kMinimumChildSize)); - SingleSplitViewListenerImpl listener; - SingleSplitView split( - child0, child1, SingleSplitView::VERTICAL_SPLIT, &listener); - - const int kTotalSplitSize = 100; - split.SetBounds(0, 0, 10, kTotalSplitSize); - const int kInitialDividerOffset = 33; - const int kMouseOffset = 2; // Mouse offset in the divider. - const int kMouseMoveDelta = 7; - split.set_divider_offset(kInitialDividerOffset); - split.Layout(); - - gfx::Point press_point(7, kInitialDividerOffset + kMouseOffset); - ui::MouseEvent mouse_pressed(ui::ET_MOUSE_PRESSED, press_point, press_point, - ui::EventTimeForNow(), 0, 0); - ASSERT_TRUE(split.OnMousePressed(mouse_pressed)); - EXPECT_EQ(kInitialDividerOffset, split.divider_offset()); - EXPECT_EQ(0, listener.count()); - - // Drag divider to the bottom. - gfx::Point drag_1_point( - 5, kInitialDividerOffset + kMouseOffset + kMouseMoveDelta); - ui::MouseEvent mouse_dragged_1(ui::ET_MOUSE_DRAGGED, drag_1_point, - drag_1_point, ui::EventTimeForNow(), 0, 0); - ASSERT_TRUE(split.OnMouseDragged(mouse_dragged_1)); - EXPECT_EQ(kInitialDividerOffset + kMouseMoveDelta, split.divider_offset()); - EXPECT_EQ(1, listener.count()); - - // Drag divider to the top, beyond first child minimum size. - gfx::Point drag_2_point( - 7, kMinimumChildSize - 5); - ui::MouseEvent mouse_dragged_2(ui::ET_MOUSE_DRAGGED, drag_2_point, - drag_2_point, ui::EventTimeForNow(), 0, 0); - ASSERT_TRUE(split.OnMouseDragged(mouse_dragged_2)); - EXPECT_EQ(kMinimumChildSize, split.divider_offset()); - EXPECT_EQ(2, listener.count()); - - // Drag divider to the bottom, beyond second child minimum size. - gfx::Point drag_3_point( - 7, kTotalSplitSize - kMinimumChildSize + 5); - ui::MouseEvent mouse_dragged_3(ui::ET_MOUSE_DRAGGED, drag_3_point, - drag_3_point, ui::EventTimeForNow(), 0, 0); - ASSERT_TRUE(split.OnMouseDragged(mouse_dragged_3)); - EXPECT_EQ(kTotalSplitSize - kMinimumChildSize - split.GetDividerSize(), - split.divider_offset()); - EXPECT_EQ(3, listener.count()); - - // Drag divider between childs' minimum sizes. - gfx::Point drag_4_point( - 6, kInitialDividerOffset + kMouseOffset + kMouseMoveDelta * 2); - ui::MouseEvent mouse_dragged_4(ui::ET_MOUSE_DRAGGED, drag_4_point, - drag_4_point, ui::EventTimeForNow(), 0, 0); - ASSERT_TRUE(split.OnMouseDragged(mouse_dragged_4)); - EXPECT_EQ(kInitialDividerOffset + kMouseMoveDelta * 2, - split.divider_offset()); - EXPECT_EQ(4, listener.count()); - - gfx::Point release_point( - 7, kInitialDividerOffset + kMouseOffset + kMouseMoveDelta * 2); - ui::MouseEvent mouse_released(ui::ET_MOUSE_RELEASED, release_point, - release_point, ui::EventTimeForNow(), 0, 0); - split.OnMouseReleased(mouse_released); - EXPECT_EQ(kInitialDividerOffset + kMouseMoveDelta * 2, - split.divider_offset()); - - // Expect intial offset after a system/user gesture cancels the drag. - // This shouldn't occur after mouse release, but it's sufficient for testing. - split.OnMouseCaptureLost(); - EXPECT_EQ(kInitialDividerOffset, split.divider_offset()); - EXPECT_EQ(5, listener.count()); -} - -} // namespace views diff --git a/chromium/ui/views/controls/slider.cc b/chromium/ui/views/controls/slider.cc index c930e3c66ee..5bf9f1301d6 100644 --- a/chromium/ui/views/controls/slider.cc +++ b/chromium/ui/views/controls/slider.cc @@ -21,25 +21,13 @@ #include "ui/gfx/geometry/point.h" #include "ui/gfx/geometry/rect.h" #include "ui/resources/grit/ui_resources.h" +#include "ui/views/controls/md_slider.h" +#include "ui/views/controls/non_md_slider.h" #include "ui/views/resources/grit/views_resources.h" #include "ui/views/widget/widget.h" namespace { -const int kSlideValueChangeDurationMS = 150; - -const int kBarImagesActive[] = { - IDR_SLIDER_ACTIVE_LEFT, - IDR_SLIDER_ACTIVE_CENTER, - IDR_SLIDER_PRESSED_CENTER, - IDR_SLIDER_PRESSED_RIGHT, -}; - -const int kBarImagesDisabled[] = { - IDR_SLIDER_DISABLED_LEFT, - IDR_SLIDER_DISABLED_CENTER, - IDR_SLIDER_DISABLED_CENTER, - IDR_SLIDER_DISABLED_RIGHT, -}; +const int kSlideValueChangeDurationMs = 150; // The image chunks. enum BorderElements { @@ -55,36 +43,64 @@ namespace views { // static const char Slider::kViewClassName[] = "Slider"; -Slider::Slider(SliderListener* listener, Orientation orientation) +// static +Slider* Slider::CreateSlider(bool is_material_design, + SliderListener* listener) { + if (is_material_design) + return new MdSlider(listener); + return new NonMdSlider(listener); +} + +Slider::~Slider() { +} + +void Slider::SetValue(float value) { + SetValueInternal(value, VALUE_CHANGED_BY_API); +} + +void Slider::SetAccessibleName(const base::string16& name) { + accessible_name_ = name; +} + +Slider::Slider(SliderListener* listener) : listener_(listener), - orientation_(orientation), value_(0.f), keyboard_increment_(0.1f), - animating_value_(0.f), + initial_animating_value_(0.f), value_is_valid_(false), accessibility_events_enabled_(true), focus_border_color_(0), - bar_active_images_(kBarImagesActive), - bar_disabled_images_(kBarImagesDisabled) { + initial_button_offset_(0) { EnableCanvasFlippingForRTLUI(true); #if defined(OS_MACOSX) SetFocusBehavior(FocusBehavior::ACCESSIBLE_ONLY); #else SetFocusBehavior(FocusBehavior::ALWAYS); #endif +} - UpdateState(true); +float Slider::GetAnimatingValue() const{ + return move_animation_ && move_animation_->is_animating() + ? move_animation_->CurrentValueBetween(initial_animating_value_, + value_) + : value_; } -Slider::~Slider() { +void Slider::SetHighlighted(bool is_highlighted) {} + +void Slider::OnPaint(gfx::Canvas* canvas) { + View::OnPaint(canvas); + OnPaintFocus(canvas); } -void Slider::SetValue(float value) { - SetValueInternal(value, VALUE_CHANGED_BY_API); +void Slider::AnimationProgressed(const gfx::Animation* animation) { + if (animation == move_animation_.get()) + SchedulePaint(); } -void Slider::SetKeyboardIncrement(float increment) { - keyboard_increment_ = increment; +void Slider::AnimationEnded(const gfx::Animation* animation) { + if (animation == move_animation_.get()) + move_animation_.reset(); } void Slider::SetValueInternal(float value, SliderChangeReason reason) { @@ -105,81 +121,46 @@ void Slider::SetValueInternal(float value, SliderChangeReason reason) { if (old_value_valid && base::MessageLoop::current()) { // Do not animate when setting the value of the slider for the first time. // There is no message-loop when running tests. So we cannot animate then. - animating_value_ = old_value; - move_animation_.reset(new gfx::SlideAnimation(this)); - move_animation_->SetSlideDuration(kSlideValueChangeDurationMS); - move_animation_->Show(); - AnimationProgressed(move_animation_.get()); + if (!move_animation_) { + initial_animating_value_ = old_value; + move_animation_.reset(new gfx::SlideAnimation(this)); + move_animation_->SetSlideDuration(kSlideValueChangeDurationMs); + move_animation_->Show(); + } } else { SchedulePaint(); } - if (accessibility_events_enabled_ && GetWidget()) { - NotifyAccessibilityEvent( - ui::AX_EVENT_VALUE_CHANGED, true); - } + if (accessibility_events_enabled_ && GetWidget()) + NotifyAccessibilityEvent(ui::AX_EVENT_VALUE_CHANGED, true); } -void Slider::PrepareForMove(const gfx::Point& point) { +void Slider::PrepareForMove(const int new_x) { // Try to remember the position of the mouse cursor on the button. gfx::Insets inset = GetInsets(); gfx::Rect content = GetContentsBounds(); - float value = move_animation_.get() && move_animation_->is_animating() ? - animating_value_ : value_; + float value = GetAnimatingValue(); - // For the horizontal orientation. - const int thumb_x = value * (content.width() - thumb_->width()); + const int thumb_width = GetThumbWidth(); + const int thumb_x = value * (content.width() - thumb_width); const int candidate_x = (base::i18n::IsRTL() ? - width() - (point.x() - inset.left()) : - point.x() - inset.left()) - thumb_x; - if (candidate_x >= 0 && candidate_x < thumb_->width()) - initial_button_offset_.set_x(candidate_x); - else - initial_button_offset_.set_x(thumb_->width() / 2); - - // For the vertical orientation. - const int thumb_y = (1.0 - value) * (content.height() - thumb_->height()); - const int candidate_y = point.y() - thumb_y; - if (candidate_y >= 0 && candidate_y < thumb_->height()) - initial_button_offset_.set_y(candidate_y); + width() - (new_x - inset.left()) : + new_x - inset.left()) - thumb_x; + if (candidate_x >= 0 && candidate_x < thumb_width) + initial_button_offset_ = candidate_x; else - initial_button_offset_.set_y(thumb_->height() / 2); + initial_button_offset_ = thumb_width / 2; } void Slider::MoveButtonTo(const gfx::Point& point) { - gfx::Insets inset = GetInsets(); + const gfx::Insets inset = GetInsets(); + const int thumb_width = GetThumbWidth(); // Calculate the value. - if (orientation_ == HORIZONTAL) { - int amount = base::i18n::IsRTL() ? - width() - inset.left() - point.x() - initial_button_offset_.x() : - point.x() - inset.left() - initial_button_offset_.x(); - SetValueInternal(static_cast<float>(amount) / - (width() - inset.width() - thumb_->width()), - VALUE_CHANGED_BY_USER); - } else { - SetValueInternal( - 1.0f - static_cast<float>(point.y() - initial_button_offset_.y()) / - (height() - thumb_->height()), - VALUE_CHANGED_BY_USER); - } -} - -void Slider::UpdateState(bool control_on) { - ResourceBundle& rb = ResourceBundle::GetSharedInstance(); - if (control_on) { - thumb_ = rb.GetImageNamed(IDR_SLIDER_ACTIVE_THUMB).ToImageSkia(); - for (int i = 0; i < 4; ++i) - images_[i] = rb.GetImageNamed(bar_active_images_[i]).ToImageSkia(); - } else { - thumb_ = rb.GetImageNamed(IDR_SLIDER_DISABLED_THUMB).ToImageSkia(); - for (int i = 0; i < 4; ++i) - images_[i] = rb.GetImageNamed(bar_disabled_images_[i]).ToImageSkia(); - } - bar_height_ = images_[LEFT]->height(); - SchedulePaint(); -} - -void Slider::SetAccessibleName(const base::string16& name) { - accessible_name_ = name; + int amount = base::i18n::IsRTL() + ? width() - inset.left() - point.x() - initial_button_offset_ + : point.x() - inset.left() - initial_button_offset_; + SetValueInternal( + static_cast<float>(amount) / (width() - inset.width() - thumb_width), + VALUE_CHANGED_BY_USER); } void Slider::OnPaintFocus(gfx::Canvas* canvas) { @@ -195,6 +176,18 @@ void Slider::OnPaintFocus(gfx::Canvas* canvas) { } } +void Slider::OnSliderDragStarted() { + SetHighlighted(true); + if (listener_) + listener_->SliderDragStarted(this); +} + +void Slider::OnSliderDragEnded() { + SetHighlighted(false); + if (listener_) + listener_->SliderDragEnded(this); +} + const char* Slider::GetClassName() const { return kViewClassName; } @@ -203,87 +196,14 @@ gfx::Size Slider::GetPreferredSize() const { const int kSizeMajor = 200; const int kSizeMinor = 40; - if (orientation_ == HORIZONTAL) - return gfx::Size(std::max(width(), kSizeMajor), kSizeMinor); - return gfx::Size(kSizeMinor, std::max(height(), kSizeMajor)); -} - -void Slider::OnPaint(gfx::Canvas* canvas) { - View::OnPaint(canvas); - gfx::Rect content = GetContentsBounds(); - float value = move_animation_.get() && move_animation_->is_animating() ? - animating_value_ : value_; - if (orientation_ == HORIZONTAL) { - // Paint slider bar with image resources. - - // Inset the slider bar a little bit, so that the left or the right end of - // the slider bar will not be exposed under the thumb button when the thumb - // button slides to the left most or right most position. - const int kBarInsetX = 2; - int bar_width = content.width() - kBarInsetX * 2; - int bar_cy = content.height() / 2 - bar_height_ / 2; - - int w = content.width() - thumb_->width(); - int full = value * w; - int middle = std::max(full, images_[LEFT]->width()); - - canvas->Save(); - canvas->Translate(gfx::Vector2d(kBarInsetX, bar_cy)); - canvas->DrawImageInt(*images_[LEFT], 0, 0); - canvas->DrawImageInt(*images_[RIGHT], - bar_width - images_[RIGHT]->width(), - 0); - canvas->TileImageInt(*images_[CENTER_LEFT], - images_[LEFT]->width(), - 0, - middle - images_[LEFT]->width(), - bar_height_); - canvas->TileImageInt(*images_[CENTER_RIGHT], - middle, - 0, - bar_width - middle - images_[RIGHT]->width(), - bar_height_); - canvas->Restore(); - - // Paint slider thumb. - int button_cx = content.x() + full; - int thumb_y = content.height() / 2 - thumb_->height() / 2; - canvas->DrawImageInt(*thumb_, button_cx, thumb_y); - } else { - // TODO(jennyz): draw vertical slider bar with resources. - // TODO(sad): The painting code should use NativeTheme for various - // platforms. - const int kButtonRadius = thumb_->width() / 2; - const int kLineThickness = bar_height_ / 2; - const SkColor kFullColor = SkColorSetARGB(125, 0, 0, 0); - const SkColor kEmptyColor = SkColorSetARGB(50, 0, 0, 0); - - int h = content.height() - thumb_->height(); - int full = value * h; - int empty = h - full; - int x = content.width() / 2 - kLineThickness / 2; - canvas->FillRect(gfx::Rect(x, content.y() + kButtonRadius, - kLineThickness, empty), - kEmptyColor); - canvas->FillRect(gfx::Rect(x, content.y() + empty + 2 * kButtonRadius, - kLineThickness, full), - kFullColor); - - // TODO(mtomasz): We draw a thumb here because so far it is the same - // for horizontal and vertical orientations. If it is different, then - // we will need a separate resource. - int button_cy = content.y() + h - full; - int thumb_x = content.width() / 2 - thumb_->width() / 2; - canvas->DrawImageInt(*thumb_, thumb_x, button_cy); - } - OnPaintFocus(canvas); + return gfx::Size(std::max(width(), kSizeMajor), kSizeMinor); } bool Slider::OnMousePressed(const ui::MouseEvent& event) { if (!event.IsOnlyLeftMouseButton()) return false; OnSliderDragStarted(); - PrepareForMove(event.location()); + PrepareForMove(event.location().x()); MoveButtonTo(event.location()); return true; } @@ -298,24 +218,22 @@ void Slider::OnMouseReleased(const ui::MouseEvent& event) { } bool Slider::OnKeyPressed(const ui::KeyEvent& event) { - if (orientation_ == HORIZONTAL) { - if (event.key_code() == ui::VKEY_LEFT) { - SetValueInternal(value_ - keyboard_increment_, VALUE_CHANGED_BY_USER); - return true; - } else if (event.key_code() == ui::VKEY_RIGHT) { - SetValueInternal(value_ + keyboard_increment_, VALUE_CHANGED_BY_USER); - return true; - } - } else { - if (event.key_code() == ui::VKEY_DOWN) { - SetValueInternal(value_ - keyboard_increment_, VALUE_CHANGED_BY_USER); - return true; - } else if (event.key_code() == ui::VKEY_UP) { - SetValueInternal(value_ + keyboard_increment_, VALUE_CHANGED_BY_USER); - return true; - } - } - return false; + float new_value = value_; + if (event.key_code() == ui::VKEY_LEFT) + new_value -= keyboard_increment_; + else if (event.key_code() == ui::VKEY_RIGHT) + new_value += keyboard_increment_; + else + return false; + SetValueInternal(new_value, VALUE_CHANGED_BY_USER); + return true; +} + +void Slider::GetAccessibleState(ui::AXViewState* state) { + state->role = ui::AX_ROLE_SLIDER; + state->name = accessible_name_; + state->value = base::UTF8ToUTF16( + base::StringPrintf("%d%%", static_cast<int>(value_ * 100 + 0.5))); } void Slider::OnFocus() { @@ -334,7 +252,7 @@ void Slider::OnGestureEvent(ui::GestureEvent* event) { // an ET_GESTURE_TAP_DOWN event. case ui::ET_GESTURE_TAP_DOWN: OnSliderDragStarted(); - PrepareForMove(event->location()); + PrepareForMove(event->location().x()); // Intentional fall through to next case. case ui::ET_GESTURE_SCROLL_BEGIN: case ui::ET_GESTURE_SCROLL_UPDATE: @@ -352,26 +270,4 @@ void Slider::OnGestureEvent(ui::GestureEvent* event) { } } -void Slider::AnimationProgressed(const gfx::Animation* animation) { - animating_value_ = animation->CurrentValueBetween(animating_value_, value_); - SchedulePaint(); -} - -void Slider::GetAccessibleState(ui::AXViewState* state) { - state->role = ui::AX_ROLE_SLIDER; - state->name = accessible_name_; - state->value = base::UTF8ToUTF16( - base::StringPrintf("%d%%", static_cast<int>(value_ * 100 + 0.5))); -} - -void Slider::OnSliderDragStarted() { - if (listener_) - listener_->SliderDragStarted(this); -} - -void Slider::OnSliderDragEnded() { - if (listener_) - listener_->SliderDragEnded(this); -} - } // namespace views diff --git a/chromium/ui/views/controls/slider.h b/chromium/ui/views/controls/slider.h index 3d2090aca47..961f96ddcf4 100644 --- a/chromium/ui/views/controls/slider.h +++ b/chromium/ui/views/controls/slider.h @@ -51,20 +51,15 @@ class VIEWS_EXPORT Slider : public View, public gfx::AnimationDelegate { // Internal class name. static const char kViewClassName[]; - enum Orientation { - HORIZONTAL, - VERTICAL - }; - - Slider(SliderListener* listener, Orientation orientation); + // Based on the bool |is_material_design|, either a md version or a non-md + // version of the slider will be created. + static Slider* CreateSlider(bool is_material_design, + SliderListener* listener); ~Slider() override; float value() const { return value_; } void SetValue(float value); - // Set the delta used for changing the value via keyboard. - void SetKeyboardIncrement(float increment); - void SetAccessibleName(const base::string16& name); void set_enable_accessibility_events(bool enabled) { @@ -74,7 +69,27 @@ class VIEWS_EXPORT Slider : public View, public gfx::AnimationDelegate { void set_focus_border_color(SkColor color) { focus_border_color_ = color; } // Update UI based on control on/off state. - void UpdateState(bool control_on); + virtual void UpdateState(bool control_on) = 0; + + protected: + explicit Slider(SliderListener* listener); + + // Returns the current position of the thumb on the slider. + float GetAnimatingValue() const; + + // Shows or hides the highlight on the slider thumb. The default + // implementation does nothing. + virtual void SetHighlighted(bool is_highlighted); + + // Gets the size of the slider's thumb. + virtual int GetThumbWidth() = 0; + + // views::View: + void OnPaint(gfx::Canvas* canvas) override; + + // gfx::AnimationDelegate: + void AnimationProgressed(const gfx::Animation* animation) override; + void AnimationEnded(const gfx::Animation* animation) override; private: friend class test::SliderTestApi; @@ -84,7 +99,7 @@ class VIEWS_EXPORT Slider : public View, public gfx::AnimationDelegate { // Should be called on the Mouse Down event. Used to calculate relative // position of the mouse cursor (or the touch point) on the button to // accurately move the button using the MoveButtonTo() method. - void PrepareForMove(const gfx::Point& point); + void PrepareForMove(const int new_x); // Moves the button to the specified point and updates the value accordingly. void MoveButtonTo(const gfx::Point& point); @@ -97,10 +112,9 @@ class VIEWS_EXPORT Slider : public View, public gfx::AnimationDelegate { // Notify the listener_, if not NULL, that dragging ended. void OnSliderDragEnded(); - // views::View overrides: + // views::View: const char* GetClassName() const override; gfx::Size GetPreferredSize() const override; - void OnPaint(gfx::Canvas* canvas) override; bool OnMousePressed(const ui::MouseEvent& event) override; bool OnMouseDragged(const ui::MouseEvent& event) override; void OnMouseReleased(const ui::MouseEvent& event) override; @@ -109,24 +123,20 @@ class VIEWS_EXPORT Slider : public View, public gfx::AnimationDelegate { void OnFocus() override; void OnBlur() override; - // ui::EventHandler overrides: + // ui::EventHandler: void OnGestureEvent(ui::GestureEvent* event) override; - // gfx::AnimationDelegate overrides: - void AnimationProgressed(const gfx::Animation* animation) override; - void set_listener(SliderListener* listener) { listener_ = listener; } SliderListener* listener_; - Orientation orientation_; std::unique_ptr<gfx::SlideAnimation> move_animation_; float value_; float keyboard_increment_; - float animating_value_; + float initial_animating_value_; bool value_is_valid_; base::string16 accessible_name_; bool accessibility_events_enabled_; @@ -134,13 +144,7 @@ class VIEWS_EXPORT Slider : public View, public gfx::AnimationDelegate { // Relative position of the mouse cursor (or the touch point) on the slider's // button. - gfx::Point initial_button_offset_; - - const int* bar_active_images_; - const int* bar_disabled_images_; - const gfx::ImageSkia* thumb_; - const gfx::ImageSkia* images_[4]; - int bar_height_; + int initial_button_offset_; DISALLOW_COPY_AND_ASSIGN(Slider); }; diff --git a/chromium/ui/views/controls/slider_unittest.cc b/chromium/ui/views/controls/slider_unittest.cc index 86b1c045b9d..390d837e0ab 100644 --- a/chromium/ui/views/controls/slider_unittest.cc +++ b/chromium/ui/views/controls/slider_unittest.cc @@ -16,8 +16,10 @@ #include "testing/gtest/include/gtest/gtest.h" #include "ui/events/event.h" #include "ui/events/gesture_event_details.h" +#include "ui/events/keycodes/keyboard_codes.h" #include "ui/events/test/event_generator.h" #include "ui/views/test/slider_test_api.h" +#include "ui/views/test/test_slider.h" #include "ui/views/test/views_test_base.h" #include "ui/views/view.h" #include "ui/views/widget/widget.h" @@ -122,7 +124,7 @@ namespace views { // Base test fixture for Slider tests. class SliderTest : public views::ViewsTestBase { public: - explicit SliderTest(Slider::Orientation orientation); + SliderTest(); ~SliderTest() override; protected: @@ -153,8 +155,6 @@ class SliderTest : public views::ViewsTestBase { } private: - // The Slider's orientation - Slider::Orientation orientation_; // The Slider to be tested. Slider* slider_; // A simple SliderListener test double. @@ -174,13 +174,8 @@ class SliderTest : public views::ViewsTestBase { DISALLOW_COPY_AND_ASSIGN(SliderTest); }; -SliderTest::SliderTest(Slider::Orientation orientation) - : orientation_(orientation), - slider_(NULL), - default_locale_(""), - max_x_(0), - max_y_(0) { -} +SliderTest::SliderTest() + : slider_(NULL), default_locale_(), max_x_(0), max_y_(0) {} SliderTest::~SliderTest() { } @@ -188,7 +183,7 @@ SliderTest::~SliderTest() { void SliderTest::SetUp() { views::ViewsTestBase::SetUp(); - slider_ = new Slider(NULL, orientation_); + slider_ = new TestSlider(nullptr); View* view = slider_; gfx::Size size = view->GetPreferredSize(); view->SetSize(size); @@ -234,30 +229,11 @@ class HorizontalSliderTest : public SliderTest { DISALLOW_COPY_AND_ASSIGN(HorizontalSliderTest); }; -HorizontalSliderTest::HorizontalSliderTest() - : SliderTest(Slider::HORIZONTAL) { -} +HorizontalSliderTest::HorizontalSliderTest() : SliderTest() {} HorizontalSliderTest::~HorizontalSliderTest() { } -// Test fixture for vertically oriented slider tests. -class VerticalSliderTest : public SliderTest { - public: - VerticalSliderTest(); - ~VerticalSliderTest() override; - - private: - DISALLOW_COPY_AND_ASSIGN(VerticalSliderTest); -}; - -VerticalSliderTest::VerticalSliderTest() - : SliderTest(Slider::VERTICAL) { -} - -VerticalSliderTest::~VerticalSliderTest() { -} - TEST_F(HorizontalSliderTest, UpdateFromClickHorizontal) { ClickAt(0, 0); EXPECT_EQ(0.0f, slider()->value()); @@ -266,13 +242,6 @@ TEST_F(HorizontalSliderTest, UpdateFromClickHorizontal) { EXPECT_EQ(1.0f, slider()->value()); } -TEST_F(VerticalSliderTest, UpdateFromClickVertical) { - ClickAt(0, 0); - EXPECT_EQ(1.0f, slider()->value()); - - ClickAt(0, max_y()); - EXPECT_EQ(0.0f, slider()->value()); -} TEST_F(HorizontalSliderTest, UpdateFromClickRTLHorizontal) { base::i18n::SetICUDefaultLocale("he"); @@ -319,10 +288,8 @@ TEST_F(HorizontalSliderTest, SliderValueForScrollGesture) { // Scroll above the maximum. slider()->SetValue(0.5); event_generator()->GestureScrollSequence( - gfx::Point(0.5 * max_x(), 0.5 * max_y()), - gfx::Point(max_x(), max_y()), - base::TimeDelta::FromMilliseconds(10), - 5 /* steps */); + gfx::Point(0.5 * max_x(), 0.5 * max_y()), gfx::Point(max_x(), max_y()), + base::TimeDelta::FromMilliseconds(10), 5 /* steps */); EXPECT_EQ(1, slider()->value()); // Scroll somewhere in the middle. @@ -335,6 +302,19 @@ TEST_F(HorizontalSliderTest, SliderValueForScrollGesture) { EXPECT_NEAR(0.75, slider()->value(), 0.03); } +// Test the slider location by adjusting it using keyboard. +TEST_F(HorizontalSliderTest, SliderValueForKeyboard) { + float value =0.5; + slider()->SetValue(value); + slider()->RequestFocus(); + event_generator()->PressKey(ui::VKEY_RIGHT, 0); + EXPECT_GT(slider()->value(), value); + + slider()->SetValue(value); + event_generator()->PressKey(ui::VKEY_LEFT, 0); + EXPECT_LT(slider()->value(), value); +} + // Verifies the correct SliderListener events are raised for a tap gesture. TEST_F(HorizontalSliderTest, SliderListenerEventsForTapGesture) { test::SliderTestApi slider_test_api(slider()); diff --git a/chromium/ui/views/controls/tabbed_pane/tabbed_pane.cc b/chromium/ui/views/controls/tabbed_pane/tabbed_pane.cc index 0be0af67873..07fe5a69545 100644 --- a/chromium/ui/views/controls/tabbed_pane/tabbed_pane.cc +++ b/chromium/ui/views/controls/tabbed_pane/tabbed_pane.cc @@ -10,12 +10,16 @@ #include "third_party/skia/include/core/SkPath.h" #include "ui/accessibility/ax_view_state.h" #include "ui/base/default_style.h" +#include "ui/base/material_design/material_design_controller.h" #include "ui/base/resource/resource_bundle.h" #include "ui/events/keycodes/keyboard_codes.h" #include "ui/gfx/canvas.h" #include "ui/gfx/font_list.h" +#include "ui/native_theme/native_theme.h" +#include "ui/views/border.h" #include "ui/views/controls/label.h" #include "ui/views/controls/tabbed_pane/tabbed_pane_listener.h" +#include "ui/views/layout/box_layout.h" #include "ui/views/layout/layout_manager.h" #include "ui/views/widget/widget.h" @@ -32,6 +36,9 @@ const gfx::Font::Weight kHoverWeight = gfx::Font::Weight::NORMAL; const gfx::Font::Weight kActiveWeight = gfx::Font::Weight::BOLD; const gfx::Font::Weight kInactiveWeight = gfx::Font::Weight::NORMAL; +const int kHarmonyTabStripVerticalPad = 16; +const int kHarmonyTabStripTabHeight = 40; + } // namespace namespace views { @@ -62,6 +69,12 @@ class Tab : public View { void Layout() override; const char* GetClassName() const override; + protected: + Label* title() { return title_; } + + // Called whenever |tab_state_| changes. + virtual void OnStateChanged(); + private: enum TabState { TAB_INACTIVE, @@ -81,6 +94,19 @@ class Tab : public View { DISALLOW_COPY_AND_ASSIGN(Tab); }; +// A subclass of Tab that implements the Harmony visual styling. +class MdTab : public Tab { + public: + MdTab(TabbedPane* tabbed_pane, const base::string16& title, View* contents); + ~MdTab() override; + + // Overridden from Tab: + void OnStateChanged() override; + + private: + DISALLOW_COPY_AND_ASSIGN(MdTab); +}; + // The tab strip shown above the tab contents. class TabStrip : public View { public: @@ -102,6 +128,22 @@ class TabStrip : public View { DISALLOW_COPY_AND_ASSIGN(TabStrip); }; +// A subclass of TabStrip that implements the Harmony visual styling. This +// class uses a BoxLayout to position tabs. +class MdTabStrip : public TabStrip { + public: + explicit MdTabStrip(TabbedPane* tabbed_pane); + ~MdTabStrip() override; + + // Overridden from View: + gfx::Size GetPreferredSize() const override; + void Layout() override; + void OnPaint(gfx::Canvas* canvas) override; + + private: + DISALLOW_COPY_AND_ASSIGN(MdTabStrip); +}; + // static const char Tab::kViewClassName[] = "Tab"; @@ -129,6 +171,27 @@ void Tab::SetSelected(bool selected) { SetState(selected ? TAB_ACTIVE : TAB_INACTIVE); } +void Tab::OnStateChanged() { + ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance(); + switch (tab_state_) { + case TAB_INACTIVE: + title_->SetEnabledColor(kTabTitleColor_Inactive); + title_->SetFontList(rb.GetFontListWithDelta( + ui::kLabelFontSizeDelta, gfx::Font::NORMAL, kInactiveWeight)); + break; + case TAB_ACTIVE: + title_->SetEnabledColor(kTabTitleColor_Active); + title_->SetFontList(rb.GetFontListWithDelta( + ui::kLabelFontSizeDelta, gfx::Font::NORMAL, kActiveWeight)); + break; + case TAB_HOVERED: + title_->SetEnabledColor(kTabTitleColor_Hovered); + title_->SetFontList(rb.GetFontListWithDelta( + ui::kLabelFontSizeDelta, gfx::Font::NORMAL, kHoverWeight)); + break; + } +} + bool Tab::OnMousePressed(const ui::MouseEvent& event) { if (event.IsOnlyLeftMouseButton() && GetLocalBounds().Contains(event.location())) @@ -185,26 +248,42 @@ void Tab::SetState(TabState tab_state) { if (tab_state == tab_state_) return; tab_state_ = tab_state; + OnStateChanged(); + SchedulePaint(); +} + +MdTab::MdTab(TabbedPane* tabbed_pane, + const base::string16& title, + View* contents) + : Tab(tabbed_pane, title, contents) { + OnStateChanged(); +} + +MdTab::~MdTab() {} + +void MdTab::OnStateChanged() { + ui::NativeTheme* theme = GetNativeTheme(); + SkColor border_color = theme->GetSystemColor( + selected() ? ui::NativeTheme::kColorId_FocusedBorderColor + : ui::NativeTheme::kColorId_UnfocusedBorderColor); + int border_thickness = selected() ? 2 : 1; + SetBorder( + Border::CreateSolidSidedBorder(0, 0, border_thickness, 0, border_color)); + + SkColor font_color = selected() + ? theme->GetSystemColor(ui::NativeTheme::kColorId_ProminentButtonColor) + : theme->GetSystemColor(ui::NativeTheme::kColorId_ButtonEnabledColor); + title()->SetEnabledColor(font_color); + + gfx::Font::Weight font_weight = gfx::Font::Weight::MEDIUM; +#if defined(OS_WIN) + if (selected()) + font_weight = gfx::Font::Weight::BOLD; +#endif ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance(); - switch (tab_state) { - case TAB_INACTIVE: - title_->SetEnabledColor(kTabTitleColor_Inactive); - title_->SetFontList(rb.GetFontListWithDelta( - ui::kLabelFontSizeDelta, gfx::Font::NORMAL, kInactiveWeight)); - break; - case TAB_ACTIVE: - title_->SetEnabledColor(kTabTitleColor_Active); - title_->SetFontList(rb.GetFontListWithDelta( - ui::kLabelFontSizeDelta, gfx::Font::NORMAL, kActiveWeight)); - break; - case TAB_HOVERED: - title_->SetEnabledColor(kTabTitleColor_Hovered); - title_->SetFontList(rb.GetFontListWithDelta( - ui::kLabelFontSizeDelta, gfx::Font::NORMAL, kHoverWeight)); - break; - } - SchedulePaint(); + title()->SetFontList(rb.GetFontListWithDelta(ui::kLabelFontSizeDelta, + gfx::Font::NORMAL, font_weight)); } // static @@ -273,11 +352,40 @@ void TabStrip::OnPaint(gfx::Canvas* canvas) { } } +MdTabStrip::MdTabStrip(TabbedPane* tabbed_pane) : TabStrip(tabbed_pane) { + BoxLayout* layout = + new BoxLayout(BoxLayout::kHorizontal, 0, kHarmonyTabStripVerticalPad, 0); + layout->set_main_axis_alignment(BoxLayout::MAIN_AXIS_ALIGNMENT_CENTER); + layout->set_cross_axis_alignment(BoxLayout::CROSS_AXIS_ALIGNMENT_STRETCH); + layout->SetDefaultFlex(1); + SetLayoutManager(layout); +} + +MdTabStrip::~MdTabStrip() {} + +gfx::Size MdTabStrip::GetPreferredSize() const { + return gfx::Size(width(), + kHarmonyTabStripVerticalPad * 2 + kHarmonyTabStripTabHeight); +} + +// Let this class's LayoutManager handle the layout. +void MdTabStrip::Layout() { + return View::Layout(); +} + +// The tab strip "border" is drawn as part of the tabs, so all this method needs +// to do is paint the background. +void MdTabStrip::OnPaint(gfx::Canvas* canvas) { + OnPaintBackground(canvas); +} + TabbedPane::TabbedPane() - : listener_(NULL), - tab_strip_(new TabStrip(this)), - contents_(new View()), - selected_tab_index_(-1) { + : listener_(NULL), + tab_strip_(ui::MaterialDesignController::IsSecondaryUiMaterial() + ? new MdTabStrip(this) + : new TabStrip(this)), + contents_(new View()), + selected_tab_index_(-1) { #if defined(OS_MACOSX) SetFocusBehavior(FocusBehavior::ACCESSIBLE_ONLY); #else @@ -310,7 +418,11 @@ void TabbedPane::AddTabAtIndex(int index, DCHECK(index >= 0 && index <= GetTabCount()); contents->SetVisible(false); - tab_strip_->AddChildViewAt(new Tab(this, title, contents), index); + tab_strip_->AddChildViewAt( + ui::MaterialDesignController::IsSecondaryUiMaterial() + ? new MdTab(this, title, contents) + : new Tab(this, title, contents), + index); contents_->AddChildViewAt(contents, index); if (selected_tab_index() < 0) SelectTabAt(index); diff --git a/chromium/ui/views/controls/tabbed_pane/tabbed_pane.h b/chromium/ui/views/controls/tabbed_pane/tabbed_pane.h index 76d927ac564..aa808acadcb 100644 --- a/chromium/ui/views/controls/tabbed_pane/tabbed_pane.h +++ b/chromium/ui/views/controls/tabbed_pane/tabbed_pane.h @@ -58,18 +58,18 @@ class VIEWS_EXPORT TabbedPane : public View { const char* GetClassName() const override; private: - friend class TabStrip; + friend class TabStrip; - // Get the Tab (the tabstrip view, not its content) at the valid |index|. - Tab* GetTabAt(int index); + // Get the Tab (the tabstrip view, not its content) at the valid |index|. + Tab* GetTabAt(int index); // Overridden from View: - void Layout() override; - void ViewHierarchyChanged( - const ViewHierarchyChangedDetails& details) override; - bool AcceleratorPressed(const ui::Accelerator& accelerator) override; - void OnFocus() override; - void GetAccessibleState(ui::AXViewState* state) override; + void Layout() override; + void ViewHierarchyChanged( + const ViewHierarchyChangedDetails& details) override; + bool AcceleratorPressed(const ui::Accelerator& accelerator) override; + void OnFocus() override; + void GetAccessibleState(ui::AXViewState* state) override; // A listener notified when tab selection changes. Weak, not owned. TabbedPaneListener* listener_; diff --git a/chromium/ui/views/controls/table/table_view.cc b/chromium/ui/views/controls/table/table_view.cc index 9ef4fe95a81..9ce94cc4519 100644 --- a/chromium/ui/views/controls/table/table_view.cc +++ b/chromium/ui/views/controls/table/table_view.cc @@ -234,15 +234,20 @@ void TableView::SetColumnVisibility(int id, bool is_visible) { void TableView::ToggleSortOrder(int visible_column_index) { DCHECK(visible_column_index >= 0 && visible_column_index < static_cast<int>(visible_columns_.size())); - if (!visible_columns_[visible_column_index].column.sortable) + const ui::TableColumn& column = visible_columns_[visible_column_index].column; + if (!column.sortable) return; - const int column_id = visible_columns_[visible_column_index].column.id; SortDescriptors sort(sort_descriptors_); - if (!sort.empty() && sort[0].column_id == column_id) { - sort[0].ascending = !sort[0].ascending; + if (!sort.empty() && sort[0].column_id == column.id) { + if (sort[0].ascending == column.initial_sort_is_ascending) { + // First toggle inverts the order. + sort[0].ascending = !sort[0].ascending; + } else { + // Second toggle clears the sort. + sort.clear(); + } } else { - SortDescriptor descriptor(column_id, visible_columns_[ - visible_column_index].column.initial_sort_is_ascending); + SortDescriptor descriptor(column.id, column.initial_sort_is_ascending); sort.insert(sort.begin(), descriptor); // Only persist two sort descriptors. if (sort.size() > 2) @@ -251,6 +256,13 @@ void TableView::ToggleSortOrder(int visible_column_index) { SetSortDescriptors(sort); } +void TableView::SetSortDescriptors(const SortDescriptors& sort_descriptors) { + sort_descriptors_ = sort_descriptors; + SortItemsAndUpdateMapping(); + if (header_) + header_->SchedulePaint(); +} + bool TableView::IsColumnVisible(int id) const { for (size_t i = 0; i < visible_columns_.size(); ++i) { if (visible_columns_[i].column.id == id) @@ -640,13 +652,6 @@ void TableView::NumRowsChanged() { SchedulePaint(); } -void TableView::SetSortDescriptors(const SortDescriptors& sort_descriptors) { - sort_descriptors_ = sort_descriptors; - SortItemsAndUpdateMapping(); - if (header_) - header_->SchedulePaint(); -} - void TableView::SortItemsAndUpdateMapping() { if (!is_sorted()) { view_to_model_.clear(); diff --git a/chromium/ui/views/controls/table/table_view.h b/chromium/ui/views/controls/table/table_view.h index fc7f028164b..3c2e954e737 100644 --- a/chromium/ui/views/controls/table/table_view.h +++ b/chromium/ui/views/controls/table/table_view.h @@ -154,10 +154,17 @@ class VIEWS_EXPORT TableView // Sets the width of the column. |index| is in terms of |visible_columns_|. void SetVisibleColumnWidth(int index, int width); - // Toggles the sort order of the specified visible column index. + // Modify the table sort order, depending on a clicked column and the previous + // table sort order. Does nothing if this column is not sortable. + // + // When called repeatedly on the same sortable column, the sort order will + // cycle through three states in order: sorted -> reverse-sorted -> unsorted. + // When switching from one sort column to another, the previous sort column + // will be remembered and used as a secondary sort key. void ToggleSortOrder(int visible_column_index); const SortDescriptors& sort_descriptors() const { return sort_descriptors_; } + void SetSortDescriptors(const SortDescriptors& descriptors); bool is_sorted() const { return !sort_descriptors_.empty(); } // Maps from the index in terms of the model to that of the view. @@ -227,9 +234,6 @@ class VIEWS_EXPORT TableView // Invoked when the number of rows changes in some way. void NumRowsChanged(); - // Resets the sort descriptions. - void SetSortDescriptors(const SortDescriptors& sort_descriptors); - // Does the actual sort and updates the mappings (|view_to_model_| and // |model_to_view_|) appropriately. void SortItemsAndUpdateMapping(); diff --git a/chromium/ui/views/controls/table/table_view_unittest.cc b/chromium/ui/views/controls/table/table_view_unittest.cc index 9fb6bb0fc2d..c94994cd819 100644 --- a/chromium/ui/views/controls/table/table_view_unittest.cc +++ b/chromium/ui/views/controls/table/table_view_unittest.cc @@ -161,6 +161,29 @@ std::string GetModelToViewAsString(TableView* table) { return result; } +// Formats the whole table as a string, like: "[a, b, c], [d, e, f]". Rows +// scrolled out of view are included; hidden columns are excluded. +std::string GetRowsInViewOrderAsString(TableView* table) { + std::string result; + for (int i = 0; i < table->RowCount(); ++i) { + if (i != 0) + result += ", "; // Comma between each row. + + // Format row |i| like this: "[value1, value2, value3]" + result += "["; + for (size_t j = 0; j < table->visible_columns().size(); ++j) { + const ui::TableColumn& column = table->visible_columns()[j].column; + if (j != 0) + result += ", "; // Comma between each value in the row. + + result += base::UTF16ToUTF8( + table->model()->GetText(table->ViewToModel(i), column.id)); + } + result += "]"; + } + return result; +} + class TestTableView : public TableView { public: TestTableView(ui::TableModel* model, @@ -335,6 +358,13 @@ TEST_F(TableViewTest, ResizeViaGesture) { // Assertions for table sorting. TEST_F(TableViewTest, Sort) { + // Initial ordering. + EXPECT_TRUE(table_->sort_descriptors().empty()); + EXPECT_EQ("0 1 2 3", GetViewToModelAsString(table_)); + EXPECT_EQ("0 1 2 3", GetModelToViewAsString(table_)); + EXPECT_EQ("[0, 1], [1, 1], [2, 2], [3, 0]", + GetRowsInViewOrderAsString(table_)); + // Toggle the sort order of the first column, shouldn't change anything. table_->ToggleSortOrder(0); ASSERT_EQ(1u, table_->sort_descriptors().size()); @@ -342,46 +372,119 @@ TEST_F(TableViewTest, Sort) { EXPECT_TRUE(table_->sort_descriptors()[0].ascending); EXPECT_EQ("0 1 2 3", GetViewToModelAsString(table_)); EXPECT_EQ("0 1 2 3", GetModelToViewAsString(table_)); + EXPECT_EQ("[0, 1], [1, 1], [2, 2], [3, 0]", + GetRowsInViewOrderAsString(table_)); - // Invert the sort (first column descending). + // Toggle the sort (first column descending). table_->ToggleSortOrder(0); ASSERT_EQ(1u, table_->sort_descriptors().size()); EXPECT_EQ(0, table_->sort_descriptors()[0].column_id); EXPECT_FALSE(table_->sort_descriptors()[0].ascending); EXPECT_EQ("3 2 1 0", GetViewToModelAsString(table_)); EXPECT_EQ("3 2 1 0", GetModelToViewAsString(table_)); + EXPECT_EQ("[3, 0], [2, 2], [1, 1], [0, 1]", + GetRowsInViewOrderAsString(table_)); - // Change cell 0x3 to -1, meaning we have 0, 1, 2, -1 (in the first column). + // Change the [3, 0] cell to [-1, 0]. This should move it to the back of + // the current sort order. model_->ChangeRow(3, -1, 0); ASSERT_EQ(1u, table_->sort_descriptors().size()); EXPECT_EQ(0, table_->sort_descriptors()[0].column_id); EXPECT_FALSE(table_->sort_descriptors()[0].ascending); EXPECT_EQ("2 1 0 3", GetViewToModelAsString(table_)); EXPECT_EQ("2 1 0 3", GetModelToViewAsString(table_)); + EXPECT_EQ("[2, 2], [1, 1], [0, 1], [-1, 0]", + GetRowsInViewOrderAsString(table_)); - // Invert sort again (first column ascending). + // Toggle the sort again, to clear the sort and restore the model ordering. + table_->ToggleSortOrder(0); + EXPECT_TRUE(table_->sort_descriptors().empty()); + EXPECT_EQ("0 1 2 3", GetViewToModelAsString(table_)); + EXPECT_EQ("0 1 2 3", GetModelToViewAsString(table_)); + EXPECT_EQ("[0, 1], [1, 1], [2, 2], [-1, 0]", + GetRowsInViewOrderAsString(table_)); + + // Toggle the sort again (first column ascending). table_->ToggleSortOrder(0); ASSERT_EQ(1u, table_->sort_descriptors().size()); EXPECT_EQ(0, table_->sort_descriptors()[0].column_id); EXPECT_TRUE(table_->sort_descriptors()[0].ascending); EXPECT_EQ("3 0 1 2", GetViewToModelAsString(table_)); EXPECT_EQ("1 2 3 0", GetModelToViewAsString(table_)); + EXPECT_EQ("[-1, 0], [0, 1], [1, 1], [2, 2]", + GetRowsInViewOrderAsString(table_)); - // Add a row so that model has 0, 3, 1, 2, -1. + // Add a row that's second in the model order, but last in the active sort + // order. model_->AddRow(1, 3, 4); ASSERT_EQ(1u, table_->sort_descriptors().size()); EXPECT_EQ(0, table_->sort_descriptors()[0].column_id); EXPECT_TRUE(table_->sort_descriptors()[0].ascending); EXPECT_EQ("4 0 2 3 1", GetViewToModelAsString(table_)); EXPECT_EQ("1 4 2 3 0", GetModelToViewAsString(table_)); + EXPECT_EQ("[-1, 0], [0, 1], [1, 1], [2, 2], [3, 4]", + GetRowsInViewOrderAsString(table_)); - // Delete the first row, ending up with 3, 1, 2, -1. - model_->RemoveRow(0); + // Add a row that's last in the model order but second in the the active sort + // order. + model_->AddRow(5, -1, 20); ASSERT_EQ(1u, table_->sort_descriptors().size()); EXPECT_EQ(0, table_->sort_descriptors()[0].column_id); EXPECT_TRUE(table_->sort_descriptors()[0].ascending); - EXPECT_EQ("3 1 2 0", GetViewToModelAsString(table_)); - EXPECT_EQ("3 1 2 0", GetModelToViewAsString(table_)); + EXPECT_EQ("4 5 0 2 3 1", GetViewToModelAsString(table_)); + EXPECT_EQ("2 5 3 4 0 1", GetModelToViewAsString(table_)); + EXPECT_EQ("[-1, 0], [-1, 20], [0, 1], [1, 1], [2, 2], [3, 4]", + GetRowsInViewOrderAsString(table_)); + + // Click the first column again, then click the second column. This should + // yield an ordering of second column ascending, with the first column + // descending as a tiebreaker. + table_->ToggleSortOrder(0); + table_->ToggleSortOrder(1); + ASSERT_EQ(2u, table_->sort_descriptors().size()); + EXPECT_EQ(1, table_->sort_descriptors()[0].column_id); + EXPECT_TRUE(table_->sort_descriptors()[0].ascending); + EXPECT_EQ(0, table_->sort_descriptors()[1].column_id); + EXPECT_FALSE(table_->sort_descriptors()[1].ascending); + EXPECT_EQ("4 2 0 3 1 5", GetViewToModelAsString(table_)); + EXPECT_EQ("2 4 1 3 0 5", GetModelToViewAsString(table_)); + EXPECT_EQ("[-1, 0], [1, 1], [0, 1], [2, 2], [3, 4], [-1, 20]", + GetRowsInViewOrderAsString(table_)); + + // Toggle the current column to change from ascending to descending. This + // should result in an almost-reversal of the previous order, except for the + // two rows with the same value for the second column. + table_->ToggleSortOrder(1); + ASSERT_EQ(2u, table_->sort_descriptors().size()); + EXPECT_EQ(1, table_->sort_descriptors()[0].column_id); + EXPECT_FALSE(table_->sort_descriptors()[0].ascending); + EXPECT_EQ(0, table_->sort_descriptors()[1].column_id); + EXPECT_FALSE(table_->sort_descriptors()[1].ascending); + EXPECT_EQ("5 1 3 2 0 4", GetViewToModelAsString(table_)); + EXPECT_EQ("4 1 3 2 5 0", GetModelToViewAsString(table_)); + EXPECT_EQ("[-1, 20], [3, 4], [2, 2], [1, 1], [0, 1], [-1, 0]", + GetRowsInViewOrderAsString(table_)); + + // Delete the [0, 1] row from the model. It's at model index zero. + model_->RemoveRow(0); + ASSERT_EQ(2u, table_->sort_descriptors().size()); + EXPECT_EQ(1, table_->sort_descriptors()[0].column_id); + EXPECT_FALSE(table_->sort_descriptors()[0].ascending); + EXPECT_EQ(0, table_->sort_descriptors()[1].column_id); + EXPECT_FALSE(table_->sort_descriptors()[1].ascending); + EXPECT_EQ("4 0 2 1 3", GetViewToModelAsString(table_)); + EXPECT_EQ("1 3 2 4 0", GetModelToViewAsString(table_)); + EXPECT_EQ("[-1, 20], [3, 4], [2, 2], [1, 1], [-1, 0]", + GetRowsInViewOrderAsString(table_)); + + // Toggle the current sort column again. This should clear both the primary + // and secondary sort descriptor. + table_->ToggleSortOrder(1); + EXPECT_TRUE(table_->sort_descriptors().empty()); + EXPECT_EQ("0 1 2 3 4", GetViewToModelAsString(table_)); + EXPECT_EQ("0 1 2 3 4", GetModelToViewAsString(table_)); + EXPECT_EQ("[3, 4], [1, 1], [2, 2], [-1, 0], [-1, 20]", + GetRowsInViewOrderAsString(table_)); } // Verfies clicking on the header sorts. @@ -501,7 +604,13 @@ TEST_F(TableViewTest, Grouping) { EXPECT_EQ("0 1 2 3", GetViewToModelAsString(table_)); EXPECT_EQ("0 1 2 3", GetModelToViewAsString(table_)); - // Toggle to ascending sort. + // Toggle to clear the sort. + table_->ToggleSortOrder(0); + EXPECT_TRUE(table_->sort_descriptors().empty()); + EXPECT_EQ("0 1 2 3", GetViewToModelAsString(table_)); + EXPECT_EQ("0 1 2 3", GetModelToViewAsString(table_)); + + // Toggle again to effect an ascending sort. table_->ToggleSortOrder(0); ASSERT_EQ(1u, table_->sort_descriptors().size()); EXPECT_EQ(0, table_->sort_descriptors()[0].column_id); diff --git a/chromium/ui/views/controls/textfield/textfield.cc b/chromium/ui/views/controls/textfield/textfield.cc index fa1025ec784..3698349384b 100644 --- a/chromium/ui/views/controls/textfield/textfield.cc +++ b/chromium/ui/views/controls/textfield/textfield.cc @@ -17,6 +17,7 @@ #include "ui/base/dragdrop/drag_utils.h" #include "ui/base/ime/input_method.h" #include "ui/base/ime/text_edit_commands.h" +#include "ui/base/material_design/material_design_controller.h" #include "ui/base/resource/resource_bundle.h" #include "ui/base/ui_base_switches_util.h" #include "ui/compositor/canvas_painter.h" @@ -32,6 +33,7 @@ #include "ui/native_theme/native_theme.h" #include "ui/strings/grit/ui_strings.h" #include "ui/views/background.h" +#include "ui/views/controls/focus_ring.h" #include "ui/views/controls/focusable_border.h" #include "ui/views/controls/label.h" #include "ui/views/controls/menu/menu_runner.h" @@ -60,6 +62,18 @@ namespace views { namespace { +#if defined(OS_MACOSX) +const gfx::SelectionBehavior kLineSelectionBehavior = gfx::SELECTION_EXTEND; +const gfx::SelectionBehavior kWordSelectionBehavior = gfx::SELECTION_CARET; +const gfx::SelectionBehavior kMoveParagraphSelectionBehavior = + gfx::SELECTION_CARET; +#else +const gfx::SelectionBehavior kLineSelectionBehavior = gfx::SELECTION_RETAIN; +const gfx::SelectionBehavior kWordSelectionBehavior = gfx::SELECTION_RETAIN; +const gfx::SelectionBehavior kMoveParagraphSelectionBehavior = + gfx::SELECTION_RETAIN; +#endif + // Default placeholder text color. const SkColor kDefaultPlaceholderTextColor = SK_ColorLTGRAY; @@ -199,6 +213,13 @@ ui::TextEditCommand GetTextEditCommandFromMenuCommand(int command_id, return ui::TextEditCommand::INVALID_COMMAND; } +base::TimeDelta GetPasswordRevealDuration() { + return ViewsDelegate::GetInstance() + ? ViewsDelegate::GetInstance() + ->GetTextfieldPasswordRevealDuration() + : base::TimeDelta(); +} + } // namespace // static @@ -235,7 +256,6 @@ Textfield::Textfield() text_input_flags_(0), performing_user_action_(false), skip_input_method_cancel_composition_(false), - cursor_visible_(false), drop_cursor_visible_(false), initiating_drag_(false), aggregated_clicks_(0), @@ -248,12 +268,6 @@ Textfield::Textfield() SetBorder(std::unique_ptr<Border>(new FocusableBorder())); SetFocusBehavior(FocusBehavior::ALWAYS); - if (ViewsDelegate::GetInstance()) { - password_reveal_duration_ = - ViewsDelegate::GetInstance() - ->GetDefaultTextfieldObscuredRevealDuration(); - } - // These allow BrowserView to pass edit commands from the Chrome menu to us // when we're focused by simply asking the FocusManager to // ProcessAccelerator() with the relevant accelerators. @@ -310,8 +324,7 @@ void Textfield::InsertOrReplaceText(const base::string16& new_text) { if (new_text.empty()) return; model_->InsertText(new_text); - OnCaretBoundsChanged(); - SchedulePaint(); + UpdateAfterChange(true, true); } base::string16 Textfield::GetSelectedText() const { @@ -343,7 +356,7 @@ SkColor Textfield::GetTextColor() const { if (!use_default_text_color_) return text_color_; - return GetNativeTheme()->GetSystemColor(read_only() ? + return GetNativeTheme()->GetSystemColor(read_only() || !enabled() ? ui::NativeTheme::kColorId_TextfieldReadOnlyColor : ui::NativeTheme::kColorId_TextfieldDefaultColor); } @@ -363,7 +376,7 @@ SkColor Textfield::GetBackgroundColor() const { if (!use_default_background_color_) return background_color_; - return GetNativeTheme()->GetSystemColor(read_only() ? + return GetNativeTheme()->GetSystemColor(read_only() || !enabled() ? ui::NativeTheme::kColorId_TextfieldReadOnlyBackground : ui::NativeTheme::kColorId_TextfieldDefaultBackground); } @@ -604,12 +617,9 @@ bool Textfield::OnMousePressed(const ui::MouseEvent& event) { bool Textfield::OnMouseDragged(const ui::MouseEvent& event) { last_drag_location_ = event.location(); - // Don't adjust the cursor on a potential drag and drop, or if the mouse - // movement from the last mouse click does not exceed the drag threshold. - if (initiating_drag_ || !event.IsOnlyLeftMouseButton() || - !ExceededDragThreshold(last_drag_location_ - last_click_location_)) { + // Don't adjust the cursor on a potential drag and drop. + if (initiating_drag_ || !event.IsOnlyLeftMouseButton()) return true; - } // A timer is used to continuously scroll while selecting beyond side edges. const int x = event.location().x(); @@ -639,6 +649,10 @@ void Textfield::OnMouseReleased(const ui::MouseEvent& event) { OnAfterUserAction(); } +WordLookupClient* Textfield::GetWordLookupClient() { + return this; +} + bool Textfield::OnKeyPressed(const ui::KeyEvent& event) { ui::TextEditCommand edit_command = scheduled_text_edit_command_; scheduled_text_edit_command_ = ui::TextEditCommand::INVALID_COMMAND; @@ -674,6 +688,9 @@ bool Textfield::OnKeyPressed(const ui::KeyEvent& event) { ExecuteTextEditCommand(edit_command); handled = true; } + + if (!handled) + OnKeypressUnhandled(); return handled; } @@ -841,6 +858,8 @@ int Textfield::OnDragUpdated(const ui::DropTargetEvent& event) { OnCaretBoundsChanged(); SchedulePaint(); + StopBlinkingCursor(); + if (initiating_drag_) { if (in_selection) return ui::DragDropTypes::DRAG_NONE; @@ -852,6 +871,8 @@ int Textfield::OnDragUpdated(const ui::DropTargetEvent& event) { void Textfield::OnDragExited() { drop_cursor_visible_ = false; + if (ShouldBlinkCursor()) + StartBlinkingCursor(); SchedulePaint(); } @@ -963,30 +984,28 @@ void Textfield::OnPaint(gfx::Canvas* canvas) { void Textfield::OnFocus() { GetRenderText()->set_focused(true); - cursor_visible_ = true; + if (ShouldShowCursor()) + GetRenderText()->set_cursor_visible(true); SchedulePaint(); if (GetInputMethod()) GetInputMethod()->SetFocusedTextInputClient(this); OnCaretBoundsChanged(); - - const size_t caret_blink_ms = Textfield::GetCaretBlinkMs(); - if (caret_blink_ms != 0) { - cursor_repaint_timer_.Start(FROM_HERE, - base::TimeDelta::FromMilliseconds(caret_blink_ms), this, - &Textfield::UpdateCursor); - } - + if (ShouldBlinkCursor()) + StartBlinkingCursor(); View::OnFocus(); SchedulePaint(); + if (ui::MaterialDesignController::IsSecondaryUiMaterial()) + FocusRing::Install(this); } void Textfield::OnBlur() { - GetRenderText()->set_focused(false); + gfx::RenderText* render_text = GetRenderText(); + render_text->set_focused(false); if (GetInputMethod()) GetInputMethod()->DetachTextInputClient(this); - cursor_repaint_timer_.Stop(); - if (cursor_visible_) { - cursor_visible_ = false; + StopBlinkingCursor(); + if (render_text->cursor_visible()) { + render_text->set_cursor_visible(false); RepaintCursor(); } @@ -994,6 +1013,8 @@ void Textfield::OnBlur() { // Border typically draws focus indicator. SchedulePaint(); + if (ui::MaterialDesignController::IsSecondaryUiMaterial()) + FocusRing::Uninstall(this); } gfx::Point Textfield::GetKeyboardContextMenuLocation() { @@ -1084,6 +1105,16 @@ bool Textfield::CanStartDragForView(View* sender, } //////////////////////////////////////////////////////////////////////////////// +// Textfield, WordLookupClient overrides: + +bool Textfield::GetDecoratedWordAtPoint(const gfx::Point& point, + gfx::DecoratedText* decorated_word, + gfx::Point* baseline_point) { + return GetRenderText()->GetDecoratedWordAtPoint(point, decorated_word, + baseline_point); +} + +//////////////////////////////////////////////////////////////////////////////// // Textfield, ui::TouchEditable overrides: void Textfield::SelectRect(const gfx::Point& start, const gfx::Point& end) { @@ -1179,7 +1210,7 @@ bool Textfield::IsCommandIdEnabled(int command_id) const { } bool Textfield::GetAcceleratorForCommandId(int command_id, - ui::Accelerator* accelerator) { + ui::Accelerator* accelerator) const { switch (command_id) { case IDS_APP_UNDO: *accelerator = ui::Accelerator(ui::VKEY_Z, ui::EF_CONTROL_DOWN); @@ -1264,6 +1295,11 @@ void Textfield::InsertText(const base::string16& new_text) { } void Textfield::InsertChar(const ui::KeyEvent& event) { + if (read_only()) { + OnKeypressUnhandled(); + return; + } + // Filter out all control characters, including tab and new line characters, // and all characters with Alt modifier (and Search on ChromeOS). But allow // characters with the AltGr modifier. On Windows AltGr is represented by @@ -1278,7 +1314,7 @@ void Textfield::InsertChar(const ui::KeyEvent& event) { DoInsertChar(ch); if (text_input_type_ == ui::TEXT_INPUT_TYPE_PASSWORD && - !password_reveal_duration_.is_zero()) { + !GetPasswordRevealDuration().is_zero()) { const size_t change_offset = model_->GetCursorPosition(); DCHECK_GT(change_offset, 0u); RevealPasswordChar(change_offset - 1); @@ -1467,6 +1503,8 @@ bool Textfield::IsTextEditCommandEnabled(ui::TextEditCommand command) const { case ui::TextEditCommand::MOVE_TO_END_OF_LINE_AND_MODIFY_SELECTION: case ui::TextEditCommand::MOVE_TO_END_OF_PARAGRAPH: case ui::TextEditCommand::MOVE_TO_END_OF_PARAGRAPH_AND_MODIFY_SELECTION: + case ui::TextEditCommand::MOVE_PARAGRAPH_FORWARD_AND_MODIFY_SELECTION: + case ui::TextEditCommand::MOVE_PARAGRAPH_BACKWARD_AND_MODIFY_SELECTION: case ui::TextEditCommand::MOVE_WORD_BACKWARD: case ui::TextEditCommand::MOVE_WORD_BACKWARD_AND_MODIFY_SELECTION: case ui::TextEditCommand::MOVE_WORD_FORWARD: @@ -1493,6 +1531,8 @@ bool Textfield::IsTextEditCommandEnabled(ui::TextEditCommand command) const { case ui::TextEditCommand::TRANSPOSE: return editable && !model_->HasSelection() && !model_->HasCompositionText(); + case ui::TextEditCommand::YANK: + return editable; case ui::TextEditCommand::MOVE_DOWN: case ui::TextEditCommand::MOVE_DOWN_AND_MODIFY_SELECTION: case ui::TextEditCommand::MOVE_PAGE_DOWN: @@ -1501,6 +1541,13 @@ bool Textfield::IsTextEditCommandEnabled(ui::TextEditCommand command) const { case ui::TextEditCommand::MOVE_PAGE_UP_AND_MODIFY_SELECTION: case ui::TextEditCommand::MOVE_UP: case ui::TextEditCommand::MOVE_UP_AND_MODIFY_SELECTION: +// On Mac, the textfield should respond to Up/Down arrows keys and +// PageUp/PageDown. +#if defined(OS_MACOSX) + return true; +#else + return false; +#endif case ui::TextEditCommand::INSERT_TEXT: case ui::TextEditCommand::SET_MARK: case ui::TextEditCommand::UNSELECT: @@ -1543,21 +1590,24 @@ base::string16 Textfield::GetSelectionClipboardText() const { void Textfield::ExecuteTextEditCommand(ui::TextEditCommand command) { DestroyTouchSelection(); + bool add_to_kill_buffer = false; + // Some codepaths may bypass GetCommandForKeyEvent, so any selection-dependent // modifications of the command should happen here. - if (HasSelection()) { - switch (command) { - case ui::TextEditCommand::DELETE_TO_BEGINNING_OF_LINE: - case ui::TextEditCommand::DELETE_TO_BEGINNING_OF_PARAGRAPH: - case ui::TextEditCommand::DELETE_TO_END_OF_LINE: - case ui::TextEditCommand::DELETE_TO_END_OF_PARAGRAPH: - case ui::TextEditCommand::DELETE_WORD_BACKWARD: - case ui::TextEditCommand::DELETE_WORD_FORWARD: + switch (command) { + case ui::TextEditCommand::DELETE_TO_BEGINNING_OF_LINE: + case ui::TextEditCommand::DELETE_TO_BEGINNING_OF_PARAGRAPH: + case ui::TextEditCommand::DELETE_TO_END_OF_LINE: + case ui::TextEditCommand::DELETE_TO_END_OF_PARAGRAPH: + add_to_kill_buffer = text_input_type_ != ui::TEXT_INPUT_TYPE_PASSWORD; + // Fall through. + case ui::TextEditCommand::DELETE_WORD_BACKWARD: + case ui::TextEditCommand::DELETE_WORD_FORWARD: + if (HasSelection()) command = ui::TextEditCommand::DELETE_FORWARD; - break; - default: - break; - } + break; + default: + break; } // We only execute the commands enabled in Textfield::IsTextEditCommandEnabled @@ -1575,98 +1625,125 @@ void Textfield::ExecuteTextEditCommand(ui::TextEditCommand command) { OnBeforeUserAction(); switch (command) { case ui::TextEditCommand::DELETE_BACKWARD: - text_changed = cursor_changed = model_->Backspace(); + text_changed = cursor_changed = model_->Backspace(add_to_kill_buffer); break; case ui::TextEditCommand::DELETE_FORWARD: - text_changed = cursor_changed = model_->Delete(); + text_changed = cursor_changed = model_->Delete(add_to_kill_buffer); break; case ui::TextEditCommand::DELETE_TO_BEGINNING_OF_LINE: case ui::TextEditCommand::DELETE_TO_BEGINNING_OF_PARAGRAPH: - model_->MoveCursor(gfx::LINE_BREAK, begin, true); - text_changed = cursor_changed = model_->Backspace(); + model_->MoveCursor(gfx::LINE_BREAK, begin, gfx::SELECTION_RETAIN); + text_changed = cursor_changed = model_->Backspace(add_to_kill_buffer); break; case ui::TextEditCommand::DELETE_TO_END_OF_LINE: case ui::TextEditCommand::DELETE_TO_END_OF_PARAGRAPH: - model_->MoveCursor(gfx::LINE_BREAK, end, true); - text_changed = cursor_changed = model_->Delete(); + model_->MoveCursor(gfx::LINE_BREAK, end, gfx::SELECTION_RETAIN); + text_changed = cursor_changed = model_->Delete(add_to_kill_buffer); break; case ui::TextEditCommand::DELETE_WORD_BACKWARD: - model_->MoveCursor(gfx::WORD_BREAK, begin, true); - text_changed = cursor_changed = model_->Backspace(); + model_->MoveCursor(gfx::WORD_BREAK, begin, gfx::SELECTION_RETAIN); + text_changed = cursor_changed = model_->Backspace(add_to_kill_buffer); break; case ui::TextEditCommand::DELETE_WORD_FORWARD: - model_->MoveCursor(gfx::WORD_BREAK, end, true); - text_changed = cursor_changed = model_->Delete(); + model_->MoveCursor(gfx::WORD_BREAK, end, gfx::SELECTION_RETAIN); + text_changed = cursor_changed = model_->Delete(add_to_kill_buffer); break; case ui::TextEditCommand::MOVE_BACKWARD: - model_->MoveCursor(gfx::CHARACTER_BREAK, begin, false); + model_->MoveCursor(gfx::CHARACTER_BREAK, begin, gfx::SELECTION_NONE); break; case ui::TextEditCommand::MOVE_BACKWARD_AND_MODIFY_SELECTION: - model_->MoveCursor(gfx::CHARACTER_BREAK, begin, true); + model_->MoveCursor(gfx::CHARACTER_BREAK, begin, gfx::SELECTION_RETAIN); break; case ui::TextEditCommand::MOVE_FORWARD: - model_->MoveCursor(gfx::CHARACTER_BREAK, end, false); + model_->MoveCursor(gfx::CHARACTER_BREAK, end, gfx::SELECTION_NONE); break; case ui::TextEditCommand::MOVE_FORWARD_AND_MODIFY_SELECTION: - model_->MoveCursor(gfx::CHARACTER_BREAK, end, true); + model_->MoveCursor(gfx::CHARACTER_BREAK, end, gfx::SELECTION_RETAIN); break; case ui::TextEditCommand::MOVE_LEFT: - model_->MoveCursor(gfx::CHARACTER_BREAK, gfx::CURSOR_LEFT, false); + model_->MoveCursor(gfx::CHARACTER_BREAK, gfx::CURSOR_LEFT, + gfx::SELECTION_NONE); break; case ui::TextEditCommand::MOVE_LEFT_AND_MODIFY_SELECTION: - model_->MoveCursor(gfx::CHARACTER_BREAK, gfx::CURSOR_LEFT, true); + model_->MoveCursor(gfx::CHARACTER_BREAK, gfx::CURSOR_LEFT, + gfx::SELECTION_RETAIN); break; case ui::TextEditCommand::MOVE_RIGHT: - model_->MoveCursor(gfx::CHARACTER_BREAK, gfx::CURSOR_RIGHT, false); + model_->MoveCursor(gfx::CHARACTER_BREAK, gfx::CURSOR_RIGHT, + gfx::SELECTION_NONE); break; case ui::TextEditCommand::MOVE_RIGHT_AND_MODIFY_SELECTION: - model_->MoveCursor(gfx::CHARACTER_BREAK, gfx::CURSOR_RIGHT, true); + model_->MoveCursor(gfx::CHARACTER_BREAK, gfx::CURSOR_RIGHT, + gfx::SELECTION_RETAIN); break; case ui::TextEditCommand::MOVE_TO_BEGINNING_OF_DOCUMENT: case ui::TextEditCommand::MOVE_TO_BEGINNING_OF_LINE: case ui::TextEditCommand::MOVE_TO_BEGINNING_OF_PARAGRAPH: - model_->MoveCursor(gfx::LINE_BREAK, begin, false); + case ui::TextEditCommand::MOVE_UP: + case ui::TextEditCommand::MOVE_PAGE_UP: + model_->MoveCursor(gfx::LINE_BREAK, begin, gfx::SELECTION_NONE); break; case ui::TextEditCommand:: MOVE_TO_BEGINNING_OF_DOCUMENT_AND_MODIFY_SELECTION: case ui::TextEditCommand::MOVE_TO_BEGINNING_OF_LINE_AND_MODIFY_SELECTION: case ui::TextEditCommand:: MOVE_TO_BEGINNING_OF_PARAGRAPH_AND_MODIFY_SELECTION: - model_->MoveCursor(gfx::LINE_BREAK, begin, true); + model_->MoveCursor(gfx::LINE_BREAK, begin, kLineSelectionBehavior); + break; + case ui::TextEditCommand::MOVE_PAGE_UP_AND_MODIFY_SELECTION: + case ui::TextEditCommand::MOVE_UP_AND_MODIFY_SELECTION: + model_->MoveCursor(gfx::LINE_BREAK, begin, gfx::SELECTION_RETAIN); break; case ui::TextEditCommand::MOVE_TO_END_OF_DOCUMENT: case ui::TextEditCommand::MOVE_TO_END_OF_LINE: case ui::TextEditCommand::MOVE_TO_END_OF_PARAGRAPH: - model_->MoveCursor(gfx::LINE_BREAK, end, false); + case ui::TextEditCommand::MOVE_DOWN: + case ui::TextEditCommand::MOVE_PAGE_DOWN: + model_->MoveCursor(gfx::LINE_BREAK, end, gfx::SELECTION_NONE); break; case ui::TextEditCommand::MOVE_TO_END_OF_DOCUMENT_AND_MODIFY_SELECTION: case ui::TextEditCommand::MOVE_TO_END_OF_LINE_AND_MODIFY_SELECTION: case ui::TextEditCommand::MOVE_TO_END_OF_PARAGRAPH_AND_MODIFY_SELECTION: - model_->MoveCursor(gfx::LINE_BREAK, end, true); + model_->MoveCursor(gfx::LINE_BREAK, end, kLineSelectionBehavior); + break; + case ui::TextEditCommand::MOVE_PAGE_DOWN_AND_MODIFY_SELECTION: + case ui::TextEditCommand::MOVE_DOWN_AND_MODIFY_SELECTION: + model_->MoveCursor(gfx::LINE_BREAK, end, gfx::SELECTION_RETAIN); + break; + case ui::TextEditCommand::MOVE_PARAGRAPH_BACKWARD_AND_MODIFY_SELECTION: + model_->MoveCursor(gfx::LINE_BREAK, begin, + kMoveParagraphSelectionBehavior); + break; + case ui::TextEditCommand::MOVE_PARAGRAPH_FORWARD_AND_MODIFY_SELECTION: + model_->MoveCursor(gfx::LINE_BREAK, end, kMoveParagraphSelectionBehavior); break; case ui::TextEditCommand::MOVE_WORD_BACKWARD: - model_->MoveCursor(gfx::WORD_BREAK, begin, false); + model_->MoveCursor(gfx::WORD_BREAK, begin, gfx::SELECTION_NONE); break; case ui::TextEditCommand::MOVE_WORD_BACKWARD_AND_MODIFY_SELECTION: - model_->MoveCursor(gfx::WORD_BREAK, begin, true); + model_->MoveCursor(gfx::WORD_BREAK, begin, kWordSelectionBehavior); break; case ui::TextEditCommand::MOVE_WORD_FORWARD: - model_->MoveCursor(gfx::WORD_BREAK, end, false); + model_->MoveCursor(gfx::WORD_BREAK, end, gfx::SELECTION_NONE); break; case ui::TextEditCommand::MOVE_WORD_FORWARD_AND_MODIFY_SELECTION: - model_->MoveCursor(gfx::WORD_BREAK, end, true); + model_->MoveCursor(gfx::WORD_BREAK, end, kWordSelectionBehavior); break; case ui::TextEditCommand::MOVE_WORD_LEFT: - model_->MoveCursor(gfx::WORD_BREAK, gfx::CURSOR_LEFT, false); + model_->MoveCursor(gfx::WORD_BREAK, gfx::CURSOR_LEFT, + gfx::SELECTION_NONE); break; case ui::TextEditCommand::MOVE_WORD_LEFT_AND_MODIFY_SELECTION: - model_->MoveCursor(gfx::WORD_BREAK, gfx::CURSOR_LEFT, true); + model_->MoveCursor(gfx::WORD_BREAK, gfx::CURSOR_LEFT, + kWordSelectionBehavior); break; case ui::TextEditCommand::MOVE_WORD_RIGHT: - model_->MoveCursor(gfx::WORD_BREAK, gfx::CURSOR_RIGHT, false); + model_->MoveCursor(gfx::WORD_BREAK, gfx::CURSOR_RIGHT, + gfx::SELECTION_NONE); break; case ui::TextEditCommand::MOVE_WORD_RIGHT_AND_MODIFY_SELECTION: - model_->MoveCursor(gfx::WORD_BREAK, gfx::CURSOR_RIGHT, true); + model_->MoveCursor(gfx::WORD_BREAK, gfx::CURSOR_RIGHT, + kWordSelectionBehavior); break; case ui::TextEditCommand::UNDO: text_changed = cursor_changed = model_->Undo(); @@ -1689,14 +1766,9 @@ void Textfield::ExecuteTextEditCommand(ui::TextEditCommand command) { case ui::TextEditCommand::TRANSPOSE: text_changed = cursor_changed = model_->Transpose(); break; - case ui::TextEditCommand::MOVE_DOWN: - case ui::TextEditCommand::MOVE_DOWN_AND_MODIFY_SELECTION: - case ui::TextEditCommand::MOVE_PAGE_DOWN: - case ui::TextEditCommand::MOVE_PAGE_DOWN_AND_MODIFY_SELECTION: - case ui::TextEditCommand::MOVE_PAGE_UP: - case ui::TextEditCommand::MOVE_PAGE_UP_AND_MODIFY_SELECTION: - case ui::TextEditCommand::MOVE_UP: - case ui::TextEditCommand::MOVE_UP_AND_MODIFY_SELECTION: + case ui::TextEditCommand::YANK: + text_changed = cursor_changed = model_->Yank(); + break; case ui::TextEditCommand::INSERT_TEXT: case ui::TextEditCommand::SET_MARK: case ui::TextEditCommand::UNSELECT: @@ -1724,12 +1796,18 @@ void Textfield::AccessibilitySetValue(const base::string16& new_value) { void Textfield::UpdateBackgroundColor() { const SkColor color = GetBackgroundColor(); - set_background(Background::CreateSolidBackground(color)); + if (ui::MaterialDesignController::IsSecondaryUiMaterial()) { + set_background(Background::CreateBackgroundPainter( + true, Painter::CreateSolidRoundRectPainter( + color, FocusableBorder::kCornerRadiusDp))); + } else { + set_background(Background::CreateSolidBackground(color)); + } // Disable subpixel rendering when the background color is transparent // because it draws incorrect colors around the glyphs in that case. // See crbug.com/115198 GetRenderText()->set_subpixel_rendering_suppressed( - SkColorGetA(color) != 0xFF); + SkColorGetA(color) != SK_AlphaOPAQUE); SchedulePaint(); } @@ -1740,10 +1818,12 @@ void Textfield::UpdateAfterChange(bool text_changed, bool cursor_changed) { NotifyAccessibilityEvent(ui::AX_EVENT_TEXT_CHANGED, true); } if (cursor_changed) { - cursor_visible_ = true; + GetRenderText()->set_cursor_visible(ShouldShowCursor()); RepaintCursor(); - if (cursor_repaint_timer_.IsRunning()) - cursor_repaint_timer_.Reset(); + if (ShouldBlinkCursor()) + StartBlinkingCursor(); + else + StopBlinkingCursor(); if (!text_changed) { // TEXT_CHANGED implies TEXT_SELECTION_CHANGED, so we only need to fire // this if only the selection changed. @@ -1756,12 +1836,6 @@ void Textfield::UpdateAfterChange(bool text_changed, bool cursor_changed) { } } -void Textfield::UpdateCursor() { - const size_t caret_blink_ms = Textfield::GetCaretBlinkMs(); - cursor_visible_ = !cursor_visible_ || (caret_blink_ms == 0); - RepaintCursor(); -} - void Textfield::RepaintCursor() { gfx::Rect r(GetRenderText()->GetUpdatedCursorBounds()); r.Inset(-1, -1, -1, -1); @@ -1776,12 +1850,12 @@ void Textfield::PaintTextAndCursor(gfx::Canvas* canvas) { gfx::RenderText* render_text = GetRenderText(); if (text().empty() && !GetPlaceholderText().empty()) { canvas->DrawStringRect(GetPlaceholderText(), GetFontList(), - placeholder_text_color(), render_text->display_rect()); + ui::MaterialDesignController::IsSecondaryUiMaterial() + ? SkColorSetA(GetTextColor(), 0x83) + : placeholder_text_color_, + render_text->display_rect()); } - // Draw the text, cursor, and selection. - render_text->set_cursor_visible(cursor_visible_ && !drop_cursor_visible_ && - !HasSelection()); render_text->Draw(canvas); // Draw the detached drop cursor that marks where the text will be dropped. @@ -1802,10 +1876,12 @@ void Textfield::SelectThroughLastDragLocation() { const bool drags_to_end = PlatformStyle::kTextfieldDragVerticallyDragsToEnd; if (drags_to_end && last_drag_location_.y() < 0) { model_->MoveCursor(gfx::BreakType::LINE_BREAK, - gfx::VisualCursorDirection::CURSOR_LEFT, true); + gfx::VisualCursorDirection::CURSOR_LEFT, + gfx::SELECTION_RETAIN); } else if (drags_to_end && last_drag_location_.y() > height()) { model_->MoveCursor(gfx::BreakType::LINE_BREAK, - gfx::VisualCursorDirection::CURSOR_RIGHT, true); + gfx::VisualCursorDirection::CURSOR_RIGHT, + gfx::SELECTION_RETAIN); } else { model_->MoveCursorTo(last_drag_location_, true); } @@ -1894,9 +1970,10 @@ void Textfield::UpdateContextMenu() { if (controller_) controller_->UpdateContextMenu(context_menu_contents_.get()); } - context_menu_runner_.reset( - new MenuRunner(context_menu_contents_.get(), - MenuRunner::HAS_MNEMONICS | MenuRunner::CONTEXT_MENU)); + context_menu_runner_.reset(new MenuRunner(context_menu_contents_.get(), + MenuRunner::HAS_MNEMONICS | + MenuRunner::CONTEXT_MENU | + MenuRunner::ASYNC)); } void Textfield::TrackMouseClicks(const ui::MouseEvent& event) { @@ -1928,7 +2005,7 @@ void Textfield::RevealPasswordChar(int index) { SchedulePaint(); if (index != -1) { - password_reveal_timer_.Start(FROM_HERE, password_reveal_duration_, + password_reveal_timer_.Start(FROM_HERE, GetPasswordRevealDuration(), base::Bind(&Textfield::RevealPasswordChar, weak_ptr_factory_.GetWeakPtr(), -1)); } @@ -1948,7 +2025,8 @@ void Textfield::CreateTouchSelectionControllerAndNotifyIt() { void Textfield::UpdateSelectionClipboard() const { #if defined(OS_LINUX) && !defined(OS_CHROMEOS) - if (performing_user_action_ && HasSelection()) { + if (performing_user_action_ && HasSelection() && + text_input_type_ != ui::TEXT_INPUT_TYPE_PASSWORD) { ui::ScopedClipboardWriter( ui::CLIPBOARD_TYPE_SELECTION).WriteText(GetSelectedText()); if (controller_) @@ -1974,4 +2052,35 @@ void Textfield::PasteSelectionClipboard(const ui::MouseEvent& event) { OnAfterUserAction(); } +void Textfield::OnKeypressUnhandled() { + PlatformStyle::OnTextfieldKeypressUnhandled(); +} + +bool Textfield::ShouldShowCursor() const { + return HasFocus() && !HasSelection() && enabled() && !read_only() && + !drop_cursor_visible_; +} + +bool Textfield::ShouldBlinkCursor() const { + return ShouldShowCursor() && Textfield::GetCaretBlinkMs() != 0; +} + +void Textfield::StartBlinkingCursor() { + DCHECK(ShouldBlinkCursor()); + cursor_blink_timer_.Start(FROM_HERE, base::TimeDelta::FromMilliseconds( + Textfield::GetCaretBlinkMs()), + this, &Textfield::OnCursorBlinkTimerFired); +} + +void Textfield::StopBlinkingCursor() { + cursor_blink_timer_.Stop(); +} + +void Textfield::OnCursorBlinkTimerFired() { + DCHECK(ShouldBlinkCursor()); + gfx::RenderText* render_text = GetRenderText(); + render_text->set_cursor_visible(!render_text->cursor_visible()); + RepaintCursor(); +} + } // namespace views diff --git a/chromium/ui/views/controls/textfield/textfield.h b/chromium/ui/views/controls/textfield/textfield.h index ac1f0cf6df1..d59a1f9187a 100644 --- a/chromium/ui/views/controls/textfield/textfield.h +++ b/chromium/ui/views/controls/textfield/textfield.h @@ -30,6 +30,7 @@ #include "ui/views/controls/textfield/textfield_model.h" #include "ui/views/drag_controller.h" #include "ui/views/view.h" +#include "ui/views/word_lookup_client.h" namespace views { @@ -42,6 +43,7 @@ class VIEWS_EXPORT Textfield : public View, public TextfieldModel::Delegate, public ContextMenuController, public DragController, + public WordLookupClient, public ui::TouchEditable, public ui::TextInputClient { public: @@ -73,7 +75,8 @@ class VIEWS_EXPORT Textfield : public View, // features. The flags is the bit map of ui::TextInputFlags. void SetTextInputFlags(int flags); - // Gets the text currently displayed in the Textfield. + // Gets the text for the Textfield. Call sites should take care to not reveal + // the text for a password textfield. const base::string16& text() const { return model_->text(); } // Sets the text currently displayed in the Textfield. This doesn't @@ -86,9 +89,13 @@ class VIEWS_EXPORT Textfield : public View, void AppendText(const base::string16& new_text); // Inserts |new_text| at the cursor position, replacing any selected text. + // This method is used to handle user input via paths Textfield doesn't + // normally handle, so it calls UpdateAfterChange() and notifies observers of + // changes. void InsertOrReplaceText(const base::string16& new_text); - // Returns the text that is currently selected. + // Returns the text that is currently selected. Call sites should take care to + // not reveal the text for a password textfield. base::string16 GetSelectedText() const; // Select the entire text range. If |reversed| is true, the range will end at @@ -150,9 +157,8 @@ class VIEWS_EXPORT Textfield : public View, void set_placeholder_text(const base::string16& text) { placeholder_text_ = text; } - virtual base::string16 GetPlaceholderText() const; + base::string16 GetPlaceholderText() const; - SkColor placeholder_text_color() const { return placeholder_text_color_; } void set_placeholder_text_color(SkColor color) { placeholder_text_color_ = color; } @@ -212,8 +218,7 @@ class VIEWS_EXPORT Textfield : public View, bool OnMousePressed(const ui::MouseEvent& event) override; bool OnMouseDragged(const ui::MouseEvent& event) override; void OnMouseReleased(const ui::MouseEvent& event) override; - bool OnKeyPressed(const ui::KeyEvent& event) override; - bool OnKeyReleased(const ui::KeyEvent& event) override; + WordLookupClient* GetWordLookupClient() override; void OnGestureEvent(ui::GestureEvent* event) override; bool AcceleratorPressed(const ui::Accelerator& accelerator) override; bool CanHandleAccelerators() const override; @@ -255,6 +260,11 @@ class VIEWS_EXPORT Textfield : public View, const gfx::Point& press_pt, const gfx::Point& p) override; + // WordLookupClient overrides: + bool GetDecoratedWordAtPoint(const gfx::Point& point, + gfx::DecoratedText* decorated_word, + gfx::Point* baseline_point) override; + // ui::TouchEditable overrides: void SelectRect(const gfx::Point& start, const gfx::Point& end) override; void MoveCaretTo(const gfx::Point& point) override; @@ -272,7 +282,7 @@ class VIEWS_EXPORT Textfield : public View, bool IsCommandIdChecked(int command_id) const override; bool IsCommandIdEnabled(int command_id) const override; bool GetAcceleratorForCommandId(int command_id, - ui::Accelerator* accelerator) override; + ui::Accelerator* accelerator) const override; void ExecuteCommand(int command_id, int event_flags) override; // ui::TextInputClient overrides: @@ -323,6 +333,13 @@ class VIEWS_EXPORT Textfield : public View, private: friend class TextfieldTestApi; + // View overrides: + // Declared final since overriding by subclasses would interfere with the + // accounting related to the scheduled text edit command. Subclasses should + // use TextfieldController::HandleKeyEvent, to intercept the key event. + bool OnKeyPressed(const ui::KeyEvent& event) final; + bool OnKeyReleased(const ui::KeyEvent& event) final; + // Handles a request to change the value of this text field from software // using an accessibility API (typically automation software, screen readers // don't normally use this). Sets the value and clears the selection. @@ -382,12 +399,33 @@ class VIEWS_EXPORT Textfield : public View, void CreateTouchSelectionControllerAndNotifyIt(); - // Updates the selection clipboard to any non-empty text selection. + // Updates the selection clipboard to any non-empty text selection for a non- + // password textfield. void UpdateSelectionClipboard() const; // Pastes the selection clipboard for the specified mouse event. void PasteSelectionClipboard(const ui::MouseEvent& event); + // Called whenever a keypress is unhandled for any reason, including failing + // to insert text into a readonly text field. + void OnKeypressUnhandled(); + + // Returns true if an insertion cursor should be visible (a vertical bar, + // placed at the point new text will be inserted). + bool ShouldShowCursor() const; + + // Returns true if an insertion cursor should be visible and blinking. + bool ShouldBlinkCursor() const; + + // Starts and stops blinking the cursor, respectively. These are both + // idempotent if the cursor is already blinking/not blinking. + void StartBlinkingCursor(); + void StopBlinkingCursor(); + + // Callback for the cursor blink timer. Called every + // Textfield::GetCaretBlinkMs(). + void OnCursorBlinkTimerFired(); + // The text model. std::unique_ptr<TextfieldModel> model_; @@ -423,6 +461,7 @@ class VIEWS_EXPORT Textfield : public View, base::string16 placeholder_text_; // Placeholder text color. + // TODO(estade): remove this when Harmony/MD is default. SkColor placeholder_text_color_; // The accessible name of the text field. @@ -434,8 +473,7 @@ class VIEWS_EXPORT Textfield : public View, // The input flags of this text field. int text_input_flags_; - // The duration and timer to reveal the last typed password character. - base::TimeDelta password_reveal_duration_; + // The timer to reveal the last typed password character. base::OneShotTimer password_reveal_timer_; // Tracks whether a user action is being performed; i.e. OnBeforeUserAction() @@ -445,9 +483,8 @@ class VIEWS_EXPORT Textfield : public View, // True if InputMethod::CancelComposition() should not be called. bool skip_input_method_cancel_composition_; - // The text editing cursor repaint timer and visibility. - base::RepeatingTimer cursor_repaint_timer_; - bool cursor_visible_; + // Insertion cursor repaint timer and visibility. + base::RepeatingTimer cursor_blink_timer_; // The drop cursor is a visual cue for where dragged text will be dropped. bool drop_cursor_visible_; diff --git a/chromium/ui/views/controls/textfield/textfield_model.cc b/chromium/ui/views/controls/textfield/textfield_model.cc index 297ea1978cb..dc1bc5539ef 100644 --- a/chromium/ui/views/controls/textfield/textfield_model.cc +++ b/chromium/ui/views/controls/textfield/textfield_model.cc @@ -8,6 +8,7 @@ #include "base/logging.h" #include "base/macros.h" +#include "base/message_loop/message_loop.h" #include "base/stl_util.h" #include "base/strings/string_util.h" #include "base/strings/utf_string_conversions.h" @@ -263,6 +264,24 @@ gfx::Range GetFirstEmphasizedRange(const ui::CompositionText& composition) { return gfx::Range::InvalidRange(); } +// Returns a pointer to the kill buffer which holds the text to be inserted on +// executing yank command. Singleton since it needs to be persisted across +// multiple textfields. +// On Mac, the size of the kill ring (no. of buffers) is controlled by +// NSTextKillRingSize, a text system default. However to keep things simple, +// the default kill ring size of 1 (i.e. a single buffer) is assumed. +base::string16* GetKillBuffer() { + CR_DEFINE_STATIC_LOCAL(base::string16, kill_buffer, ()); + DCHECK(base::MessageLoopForUI::IsCurrent()); + return &kill_buffer; +} + +// Helper method to set the kill buffer. +void SetKillBuffer(const base::string16& buffer) { + base::string16* kill_buffer = GetKillBuffer(); + *kill_buffer = buffer; +} + } // namespace using internal::Edit; @@ -317,21 +336,26 @@ void TextfieldModel::Append(const base::string16& new_text) { if (HasCompositionText()) ConfirmCompositionText(); size_t save = GetCursorPosition(); - MoveCursor(gfx::LINE_BREAK, - render_text_->GetVisualDirectionOfLogicalEnd(), - false); + MoveCursor(gfx::LINE_BREAK, render_text_->GetVisualDirectionOfLogicalEnd(), + gfx::SELECTION_NONE); InsertText(new_text); render_text_->SetCursorPosition(save); ClearSelection(); } -bool TextfieldModel::Delete() { +bool TextfieldModel::Delete(bool add_to_kill_buffer) { + // |add_to_kill_buffer| should never be true for an obscured textfield. + DCHECK(!add_to_kill_buffer || !render_text_->obscured()); + if (HasCompositionText()) { // No undo/redo for composition text. CancelCompositionText(); return true; } + if (HasSelection()) { + if (add_to_kill_buffer) + SetKillBuffer(GetSelectedText()); DeleteSelection(); return true; } @@ -339,28 +363,40 @@ bool TextfieldModel::Delete() { size_t cursor_position = GetCursorPosition(); size_t next_grapheme_index = render_text_->IndexOfAdjacentGrapheme( cursor_position, gfx::CURSOR_FORWARD); - ExecuteAndRecordDelete(gfx::Range(cursor_position, next_grapheme_index), - true); + gfx::Range range_to_delete(cursor_position, next_grapheme_index); + if (add_to_kill_buffer) + SetKillBuffer(GetTextFromRange(range_to_delete)); + ExecuteAndRecordDelete(range_to_delete, true); return true; } return false; } -bool TextfieldModel::Backspace() { +bool TextfieldModel::Backspace(bool add_to_kill_buffer) { + // |add_to_kill_buffer| should never be true for an obscured textfield. + DCHECK(!add_to_kill_buffer || !render_text_->obscured()); + if (HasCompositionText()) { // No undo/redo for composition text. CancelCompositionText(); return true; } + if (HasSelection()) { + if (add_to_kill_buffer) + SetKillBuffer(GetSelectedText()); DeleteSelection(); return true; } size_t cursor_position = GetCursorPosition(); if (cursor_position > 0) { // Delete one code point, which may be two UTF-16 words. - size_t previous_char = gfx::UTF16OffsetToIndex(text(), cursor_position, -1); - ExecuteAndRecordDelete(gfx::Range(cursor_position, previous_char), true); + size_t previous_grapheme_index = + gfx::UTF16OffsetToIndex(text(), cursor_position, -1); + gfx::Range range_to_delete(cursor_position, previous_grapheme_index); + if (add_to_kill_buffer) + SetKillBuffer(GetTextFromRange(range_to_delete)); + ExecuteAndRecordDelete(range_to_delete, true); return true; } return false; @@ -372,10 +408,10 @@ size_t TextfieldModel::GetCursorPosition() const { void TextfieldModel::MoveCursor(gfx::BreakType break_type, gfx::VisualCursorDirection direction, - bool select) { + gfx::SelectionBehavior selection_behavior) { if (HasCompositionText()) ConfirmCompositionText(); - render_text_->MoveCursor(break_type, direction, select); + render_text_->MoveCursor(break_type, direction, selection_behavior); } bool TextfieldModel::MoveCursorTo(const gfx::SelectionModel& cursor) { @@ -402,8 +438,7 @@ bool TextfieldModel::MoveCursorTo(const gfx::Point& point, bool select) { } base::string16 TextfieldModel::GetSelectedText() const { - return text().substr(render_text_->selection().GetMin(), - render_text_->selection().length()); + return GetTextFromRange(render_text_->selection()); } void TextfieldModel::SelectRange(const gfx::Range& range) { @@ -441,7 +476,7 @@ bool TextfieldModel::CanUndo() { } bool TextfieldModel::CanRedo() { - if (!edit_history_.size()) + if (edit_history_.empty()) return false; // There is no redo iff the current edit is the last element in the history. EditHistory::iterator iter = current_edit_; @@ -464,7 +499,7 @@ bool TextfieldModel::Undo() { if (current_edit_ == edit_history_.begin()) current_edit_ = edit_history_.end(); else - current_edit_--; + --current_edit_; return old != text() || old_cursor != GetCursorPosition(); } @@ -478,7 +513,7 @@ bool TextfieldModel::Redo() { if (current_edit_ == edit_history_.end()) current_edit_ = edit_history_.begin(); else - current_edit_ ++; + ++current_edit_; base::string16 old = text(); size_t old_cursor = GetCursorPosition(); (*current_edit_)->Redo(this); @@ -557,6 +592,15 @@ bool TextfieldModel::Transpose() { return true; } +bool TextfieldModel::Yank() { + const base::string16* kill_buffer = GetKillBuffer(); + if (!kill_buffer->empty() || HasSelection()) { + InsertTextInternal(*kill_buffer, false); + return true; + } + return false; +} + bool TextfieldModel::HasSelection() const { return !render_text_->selection().is_empty(); } @@ -580,9 +624,7 @@ void TextfieldModel::DeleteSelectionAndInsertTextAt( } base::string16 TextfieldModel::GetTextFromRange(const gfx::Range& range) const { - if (range.IsValid() && range.GetMin() < text().length()) - return text().substr(range.GetMin(), range.length()); - return base::string16(); + return render_text_->GetTextFromRange(range); } void TextfieldModel::GetTextRange(gfx::Range* range) const { @@ -668,7 +710,7 @@ bool TextfieldModel::HasCompositionText() const { } void TextfieldModel::ClearEditHistory() { - STLDeleteElements(&edit_history_); + base::STLDeleteElements(&edit_history_); current_edit_ = edit_history_.end(); } @@ -716,8 +758,8 @@ void TextfieldModel::ClearRedoHistory() { return; } EditHistory::iterator delete_start = current_edit_; - delete_start++; - STLDeleteContainerPointers(delete_start, edit_history_.end()); + ++delete_start; + base::STLDeleteContainerPointers(delete_start, edit_history_.end()); edit_history_.erase(delete_start, edit_history_.end()); } @@ -789,7 +831,7 @@ bool TextfieldModel::AddOrMergeEditHistory(Edit* edit) { DCHECK_EQ(1u, edit_history_.size()); current_edit_ = edit_history_.begin(); } else { - current_edit_++; + ++current_edit_; } return false; } @@ -810,4 +852,9 @@ void TextfieldModel::ModifyText(size_t delete_from, // TODO(oshima): Select text that was just undone, like Mac (but not GTK). } +// static +void TextfieldModel::ClearKillBuffer() { + SetKillBuffer(base::string16()); +} + } // namespace views diff --git a/chromium/ui/views/controls/textfield/textfield_model.h b/chromium/ui/views/controls/textfield/textfield_model.h index df3e9754e2d..c66fe6d0680 100644 --- a/chromium/ui/views/controls/textfield/textfield_model.h +++ b/chromium/ui/views/controls/textfield/textfield_model.h @@ -37,6 +37,10 @@ enum MergeType { } // namespace internal +namespace test { +class BridgedNativeWidgetTest; +} // namsespace test + // A model that represents text content for a views::Textfield. // It supports editing, selection and cursor manipulation. class VIEWS_EXPORT TextfieldModel { @@ -95,15 +99,17 @@ class VIEWS_EXPORT TextfieldModel { // Deletes the first character after the current cursor position (as if, the // the user has pressed delete key in the textfield). Returns true if - // the deletion is successful. + // the deletion is successful. If |add_to_kill_buffer| is true, the deleted + // text is copied to the kill buffer. // If there is composition text, it'll be deleted instead. - bool Delete(); + bool Delete(bool add_to_kill_buffer = false); // Deletes the first character before the current cursor position (as if, the // the user has pressed backspace key in the textfield). Returns true if - // the removal is successful. + // the removal is successful. If |add_to_kill_buffer| is true, the deleted + // text is copied to the kill buffer. // If there is composition text, it'll be deleted instead. - bool Backspace(); + bool Backspace(bool add_to_kill_buffer = false); // Cursor related methods. @@ -114,7 +120,7 @@ class VIEWS_EXPORT TextfieldModel { // The current composition text will be confirmed. void MoveCursor(gfx::BreakType break_type, gfx::VisualCursorDirection direction, - bool select); + gfx::SelectionBehavior selection_behavior); // Updates the cursor to the specified selection model. Any composition text // will be confirmed, which may alter the specified selection range start. @@ -182,6 +188,10 @@ class VIEWS_EXPORT TextfieldModel { // changed. bool Transpose(); + // Pastes text from the kill buffer at the current cursor position. Returns + // true if the text has changed after yanking. + bool Yank(); + // Tells if any text is selected, even if the selection is in composition // text. bool HasSelection() const; @@ -225,6 +235,9 @@ class VIEWS_EXPORT TextfieldModel { private: friend class internal::Edit; + friend class test::BridgedNativeWidgetTest; + friend class TextfieldModelTest; + friend class TextfieldTest; FRIEND_TEST_ALL_PREFIXES(TextfieldModelTest, UndoRedo_BasicTest); FRIEND_TEST_ALL_PREFIXES(TextfieldModelTest, UndoRedo_CutCopyPasteTest); @@ -269,6 +282,9 @@ class VIEWS_EXPORT TextfieldModel { void ClearComposition(); + // Clears the kill buffer. Used to clear global state between tests. + static void ClearKillBuffer(); + // The TextfieldModel::Delegate instance should be provided by the owner. Delegate* delegate_; diff --git a/chromium/ui/views/controls/textfield/textfield_model_unittest.cc b/chromium/ui/views/controls/textfield/textfield_model_unittest.cc index 36638168130..be0a344f5a3 100644 --- a/chromium/ui/views/controls/textfield/textfield_model_unittest.cc +++ b/chromium/ui/views/controls/textfield/textfield_model_unittest.cc @@ -55,6 +55,14 @@ class TextfieldModelTest : public ViewsTestBase, composition_text_confirmed_or_cleared_(false) { } + // ::testing::Test: + void TearDown() override { + // Clear kill buffer used for "Yank" text editing command so that no state + // persists between tests. + TextfieldModel::ClearKillBuffer(); + ViewsTestBase::TearDown(); + } + void OnCompositionTextConfirmedOrCleared() override { composition_text_confirmed_or_cleared_ = true; } @@ -80,7 +88,8 @@ TEST_F(TextfieldModelTest, EditString) { EXPECT_STR_EQ("HILLWORLD", model.text()); // Insert "E" and replace "I" with "L" to make "HELLO". - model.MoveCursor(gfx::CHARACTER_BREAK, gfx::CURSOR_RIGHT, false); + model.MoveCursor(gfx::CHARACTER_BREAK, gfx::CURSOR_RIGHT, + gfx::SELECTION_NONE); model.InsertChar('E'); EXPECT_STR_EQ("HEILLWORLD", model.text()); model.ReplaceChar('L'); @@ -98,11 +107,11 @@ TEST_F(TextfieldModelTest, EditString) { EXPECT_STR_EQ("HELLORLD", model.text()); // Move the cursor to start; backspace should fail. - model.MoveCursor(gfx::LINE_BREAK, gfx::CURSOR_LEFT, false); + model.MoveCursor(gfx::LINE_BREAK, gfx::CURSOR_LEFT, gfx::SELECTION_NONE); EXPECT_FALSE(model.Backspace()); EXPECT_STR_EQ("HELLORLD", model.text()); // Move the cursor to the end; delete should fail, but backspace should work. - model.MoveCursor(gfx::LINE_BREAK, gfx::CURSOR_RIGHT, false); + model.MoveCursor(gfx::LINE_BREAK, gfx::CURSOR_RIGHT, gfx::SELECTION_NONE); EXPECT_FALSE(model.Delete()); EXPECT_STR_EQ("HELLORLD", model.text()); EXPECT_TRUE(model.Backspace()); @@ -233,7 +242,7 @@ TEST_F(TextfieldModelTest, EditString_ComplexScript) { // The first 2 characters are not strong directionality characters. model.SetText( base::WideToUTF16(L"\x002C\x0020\x05D1\x05BC\x05B7\x05E9\x05BC")); - model.MoveCursor(gfx::LINE_BREAK, gfx::CURSOR_LEFT, false); + model.MoveCursor(gfx::LINE_BREAK, gfx::CURSOR_LEFT, gfx::SELECTION_NONE); EXPECT_TRUE(model.Backspace()); EXPECT_EQ(base::WideToUTF16(L"\x002C\x0020\x05D1\x05BC\x05B7\x05E9"), model.text()); @@ -244,9 +253,11 @@ TEST_F(TextfieldModelTest, EmptyString) { EXPECT_EQ(base::string16(), model.text()); EXPECT_EQ(base::string16(), model.GetSelectedText()); - model.MoveCursor(gfx::CHARACTER_BREAK, gfx::CURSOR_LEFT, true); + model.MoveCursor(gfx::CHARACTER_BREAK, gfx::CURSOR_LEFT, + gfx::SELECTION_RETAIN); EXPECT_EQ(0U, model.GetCursorPosition()); - model.MoveCursor(gfx::CHARACTER_BREAK, gfx::CURSOR_RIGHT, true); + model.MoveCursor(gfx::CHARACTER_BREAK, gfx::CURSOR_RIGHT, + gfx::SELECTION_RETAIN); EXPECT_EQ(0U, model.GetCursorPosition()); EXPECT_EQ(base::string16(), model.GetSelectedText()); @@ -258,15 +269,18 @@ TEST_F(TextfieldModelTest, EmptyString) { TEST_F(TextfieldModelTest, Selection) { TextfieldModel model(NULL); model.Append(base::ASCIIToUTF16("HELLO")); - model.MoveCursor(gfx::CHARACTER_BREAK, gfx::CURSOR_RIGHT, false); - model.MoveCursor(gfx::CHARACTER_BREAK, gfx::CURSOR_RIGHT, true); + model.MoveCursor(gfx::CHARACTER_BREAK, gfx::CURSOR_RIGHT, + gfx::SELECTION_NONE); + model.MoveCursor(gfx::CHARACTER_BREAK, gfx::CURSOR_RIGHT, + gfx::SELECTION_RETAIN); EXPECT_STR_EQ("E", model.GetSelectedText()); - model.MoveCursor(gfx::CHARACTER_BREAK, gfx::CURSOR_RIGHT, true); + model.MoveCursor(gfx::CHARACTER_BREAK, gfx::CURSOR_RIGHT, + gfx::SELECTION_RETAIN); EXPECT_STR_EQ("EL", model.GetSelectedText()); - model.MoveCursor(gfx::LINE_BREAK, gfx::CURSOR_LEFT, true); + model.MoveCursor(gfx::LINE_BREAK, gfx::CURSOR_LEFT, gfx::SELECTION_RETAIN); EXPECT_STR_EQ("H", model.GetSelectedText()); - model.MoveCursor(gfx::LINE_BREAK, gfx::CURSOR_RIGHT, true); + model.MoveCursor(gfx::LINE_BREAK, gfx::CURSOR_RIGHT, gfx::SELECTION_RETAIN); EXPECT_STR_EQ("ELLO", model.GetSelectedText()); model.ClearSelection(); EXPECT_EQ(base::string16(), model.GetSelectedText()); @@ -284,18 +298,20 @@ TEST_F(TextfieldModelTest, Selection) { // Select and move cursor. model.SelectRange(gfx::Range(1U, 3U)); EXPECT_STR_EQ("EL", model.GetSelectedText()); - model.MoveCursor(gfx::CHARACTER_BREAK, gfx::CURSOR_LEFT, false); + model.MoveCursor(gfx::CHARACTER_BREAK, gfx::CURSOR_LEFT, gfx::SELECTION_NONE); EXPECT_EQ(1U, model.GetCursorPosition()); model.SelectRange(gfx::Range(1U, 3U)); - model.MoveCursor(gfx::CHARACTER_BREAK, gfx::CURSOR_RIGHT, false); + model.MoveCursor(gfx::CHARACTER_BREAK, gfx::CURSOR_RIGHT, + gfx::SELECTION_NONE); EXPECT_EQ(3U, model.GetCursorPosition()); // Select all and move cursor. model.SelectAll(false); - model.MoveCursor(gfx::CHARACTER_BREAK, gfx::CURSOR_LEFT, false); + model.MoveCursor(gfx::CHARACTER_BREAK, gfx::CURSOR_LEFT, gfx::SELECTION_NONE); EXPECT_EQ(0U, model.GetCursorPosition()); model.SelectAll(false); - model.MoveCursor(gfx::CHARACTER_BREAK, gfx::CURSOR_RIGHT, false); + model.MoveCursor(gfx::CHARACTER_BREAK, gfx::CURSOR_RIGHT, + gfx::SELECTION_NONE); EXPECT_EQ(5U, model.GetCursorPosition()); } @@ -312,23 +328,29 @@ TEST_F(TextfieldModelTest, Selection_BidiWithNonSpacingMarks) { #if defined(OS_LINUX) model.Append(base::WideToUTF16( L"abc\x05E9\x05BC\x05C1\x05B8\x05E0\x05B8" L"def")); - model.MoveCursor(gfx::CHARACTER_BREAK, gfx::CURSOR_RIGHT, false); - model.MoveCursor(gfx::CHARACTER_BREAK, gfx::CURSOR_RIGHT, false); + model.MoveCursor(gfx::CHARACTER_BREAK, gfx::CURSOR_RIGHT, + gfx::SELECTION_NONE); + model.MoveCursor(gfx::CHARACTER_BREAK, gfx::CURSOR_RIGHT, + gfx::SELECTION_NONE); - model.MoveCursor(gfx::CHARACTER_BREAK, gfx::CURSOR_RIGHT, true); + model.MoveCursor(gfx::CHARACTER_BREAK, gfx::CURSOR_RIGHT, + gfx::SELECTION_RETAIN); EXPECT_EQ(gfx::Range(2, 3), model.render_text()->selection()); EXPECT_EQ(base::WideToUTF16(L"c"), model.GetSelectedText()); - model.MoveCursor(gfx::CHARACTER_BREAK, gfx::CURSOR_RIGHT, true); + model.MoveCursor(gfx::CHARACTER_BREAK, gfx::CURSOR_RIGHT, + gfx::SELECTION_RETAIN); EXPECT_EQ(gfx::Range(2, 7), model.render_text()->selection()); EXPECT_EQ(base::WideToUTF16(L"c\x05E9\x05BC\x05C1\x05B8"), model.GetSelectedText()); - model.MoveCursor(gfx::CHARACTER_BREAK, gfx::CURSOR_RIGHT, true); + model.MoveCursor(gfx::CHARACTER_BREAK, gfx::CURSOR_RIGHT, + gfx::SELECTION_RETAIN); EXPECT_EQ(gfx::Range(2, 3), model.render_text()->selection()); EXPECT_EQ(base::WideToUTF16(L"c"), model.GetSelectedText()); - model.MoveCursor(gfx::CHARACTER_BREAK, gfx::CURSOR_RIGHT, true); + model.MoveCursor(gfx::CHARACTER_BREAK, gfx::CURSOR_RIGHT, + gfx::SELECTION_RETAIN); EXPECT_EQ(gfx::Range(2, 10), model.render_text()->selection()); EXPECT_EQ(base::WideToUTF16(L"c\x05E9\x05BC\x05C1\x05B8\x05E0\x05B8" L"d"), model.GetSelectedText()); @@ -345,36 +367,49 @@ TEST_F(TextfieldModelTest, Selection_BidiWithNonSpacingMarks) { // an RTL character. model.SetText(base::WideToUTF16(L"a\x05E9" L"b")); MoveCursorTo(model, 0); - model.MoveCursor(gfx::CHARACTER_BREAK, gfx::CURSOR_RIGHT, true); + model.MoveCursor(gfx::CHARACTER_BREAK, gfx::CURSOR_RIGHT, + gfx::SELECTION_RETAIN); EXPECT_EQ(base::WideToUTF16(L"a"), model.GetSelectedText()); - model.MoveCursor(gfx::CHARACTER_BREAK, gfx::CURSOR_RIGHT, true); + model.MoveCursor(gfx::CHARACTER_BREAK, gfx::CURSOR_RIGHT, + gfx::SELECTION_RETAIN); EXPECT_EQ(base::WideToUTF16(L"a"), model.GetSelectedText()); - model.MoveCursor(gfx::CHARACTER_BREAK, gfx::CURSOR_RIGHT, true); + model.MoveCursor(gfx::CHARACTER_BREAK, gfx::CURSOR_RIGHT, + gfx::SELECTION_RETAIN); EXPECT_EQ(base::WideToUTF16(L"a\x05E9" L"b"), model.GetSelectedText()); - model.MoveCursor(gfx::CHARACTER_BREAK, gfx::CURSOR_RIGHT, false); + model.MoveCursor(gfx::CHARACTER_BREAK, gfx::CURSOR_RIGHT, + gfx::SELECTION_NONE); EXPECT_EQ(3U, model.GetCursorPosition()); - model.MoveCursor(gfx::CHARACTER_BREAK, gfx::CURSOR_LEFT, true); + model.MoveCursor(gfx::CHARACTER_BREAK, gfx::CURSOR_LEFT, + gfx::SELECTION_RETAIN); EXPECT_EQ(base::WideToUTF16(L"b"), model.GetSelectedText()); - model.MoveCursor(gfx::CHARACTER_BREAK, gfx::CURSOR_LEFT, true); + model.MoveCursor(gfx::CHARACTER_BREAK, gfx::CURSOR_LEFT, + gfx::SELECTION_RETAIN); EXPECT_EQ(base::WideToUTF16(L"b"), model.GetSelectedText()); - model.MoveCursor(gfx::CHARACTER_BREAK, gfx::CURSOR_LEFT, true); + model.MoveCursor(gfx::CHARACTER_BREAK, gfx::CURSOR_LEFT, + gfx::SELECTION_RETAIN); EXPECT_EQ(base::WideToUTF16(L"a\x05E9" L"b"), model.GetSelectedText()); - model.MoveCursor(gfx::LINE_BREAK, gfx::CURSOR_LEFT, false); - model.MoveCursor(gfx::CHARACTER_BREAK, gfx::CURSOR_RIGHT, true); - model.MoveCursor(gfx::CHARACTER_BREAK, gfx::CURSOR_RIGHT, true); - model.MoveCursor(gfx::CHARACTER_BREAK, gfx::CURSOR_LEFT, true); + model.MoveCursor(gfx::LINE_BREAK, gfx::CURSOR_LEFT, gfx::SELECTION_NONE); + model.MoveCursor(gfx::CHARACTER_BREAK, gfx::CURSOR_RIGHT, + gfx::SELECTION_RETAIN); + model.MoveCursor(gfx::CHARACTER_BREAK, gfx::CURSOR_RIGHT, + gfx::SELECTION_RETAIN); + model.MoveCursor(gfx::CHARACTER_BREAK, gfx::CURSOR_LEFT, + gfx::SELECTION_RETAIN); EXPECT_EQ(base::WideToUTF16(L"a\x05E9"), model.GetSelectedText()); - model.MoveCursor(gfx::LINE_BREAK, gfx::CURSOR_RIGHT, false); - model.MoveCursor(gfx::CHARACTER_BREAK, gfx::CURSOR_LEFT, true); - model.MoveCursor(gfx::CHARACTER_BREAK, gfx::CURSOR_LEFT, true); - model.MoveCursor(gfx::CHARACTER_BREAK, gfx::CURSOR_RIGHT, true); + model.MoveCursor(gfx::LINE_BREAK, gfx::CURSOR_RIGHT, gfx::SELECTION_NONE); + model.MoveCursor(gfx::CHARACTER_BREAK, gfx::CURSOR_LEFT, + gfx::SELECTION_RETAIN); + model.MoveCursor(gfx::CHARACTER_BREAK, gfx::CURSOR_LEFT, + gfx::SELECTION_RETAIN); + model.MoveCursor(gfx::CHARACTER_BREAK, gfx::CURSOR_RIGHT, + gfx::SELECTION_RETAIN); EXPECT_EQ(base::WideToUTF16(L"\x05E9" L"b"), model.GetSelectedText()); model.ClearSelection(); @@ -386,28 +421,37 @@ TEST_F(TextfieldModelTest, Selection_BidiWithNonSpacingMarks) { TEST_F(TextfieldModelTest, SelectionAndEdit) { TextfieldModel model(NULL); model.Append(base::ASCIIToUTF16("HELLO")); - model.MoveCursor(gfx::CHARACTER_BREAK, gfx::CURSOR_RIGHT, false); - model.MoveCursor(gfx::CHARACTER_BREAK, gfx::CURSOR_RIGHT, true); - model.MoveCursor(gfx::CHARACTER_BREAK, gfx::CURSOR_RIGHT, true); // "EL" + model.MoveCursor(gfx::CHARACTER_BREAK, gfx::CURSOR_RIGHT, + gfx::SELECTION_NONE); + model.MoveCursor(gfx::CHARACTER_BREAK, gfx::CURSOR_RIGHT, + gfx::SELECTION_RETAIN); + model.MoveCursor(gfx::CHARACTER_BREAK, gfx::CURSOR_RIGHT, + gfx::SELECTION_RETAIN); // "EL" EXPECT_TRUE(model.Backspace()); EXPECT_STR_EQ("HLO", model.text()); model.Append(base::ASCIIToUTF16("ILL")); - model.MoveCursor(gfx::CHARACTER_BREAK, gfx::CURSOR_RIGHT, true); - model.MoveCursor(gfx::CHARACTER_BREAK, gfx::CURSOR_RIGHT, true); // "LO" + model.MoveCursor(gfx::CHARACTER_BREAK, gfx::CURSOR_RIGHT, + gfx::SELECTION_RETAIN); + model.MoveCursor(gfx::CHARACTER_BREAK, gfx::CURSOR_RIGHT, + gfx::SELECTION_RETAIN); // "LO" EXPECT_TRUE(model.Delete()); EXPECT_STR_EQ("HILL", model.text()); EXPECT_EQ(1U, model.GetCursorPosition()); - model.MoveCursor(gfx::CHARACTER_BREAK, gfx::CURSOR_RIGHT, true); // "I" + model.MoveCursor(gfx::CHARACTER_BREAK, gfx::CURSOR_RIGHT, + gfx::SELECTION_RETAIN); // "I" model.InsertChar('E'); EXPECT_STR_EQ("HELL", model.text()); - model.MoveCursor(gfx::LINE_BREAK, gfx::CURSOR_LEFT, false); - model.MoveCursor(gfx::CHARACTER_BREAK, gfx::CURSOR_RIGHT, true); // "H" + model.MoveCursor(gfx::LINE_BREAK, gfx::CURSOR_LEFT, gfx::SELECTION_NONE); + model.MoveCursor(gfx::CHARACTER_BREAK, gfx::CURSOR_RIGHT, + gfx::SELECTION_RETAIN); // "H" model.ReplaceChar('B'); EXPECT_STR_EQ("BELL", model.text()); - model.MoveCursor(gfx::LINE_BREAK, gfx::CURSOR_RIGHT, false); - model.MoveCursor(gfx::CHARACTER_BREAK, gfx::CURSOR_LEFT, true); - model.MoveCursor(gfx::CHARACTER_BREAK, gfx::CURSOR_LEFT, true); // "ELL" + model.MoveCursor(gfx::LINE_BREAK, gfx::CURSOR_RIGHT, gfx::SELECTION_NONE); + model.MoveCursor(gfx::CHARACTER_BREAK, gfx::CURSOR_LEFT, + gfx::SELECTION_RETAIN); + model.MoveCursor(gfx::CHARACTER_BREAK, gfx::CURSOR_LEFT, + gfx::SELECTION_RETAIN); // "ELL" model.ReplaceChar('E'); EXPECT_STR_EQ("BEE", model.text()); } @@ -416,43 +460,45 @@ TEST_F(TextfieldModelTest, Word) { TextfieldModel model(NULL); model.Append( base::ASCIIToUTF16("The answer to Life, the Universe, and Everything")); - model.MoveCursor(gfx::WORD_BREAK, gfx::CURSOR_RIGHT, false); + model.MoveCursor(gfx::WORD_BREAK, gfx::CURSOR_RIGHT, gfx::SELECTION_NONE); EXPECT_EQ(3U, model.GetCursorPosition()); - model.MoveCursor(gfx::WORD_BREAK, gfx::CURSOR_RIGHT, false); + model.MoveCursor(gfx::WORD_BREAK, gfx::CURSOR_RIGHT, gfx::SELECTION_NONE); EXPECT_EQ(10U, model.GetCursorPosition()); - model.MoveCursor(gfx::WORD_BREAK, gfx::CURSOR_RIGHT, false); - model.MoveCursor(gfx::WORD_BREAK, gfx::CURSOR_RIGHT, false); + model.MoveCursor(gfx::WORD_BREAK, gfx::CURSOR_RIGHT, gfx::SELECTION_NONE); + model.MoveCursor(gfx::WORD_BREAK, gfx::CURSOR_RIGHT, gfx::SELECTION_NONE); EXPECT_EQ(18U, model.GetCursorPosition()); // Should passes the non word char ',' - model.MoveCursor(gfx::WORD_BREAK, gfx::CURSOR_RIGHT, true); + model.MoveCursor(gfx::WORD_BREAK, gfx::CURSOR_RIGHT, gfx::SELECTION_RETAIN); EXPECT_EQ(23U, model.GetCursorPosition()); EXPECT_STR_EQ(", the", model.GetSelectedText()); // Move to the end. - model.MoveCursor(gfx::WORD_BREAK, gfx::CURSOR_RIGHT, true); - model.MoveCursor(gfx::WORD_BREAK, gfx::CURSOR_RIGHT, true); - model.MoveCursor(gfx::WORD_BREAK, gfx::CURSOR_RIGHT, true); + model.MoveCursor(gfx::WORD_BREAK, gfx::CURSOR_RIGHT, gfx::SELECTION_RETAIN); + model.MoveCursor(gfx::WORD_BREAK, gfx::CURSOR_RIGHT, gfx::SELECTION_RETAIN); + model.MoveCursor(gfx::WORD_BREAK, gfx::CURSOR_RIGHT, gfx::SELECTION_RETAIN); EXPECT_STR_EQ(", the Universe, and Everything", model.GetSelectedText()); // Should be safe to go next word at the end. - model.MoveCursor(gfx::WORD_BREAK, gfx::CURSOR_RIGHT, true); + model.MoveCursor(gfx::WORD_BREAK, gfx::CURSOR_RIGHT, gfx::SELECTION_RETAIN); EXPECT_STR_EQ(", the Universe, and Everything", model.GetSelectedText()); model.InsertChar('2'); EXPECT_EQ(19U, model.GetCursorPosition()); // Now backwards. - model.MoveCursor(gfx::CHARACTER_BREAK, gfx::CURSOR_LEFT, false); // leave 2. - model.MoveCursor(gfx::WORD_BREAK, gfx::CURSOR_LEFT, true); + model.MoveCursor(gfx::CHARACTER_BREAK, gfx::CURSOR_LEFT, + gfx::SELECTION_NONE); // leave 2. + model.MoveCursor(gfx::WORD_BREAK, gfx::CURSOR_LEFT, gfx::SELECTION_RETAIN); EXPECT_EQ(14U, model.GetCursorPosition()); EXPECT_STR_EQ("Life", model.GetSelectedText()); - model.MoveCursor(gfx::WORD_BREAK, gfx::CURSOR_LEFT, true); + model.MoveCursor(gfx::WORD_BREAK, gfx::CURSOR_LEFT, gfx::SELECTION_RETAIN); EXPECT_STR_EQ("to Life", model.GetSelectedText()); - model.MoveCursor(gfx::WORD_BREAK, gfx::CURSOR_LEFT, true); - model.MoveCursor(gfx::WORD_BREAK, gfx::CURSOR_LEFT, true); - model.MoveCursor(gfx::WORD_BREAK, gfx::CURSOR_LEFT, true); // Now at start. + model.MoveCursor(gfx::WORD_BREAK, gfx::CURSOR_LEFT, gfx::SELECTION_RETAIN); + model.MoveCursor(gfx::WORD_BREAK, gfx::CURSOR_LEFT, gfx::SELECTION_RETAIN); + model.MoveCursor(gfx::WORD_BREAK, gfx::CURSOR_LEFT, + gfx::SELECTION_RETAIN); // Now at start. EXPECT_STR_EQ("The answer to Life", model.GetSelectedText()); // Should be safe to go to the previous word at the beginning. - model.MoveCursor(gfx::WORD_BREAK, gfx::CURSOR_LEFT, true); + model.MoveCursor(gfx::WORD_BREAK, gfx::CURSOR_LEFT, gfx::SELECTION_RETAIN); EXPECT_STR_EQ("The answer to Life", model.GetSelectedText()); model.ReplaceChar('4'); EXPECT_EQ(base::string16(), model.GetSelectedText()); @@ -462,14 +508,14 @@ TEST_F(TextfieldModelTest, Word) { TEST_F(TextfieldModelTest, SetText) { TextfieldModel model(NULL); model.Append(base::ASCIIToUTF16("HELLO")); - model.MoveCursor(gfx::LINE_BREAK, gfx::CURSOR_RIGHT, false); + model.MoveCursor(gfx::LINE_BREAK, gfx::CURSOR_RIGHT, gfx::SELECTION_NONE); model.SetText(base::ASCIIToUTF16("GOODBYE")); EXPECT_STR_EQ("GOODBYE", model.text()); // SetText move the cursor to the end of the new text. EXPECT_EQ(7U, model.GetCursorPosition()); model.SelectAll(false); EXPECT_STR_EQ("GOODBYE", model.GetSelectedText()); - model.MoveCursor(gfx::LINE_BREAK, gfx::CURSOR_RIGHT, false); + model.MoveCursor(gfx::LINE_BREAK, gfx::CURSOR_RIGHT, gfx::SELECTION_NONE); EXPECT_EQ(7U, model.GetCursorPosition()); model.SetText(base::ASCIIToUTF16("BYE")); @@ -492,7 +538,7 @@ TEST_F(TextfieldModelTest, Clipboard) { model.Append(base::ASCIIToUTF16("HELLO WORLD")); // Cut with an empty selection should do nothing. - model.MoveCursor(gfx::LINE_BREAK, gfx::CURSOR_RIGHT, false); + model.MoveCursor(gfx::LINE_BREAK, gfx::CURSOR_RIGHT, gfx::SELECTION_NONE); EXPECT_FALSE(model.Cut()); clipboard->ReadText(ui::CLIPBOARD_TYPE_COPY_PASTE, &clipboard_text); EXPECT_EQ(initial_clipboard_text, clipboard_text); @@ -525,8 +571,8 @@ TEST_F(TextfieldModelTest, Clipboard) { // Cut with non-empty selection. model.render_text()->SetObscured(false); - model.MoveCursor(gfx::LINE_BREAK, gfx::CURSOR_RIGHT, false); - model.MoveCursor(gfx::WORD_BREAK, gfx::CURSOR_LEFT, true); + model.MoveCursor(gfx::LINE_BREAK, gfx::CURSOR_RIGHT, gfx::SELECTION_NONE); + model.MoveCursor(gfx::WORD_BREAK, gfx::CURSOR_LEFT, gfx::SELECTION_RETAIN); EXPECT_TRUE(model.Cut()); clipboard->ReadText(ui::CLIPBOARD_TYPE_COPY_PASTE, &clipboard_text); EXPECT_STR_EQ("WORLD", clipboard_text); @@ -543,7 +589,7 @@ TEST_F(TextfieldModelTest, Clipboard) { // Test that paste works regardless of the obscured bit. Please note that // trailing spaces and tabs in clipboard strings will be stripped. - model.MoveCursor(gfx::LINE_BREAK, gfx::CURSOR_RIGHT, false); + model.MoveCursor(gfx::LINE_BREAK, gfx::CURSOR_RIGHT, gfx::SELECTION_NONE); EXPECT_TRUE(model.Paste()); EXPECT_STR_EQ("HELLO HELLO", model.text()); EXPECT_EQ(11U, model.GetCursorPosition()); @@ -566,7 +612,7 @@ TEST_F(TextfieldModelTest, SelectWordTest) { model.Append(base::ASCIIToUTF16(" HELLO !! WO RLD ")); // Test when cursor is at the beginning. - model.MoveCursor(gfx::LINE_BREAK, gfx::CURSOR_LEFT, false); + model.MoveCursor(gfx::LINE_BREAK, gfx::CURSOR_LEFT, gfx::SELECTION_NONE); model.SelectWord(); SelectWordTestVerifier(model, base::ASCIIToUTF16(" "), 2U); @@ -593,7 +639,7 @@ TEST_F(TextfieldModelTest, SelectWordTest) { SelectWordTestVerifier(model, base::ASCIIToUTF16(" "), 20U); // Test when cursor is at the end. - model.MoveCursor(gfx::LINE_BREAK, gfx::CURSOR_RIGHT, false); + model.MoveCursor(gfx::LINE_BREAK, gfx::CURSOR_RIGHT, gfx::SELECTION_NONE); model.SelectWord(); SelectWordTestVerifier(model, base::ASCIIToUTF16(" "), 24U); } @@ -623,9 +669,10 @@ TEST_F(TextfieldModelTest, SelectWordTest_MixScripts) { model.SetText(base::WideToUTF16(L"a\x05d0 \x05d1\x05d2 \x0915\x094d\x0915 " L"\x4E2D\x56FD\x82B1\x5929")); for (size_t i = 0; i < word_and_cursor.size(); ++i) { - model.MoveCursor(gfx::LINE_BREAK, gfx::CURSOR_LEFT, false); + model.MoveCursor(gfx::LINE_BREAK, gfx::CURSOR_LEFT, gfx::SELECTION_NONE); for (size_t j = 0; j < i; ++j) - model.MoveCursor(gfx::CHARACTER_BREAK, gfx::CURSOR_RIGHT, false); + model.MoveCursor(gfx::CHARACTER_BREAK, gfx::CURSOR_RIGHT, + gfx::SELECTION_NONE); model.SelectWord(); SelectWordTestVerifier(model, base::WideToUTF16(word_and_cursor[i].word), word_and_cursor[i].cursor); @@ -636,60 +683,62 @@ TEST_F(TextfieldModelTest, SelectWordTest_MixScripts) { TEST_F(TextfieldModelTest, RangeTest) { TextfieldModel model(NULL); model.Append(base::ASCIIToUTF16("HELLO WORLD")); - model.MoveCursor(gfx::LINE_BREAK, gfx::CURSOR_LEFT, false); + model.MoveCursor(gfx::LINE_BREAK, gfx::CURSOR_LEFT, gfx::SELECTION_NONE); gfx::Range range = model.render_text()->selection(); EXPECT_TRUE(range.is_empty()); EXPECT_EQ(0U, range.start()); EXPECT_EQ(0U, range.end()); - model.MoveCursor(gfx::WORD_BREAK, gfx::CURSOR_RIGHT, true); + model.MoveCursor(gfx::WORD_BREAK, gfx::CURSOR_RIGHT, gfx::SELECTION_RETAIN); range = model.render_text()->selection(); EXPECT_FALSE(range.is_empty()); EXPECT_FALSE(range.is_reversed()); EXPECT_EQ(0U, range.start()); EXPECT_EQ(5U, range.end()); - model.MoveCursor(gfx::CHARACTER_BREAK, gfx::CURSOR_LEFT, true); + model.MoveCursor(gfx::CHARACTER_BREAK, gfx::CURSOR_LEFT, + gfx::SELECTION_RETAIN); range = model.render_text()->selection(); EXPECT_FALSE(range.is_empty()); EXPECT_EQ(0U, range.start()); EXPECT_EQ(4U, range.end()); - model.MoveCursor(gfx::WORD_BREAK, gfx::CURSOR_LEFT, true); + model.MoveCursor(gfx::WORD_BREAK, gfx::CURSOR_LEFT, gfx::SELECTION_RETAIN); range = model.render_text()->selection(); EXPECT_TRUE(range.is_empty()); EXPECT_EQ(0U, range.start()); EXPECT_EQ(0U, range.end()); // now from the end. - model.MoveCursor(gfx::LINE_BREAK, gfx::CURSOR_RIGHT, false); + model.MoveCursor(gfx::LINE_BREAK, gfx::CURSOR_RIGHT, gfx::SELECTION_NONE); range = model.render_text()->selection(); EXPECT_TRUE(range.is_empty()); EXPECT_EQ(11U, range.start()); EXPECT_EQ(11U, range.end()); - model.MoveCursor(gfx::WORD_BREAK, gfx::CURSOR_LEFT, true); + model.MoveCursor(gfx::WORD_BREAK, gfx::CURSOR_LEFT, gfx::SELECTION_RETAIN); range = model.render_text()->selection(); EXPECT_FALSE(range.is_empty()); EXPECT_TRUE(range.is_reversed()); EXPECT_EQ(11U, range.start()); EXPECT_EQ(6U, range.end()); - model.MoveCursor(gfx::CHARACTER_BREAK, gfx::CURSOR_RIGHT, true); + model.MoveCursor(gfx::CHARACTER_BREAK, gfx::CURSOR_RIGHT, + gfx::SELECTION_RETAIN); range = model.render_text()->selection(); EXPECT_FALSE(range.is_empty()); EXPECT_TRUE(range.is_reversed()); EXPECT_EQ(11U, range.start()); EXPECT_EQ(7U, range.end()); - model.MoveCursor(gfx::WORD_BREAK, gfx::CURSOR_RIGHT, true); + model.MoveCursor(gfx::WORD_BREAK, gfx::CURSOR_RIGHT, gfx::SELECTION_RETAIN); range = model.render_text()->selection(); EXPECT_TRUE(range.is_empty()); EXPECT_EQ(11U, range.start()); EXPECT_EQ(11U, range.end()); // Select All - model.MoveCursor(gfx::LINE_BREAK, gfx::CURSOR_LEFT, true); + model.MoveCursor(gfx::LINE_BREAK, gfx::CURSOR_LEFT, gfx::SELECTION_RETAIN); range = model.render_text()->selection(); EXPECT_FALSE(range.is_empty()); EXPECT_TRUE(range.is_reversed()); @@ -744,41 +793,43 @@ TEST_F(TextfieldModelTest, SelectRangeTest) { TEST_F(TextfieldModelTest, SelectionTest) { TextfieldModel model(NULL); model.Append(base::ASCIIToUTF16("HELLO WORLD")); - model.MoveCursor(gfx::LINE_BREAK, gfx::CURSOR_LEFT, false); + model.MoveCursor(gfx::LINE_BREAK, gfx::CURSOR_LEFT, gfx::SELECTION_NONE); gfx::Range selection = model.render_text()->selection(); EXPECT_EQ(gfx::Range(0), selection); - model.MoveCursor(gfx::WORD_BREAK, gfx::CURSOR_RIGHT, true); + model.MoveCursor(gfx::WORD_BREAK, gfx::CURSOR_RIGHT, gfx::SELECTION_RETAIN); selection = model.render_text()->selection(); EXPECT_EQ(gfx::Range(0, 5), selection); - model.MoveCursor(gfx::CHARACTER_BREAK, gfx::CURSOR_LEFT, true); + model.MoveCursor(gfx::CHARACTER_BREAK, gfx::CURSOR_LEFT, + gfx::SELECTION_RETAIN); selection = model.render_text()->selection(); EXPECT_EQ(gfx::Range(0, 4), selection); - model.MoveCursor(gfx::WORD_BREAK, gfx::CURSOR_LEFT, true); + model.MoveCursor(gfx::WORD_BREAK, gfx::CURSOR_LEFT, gfx::SELECTION_RETAIN); selection = model.render_text()->selection(); EXPECT_EQ(gfx::Range(0), selection); // now from the end. - model.MoveCursor(gfx::LINE_BREAK, gfx::CURSOR_RIGHT, false); + model.MoveCursor(gfx::LINE_BREAK, gfx::CURSOR_RIGHT, gfx::SELECTION_NONE); selection = model.render_text()->selection(); EXPECT_EQ(gfx::Range(11), selection); - model.MoveCursor(gfx::WORD_BREAK, gfx::CURSOR_LEFT, true); + model.MoveCursor(gfx::WORD_BREAK, gfx::CURSOR_LEFT, gfx::SELECTION_RETAIN); selection = model.render_text()->selection(); EXPECT_EQ(gfx::Range(11, 6), selection); - model.MoveCursor(gfx::CHARACTER_BREAK, gfx::CURSOR_RIGHT, true); + model.MoveCursor(gfx::CHARACTER_BREAK, gfx::CURSOR_RIGHT, + gfx::SELECTION_RETAIN); selection = model.render_text()->selection(); EXPECT_EQ(gfx::Range(11, 7), selection); - model.MoveCursor(gfx::WORD_BREAK, gfx::CURSOR_RIGHT, true); + model.MoveCursor(gfx::WORD_BREAK, gfx::CURSOR_RIGHT, gfx::SELECTION_RETAIN); selection = model.render_text()->selection(); EXPECT_EQ(gfx::Range(11), selection); // Select All - model.MoveCursor(gfx::LINE_BREAK, gfx::CURSOR_LEFT, true); + model.MoveCursor(gfx::LINE_BREAK, gfx::CURSOR_LEFT, gfx::SELECTION_RETAIN); selection = model.render_text()->selection(); EXPECT_EQ(gfx::Range(11, 0), selection); } @@ -874,7 +925,7 @@ TEST_F(TextfieldModelTest, CompositionTextTest) { EXPECT_TRUE(model.SetText(base::ASCIIToUTF16("1234567890"))); EXPECT_TRUE(composition_text_confirmed_or_cleared_); composition_text_confirmed_or_cleared_ = false; - model.MoveCursor(gfx::LINE_BREAK, gfx::CURSOR_RIGHT, false); + model.MoveCursor(gfx::LINE_BREAK, gfx::CURSOR_RIGHT, gfx::SELECTION_NONE); model.SetCompositionText(composition); EXPECT_STR_EQ("1234567890678", model.text()); @@ -886,7 +937,8 @@ TEST_F(TextfieldModelTest, CompositionTextTest) { EXPECT_FALSE(model.HasCompositionText()); EXPECT_FALSE(model.HasSelection()); - model.MoveCursor(gfx::CHARACTER_BREAK, gfx::CURSOR_LEFT, true); + model.MoveCursor(gfx::CHARACTER_BREAK, gfx::CURSOR_LEFT, + gfx::SELECTION_RETAIN); EXPECT_STR_EQ("-", model.GetSelectedText()); model.SetCompositionText(composition); EXPECT_STR_EQ("1234567890678", model.text()); @@ -918,39 +970,40 @@ TEST_F(TextfieldModelTest, CompositionTextTest) { model.SetText(base::string16()); model.SetCompositionText(composition); - model.MoveCursor(gfx::CHARACTER_BREAK, gfx::CURSOR_LEFT, false); + model.MoveCursor(gfx::CHARACTER_BREAK, gfx::CURSOR_LEFT, gfx::SELECTION_NONE); EXPECT_TRUE(composition_text_confirmed_or_cleared_); composition_text_confirmed_or_cleared_ = false; EXPECT_STR_EQ("678", model.text()); EXPECT_EQ(2U, model.GetCursorPosition()); model.SetCompositionText(composition); - model.MoveCursor(gfx::CHARACTER_BREAK, gfx::CURSOR_RIGHT, false); + model.MoveCursor(gfx::CHARACTER_BREAK, gfx::CURSOR_RIGHT, + gfx::SELECTION_NONE); EXPECT_TRUE(composition_text_confirmed_or_cleared_); composition_text_confirmed_or_cleared_ = false; EXPECT_STR_EQ("676788", model.text()); EXPECT_EQ(6U, model.GetCursorPosition()); model.SetCompositionText(composition); - model.MoveCursor(gfx::WORD_BREAK, gfx::CURSOR_LEFT, false); + model.MoveCursor(gfx::WORD_BREAK, gfx::CURSOR_LEFT, gfx::SELECTION_NONE); EXPECT_TRUE(composition_text_confirmed_or_cleared_); composition_text_confirmed_or_cleared_ = false; EXPECT_STR_EQ("676788678", model.text()); model.SetText(base::string16()); model.SetCompositionText(composition); - model.MoveCursor(gfx::WORD_BREAK, gfx::CURSOR_RIGHT, false); + model.MoveCursor(gfx::WORD_BREAK, gfx::CURSOR_RIGHT, gfx::SELECTION_NONE); EXPECT_TRUE(composition_text_confirmed_or_cleared_); composition_text_confirmed_or_cleared_ = false; model.SetCompositionText(composition); - model.MoveCursor(gfx::LINE_BREAK, gfx::CURSOR_LEFT, true); + model.MoveCursor(gfx::LINE_BREAK, gfx::CURSOR_LEFT, gfx::SELECTION_RETAIN); EXPECT_TRUE(composition_text_confirmed_or_cleared_); composition_text_confirmed_or_cleared_ = false; EXPECT_STR_EQ("678678", model.text()); model.SetCompositionText(composition); - model.MoveCursor(gfx::LINE_BREAK, gfx::CURSOR_RIGHT, false); + model.MoveCursor(gfx::LINE_BREAK, gfx::CURSOR_RIGHT, gfx::SELECTION_NONE); EXPECT_TRUE(composition_text_confirmed_or_cleared_); composition_text_confirmed_or_cleared_ = false; EXPECT_STR_EQ("678", model.text()); @@ -1065,7 +1118,7 @@ TEST_F(TextfieldModelTest, UndoRedo_BasicTest) { MoveCursorTo(model, 2); EXPECT_TRUE(model.Delete()); EXPECT_STR_EQ("ABDE", model.text()); - model.MoveCursor(gfx::LINE_BREAK, gfx::CURSOR_LEFT, false); + model.MoveCursor(gfx::LINE_BREAK, gfx::CURSOR_LEFT, gfx::SELECTION_NONE); EXPECT_TRUE(model.Delete()); EXPECT_STR_EQ("BDE", model.text()); EXPECT_TRUE(model.Undo()); @@ -1161,7 +1214,7 @@ TEST_F(TextfieldModelTest, UndoRedo_BackspaceThenSetText) { EXPECT_EQ(14U, model.GetCursorPosition()); EXPECT_STR_EQ("www.google.com", model.text()); model.SetText(base::ASCIIToUTF16("www.google.com")); // Confirm the text. - model.MoveCursor(gfx::LINE_BREAK, gfx::CURSOR_RIGHT, false); + model.MoveCursor(gfx::LINE_BREAK, gfx::CURSOR_RIGHT, gfx::SELECTION_NONE); EXPECT_EQ(14U, model.GetCursorPosition()); EXPECT_TRUE(model.Backspace()); EXPECT_TRUE(model.Backspace()); @@ -1249,7 +1302,7 @@ TEST_F(TextfieldModelTest, UndoRedo_CutCopyPasteTest) { EXPECT_EQ(1U, model.GetCursorPosition()); model.SelectRange(gfx::Range(1, 1)); EXPECT_FALSE(model.Cut()); - model.MoveCursor(gfx::LINE_BREAK, gfx::CURSOR_RIGHT, false); + model.MoveCursor(gfx::LINE_BREAK, gfx::CURSOR_RIGHT, gfx::SELECTION_NONE); EXPECT_TRUE(model.Paste()); EXPECT_STR_EQ("ABCBCDEBC", model.text()); EXPECT_EQ(9U, model.GetCursorPosition()); @@ -1303,7 +1356,7 @@ TEST_F(TextfieldModelTest, UndoRedo_CutCopyPasteTest) { model.SelectRange(gfx::Range(1, 3)); model.Copy(); EXPECT_STR_EQ("1232345", model.text()); - model.MoveCursor(gfx::LINE_BREAK, gfx::CURSOR_RIGHT, false); + model.MoveCursor(gfx::LINE_BREAK, gfx::CURSOR_RIGHT, gfx::SELECTION_NONE); EXPECT_TRUE(model.Paste()); EXPECT_STR_EQ("123234523", model.text()); EXPECT_EQ(9U, model.GetCursorPosition()); @@ -1315,8 +1368,9 @@ TEST_F(TextfieldModelTest, UndoRedo_CutCopyPasteTest) { TEST_F(TextfieldModelTest, UndoRedo_CursorTest) { TextfieldModel model(NULL); model.InsertChar('a'); - model.MoveCursor(gfx::CHARACTER_BREAK, gfx::CURSOR_LEFT, false); - model.MoveCursor(gfx::CHARACTER_BREAK, gfx::CURSOR_RIGHT, false); + model.MoveCursor(gfx::CHARACTER_BREAK, gfx::CURSOR_LEFT, gfx::SELECTION_NONE); + model.MoveCursor(gfx::CHARACTER_BREAK, gfx::CURSOR_RIGHT, + gfx::SELECTION_NONE); model.InsertChar('b'); // Moving the cursor shouldn't create a new edit. EXPECT_STR_EQ("ab", model.text()); @@ -1418,7 +1472,7 @@ TEST_F(TextfieldModelTest, UndoRedo_CompositionText) { composition.selection = gfx::Range(2, 3); model.SetText(base::ASCIIToUTF16("ABCDE")); - model.MoveCursor(gfx::LINE_BREAK, gfx::CURSOR_RIGHT, false); + model.MoveCursor(gfx::LINE_BREAK, gfx::CURSOR_RIGHT, gfx::SELECTION_NONE); model.InsertChar('x'); EXPECT_STR_EQ("ABCDEx", model.text()); EXPECT_TRUE(model.Undo()); // set composition should forget undone edit. @@ -1441,7 +1495,7 @@ TEST_F(TextfieldModelTest, UndoRedo_CompositionText) { EXPECT_FALSE(model.Redo()); // Cancel the composition. - model.MoveCursor(gfx::LINE_BREAK, gfx::CURSOR_LEFT, false); + model.MoveCursor(gfx::LINE_BREAK, gfx::CURSOR_LEFT, gfx::SELECTION_NONE); model.SetCompositionText(composition); EXPECT_STR_EQ("abcABCDEabc", model.text()); model.CancelCompositionText(); @@ -1457,7 +1511,7 @@ TEST_F(TextfieldModelTest, UndoRedo_CompositionText) { // Call SetText with the same text as the result. ResetModel(&model); model.SetText(base::ASCIIToUTF16("ABCDE")); - model.MoveCursor(gfx::LINE_BREAK, gfx::CURSOR_RIGHT, false); + model.MoveCursor(gfx::LINE_BREAK, gfx::CURSOR_RIGHT, gfx::SELECTION_NONE); model.SetCompositionText(composition); EXPECT_STR_EQ("ABCDEabc", model.text()); model.SetText(base::ASCIIToUTF16("ABCDEabc")); @@ -1471,7 +1525,7 @@ TEST_F(TextfieldModelTest, UndoRedo_CompositionText) { // Call SetText with a different result; the composition should be forgotten. ResetModel(&model); model.SetText(base::ASCIIToUTF16("ABCDE")); - model.MoveCursor(gfx::LINE_BREAK, gfx::CURSOR_RIGHT, false); + model.MoveCursor(gfx::LINE_BREAK, gfx::CURSOR_RIGHT, gfx::SELECTION_NONE); model.SetCompositionText(composition); EXPECT_STR_EQ("ABCDEabc", model.text()); model.SetText(base::ASCIIToUTF16("1234")); @@ -1498,7 +1552,7 @@ TEST_F(TextfieldModelTest, Clipboard_WhiteSpaceStringTest) { TextfieldModel model(NULL); model.Append(base::ASCIIToUTF16("HELLO WORLD")); EXPECT_STR_EQ("HELLO WORLD", model.text()); - model.MoveCursor(gfx::LINE_BREAK, gfx::CURSOR_RIGHT, false); + model.MoveCursor(gfx::LINE_BREAK, gfx::CURSOR_RIGHT, gfx::SELECTION_NONE); EXPECT_EQ(11U, model.GetCursorPosition()); EXPECT_TRUE(model.Paste()); @@ -1516,7 +1570,7 @@ TEST_F(TextfieldModelTest, Clipboard_WhiteSpaceStringTest) { model.Append(base::ASCIIToUTF16("HELLO WORLD")); EXPECT_STR_EQ("HELLO WORLD", model.text()); - model.MoveCursor(gfx::LINE_BREAK, gfx::CURSOR_RIGHT, false); + model.MoveCursor(gfx::LINE_BREAK, gfx::CURSOR_RIGHT, gfx::SELECTION_NONE); EXPECT_EQ(11U, model.GetCursorPosition()); EXPECT_TRUE(model.Paste()); EXPECT_STR_EQ("HELLO WORLDB", model.text()); @@ -1533,7 +1587,7 @@ TEST_F(TextfieldModelTest, Clipboard_WhiteSpaceStringTest) { model.Append(base::ASCIIToUTF16("HELLO WORLD")); EXPECT_STR_EQ("HELLO WORLD", model.text()); - model.MoveCursor(gfx::LINE_BREAK, gfx::CURSOR_RIGHT, false); + model.MoveCursor(gfx::LINE_BREAK, gfx::CURSOR_RIGHT, gfx::SELECTION_NONE); EXPECT_EQ(11U, model.GetCursorPosition()); EXPECT_TRUE(model.Paste()); EXPECT_STR_EQ("HELLO WORLDFOO BAR", model.text()); @@ -1653,4 +1707,52 @@ TEST_F(TextfieldModelTest, Transpose) { } } +TEST_F(TextfieldModelTest, Yank) { + TextfieldModel model(nullptr); + model.SetText(base::ASCIIToUTF16("abcde")); + model.SelectRange(gfx::Range(1, 3)); + + // Delete selection but don't add to kill buffer. + model.Delete(false); + EXPECT_STR_EQ("ade", model.text()); + + // Since the kill buffer is empty, yank should cause no change. + model.Yank(); + EXPECT_STR_EQ("ade", model.text()); + + // Delete selection and add to kill buffer. + model.SelectRange(gfx::Range(0, 1)); + model.Delete(true); + EXPECT_STR_EQ("de", model.text()); + + // Yank twice. + model.Yank(); + model.Yank(); + EXPECT_STR_EQ("aade", model.text()); + + // Ensure an empty deletion does not modify the kill buffer. + model.SelectRange(gfx::Range(4)); + model.Delete(true); + model.Yank(); + EXPECT_STR_EQ("aadea", model.text()); + + // Backspace twice but don't add to kill buffer. + model.Backspace(false); + model.Backspace(false); + EXPECT_STR_EQ("aad", model.text()); + + // Ensure kill buffer is not modified. + model.Yank(); + EXPECT_STR_EQ("aada", model.text()); + + // Backspace twice, each time modifying the kill buffer. + model.Backspace(true); + model.Backspace(true); + EXPECT_STR_EQ("aa", model.text()); + + // Ensure yanking inserts the modified kill buffer text. + model.Yank(); + EXPECT_STR_EQ("aad", model.text()); +} + } // namespace views diff --git a/chromium/ui/views/controls/textfield/textfield_test_api.h b/chromium/ui/views/controls/textfield/textfield_test_api.h index 4505b712907..f488f2cfefc 100644 --- a/chromium/ui/views/controls/textfield/textfield_test_api.h +++ b/chromium/ui/views/controls/textfield/textfield_test_api.h @@ -25,6 +25,10 @@ class TextfieldTestApi { TextfieldModel* model() const { return textfield_->model_.get(); } + void ExecuteTextEditCommand(ui::TextEditCommand command) { + textfield_->ExecuteTextEditCommand(command); + } + ui::MenuModel* context_menu_contents() const { return textfield_->context_menu_contents_.get(); } @@ -33,6 +37,14 @@ class TextfieldTestApi { return textfield_->touch_selection_controller_.get(); } + ui::TextEditCommand scheduled_text_edit_command() const { + return textfield_->scheduled_text_edit_command_; + } + + bool IsCursorBlinkTimerRunning() const { + return textfield_->cursor_blink_timer_.IsRunning(); + } + private: Textfield* textfield_; diff --git a/chromium/ui/views/controls/textfield/textfield_unittest.cc b/chromium/ui/views/controls/textfield/textfield_unittest.cc index c6e4fba368c..4786bf1ed8b 100644 --- a/chromium/ui/views/controls/textfield/textfield_unittest.cc +++ b/chromium/ui/views/controls/textfield/textfield_unittest.cc @@ -27,6 +27,7 @@ #include "ui/base/ime/input_method_base.h" #include "ui/base/ime/input_method_delegate.h" #include "ui/base/ime/input_method_factory.h" +#include "ui/base/ime/text_edit_commands.h" #include "ui/base/ime/text_input_client.h" #include "ui/base/ui_base_switches.h" #include "ui/base/ui_base_switches_util.h" @@ -35,6 +36,7 @@ #include "ui/events/event_utils.h" #include "ui/events/keycodes/keyboard_codes.h" #include "ui/events/test/event_generator.h" +#include "ui/events/test/keyboard_layout.h" #include "ui/gfx/render_text.h" #include "ui/strings/grit/ui_strings.h" #include "ui/views/controls/textfield/textfield_controller.h" @@ -82,8 +84,6 @@ class MockInputMethod : public ui::InputMethodBase { void OnTextInputTypeChanged(const ui::TextInputClient* client) override; void OnCaretBoundsChanged(const ui::TextInputClient* client) override {} void CancelComposition(const ui::TextInputClient* client) override; - void OnInputLocaleChanged() override {} - std::string GetInputLocale() override; bool IsCandidatePopupOpen() const override; void ShowImeIfNeeded() override {} @@ -210,10 +210,6 @@ void MockInputMethod::CancelComposition(const ui::TextInputClient* client) { } } -std::string MockInputMethod::GetInputLocale() { - return "en-US"; -} - bool MockInputMethod::IsCandidatePopupOpen() const { return false; } @@ -265,29 +261,6 @@ class TestTextfield : public views::Textfield { key_received_(false), weak_ptr_factory_(this) {} - bool OnKeyPressed(const ui::KeyEvent& e) override { - key_received_ = true; - - // Since OnKeyPressed() might destroy |this|, get a weak pointer and - // verify it isn't null before writing the bool value to key_handled_. - base::WeakPtr<TestTextfield> textfield(weak_ptr_factory_.GetWeakPtr()); - bool key = views::Textfield::OnKeyPressed(e); - - if (!textfield) - return key; - - key_handled_ = key; - - return key_handled_; - } - - bool OnKeyReleased(const ui::KeyEvent& e) override { - key_received_ = true; - key_handled_ = views::Textfield::OnKeyReleased(e); - EXPECT_FALSE(key_handled_); // Textfield doesn't override OnKeyReleased. - return key_handled_; - } - // ui::TextInputClient overrides: void InsertChar(const ui::KeyEvent& e) override { views::Textfield::InsertChar(e); @@ -304,6 +277,25 @@ class TestTextfield : public views::Textfield { void clear() { key_received_ = key_handled_ = false; } private: + // views::View override: + void OnKeyEvent(ui::KeyEvent* event) override { + key_received_ = true; + + // Since Textfield::OnKeyPressed() might destroy |this|, get a weak pointer + // and verify it isn't null before writing the bool value to key_handled_. + base::WeakPtr<TestTextfield> textfield(weak_ptr_factory_.GetWeakPtr()); + views::View::OnKeyEvent(event); + + if (!textfield) + return; + + key_handled_ = event->handled(); + + // Currently, Textfield::OnKeyReleased always returns false. + if (event->type() == ui::ET_KEY_RELEASED) + EXPECT_FALSE(key_handled_); + } + bool key_handled_; bool key_received_; @@ -378,6 +370,9 @@ class TextfieldTest : public ViewsTestBase, public TextfieldController { void TearDown() override { if (widget_) widget_->Close(); + // Clear kill buffer used for "Yank" text editing command so that no state + // persists between tests. + TextfieldModel::ClearKillBuffer(); ViewsTestBase::TearDown(); } @@ -765,7 +760,19 @@ TEST_F(TextfieldTest, KeyTest) { EXPECT_STR_EQ("TexT!1!1", textfield_->text()); } -TEST_F(TextfieldTest, KeysWithModifiersTest) { +#if defined(OS_WIN) || defined(OS_MACOSX) +#define MAYBE_KeysWithModifiersTest KeysWithModifiersTest +#else +// TODO(crbug.com/645104): Implement keyboard layout changing for other +// platforms. +#define MAYBE_KeysWithModifiersTest DISABLED_KeysWithModifiersTest +#endif + +TEST_F(TextfieldTest, MAYBE_KeysWithModifiersTest) { + // Activate U.S. English keyboard layout. Modifier keys in other layouts may + // change the text inserted into a texfield and cause this test to fail. + ui::ScopedKeyboardLayout keyboard_layout(ui::KEYBOARD_LAYOUT_ENGLISH_US); + InitTextfield(); const int ctrl = ui::EF_CONTROL_DOWN; const int alt = ui::EF_ALT_DOWN; @@ -829,7 +836,212 @@ TEST_F(TextfieldTest, ControlAndSelectTest) { SendEndEvent(true); EXPECT_STR_EQ("two three", textfield_->GetSelectedText()); SendHomeEvent(true); + +// On Mac, the existing selection should be extended. +#if defined(OS_MACOSX) + EXPECT_STR_EQ("ZERO two three", textfield_->GetSelectedText()); +#else EXPECT_STR_EQ("ZERO ", textfield_->GetSelectedText()); +#endif +} + +TEST_F(TextfieldTest, WordSelection) { + InitTextfield(); + textfield_->SetText(ASCIIToUTF16("12 34567 89")); + + // Place the cursor after "5". + textfield_->SetSelectionRange(gfx::Range(6)); + + // Select word towards right. + SendWordEvent(ui::VKEY_RIGHT, true); + EXPECT_STR_EQ("67", textfield_->GetSelectedText()); + SendWordEvent(ui::VKEY_RIGHT, true); + EXPECT_STR_EQ("67 89", textfield_->GetSelectedText()); + + // Select word towards left. + SendWordEvent(ui::VKEY_LEFT, true); + EXPECT_STR_EQ("67 ", textfield_->GetSelectedText()); + SendWordEvent(ui::VKEY_LEFT, true); + +// On Mac, the selection should reduce to a caret when the selection direction +// changes for a word selection. +#if defined(OS_MACOSX) + EXPECT_EQ(gfx::Range(6), textfield_->GetSelectedRange()); +#else + EXPECT_STR_EQ("345", textfield_->GetSelectedText()); + EXPECT_EQ(gfx::Range(6, 3), textfield_->GetSelectedRange()); +#endif + + SendWordEvent(ui::VKEY_LEFT, true); +#if defined(OS_MACOSX) + EXPECT_STR_EQ("345", textfield_->GetSelectedText()); +#else + EXPECT_STR_EQ("12 345", textfield_->GetSelectedText()); +#endif + EXPECT_TRUE(textfield_->GetSelectedRange().is_reversed()); + + SendWordEvent(ui::VKEY_LEFT, true); + EXPECT_STR_EQ("12 345", textfield_->GetSelectedText()); +} + +TEST_F(TextfieldTest, LineSelection) { + InitTextfield(); + textfield_->SetText(ASCIIToUTF16("12 34567 89")); + + // Place the cursor after "5". + textfield_->SetSelectionRange(gfx::Range(6)); + + // Select line towards right. + SendEndEvent(true); + EXPECT_STR_EQ("67 89", textfield_->GetSelectedText()); + + // Select line towards left. On Mac, the existing selection should be extended + // to cover the whole line. + SendHomeEvent(true); +#if defined(OS_MACOSX) + EXPECT_EQ(textfield_->text(), textfield_->GetSelectedText()); +#else + EXPECT_STR_EQ("12 345", textfield_->GetSelectedText()); +#endif + EXPECT_TRUE(textfield_->GetSelectedRange().is_reversed()); + + // Select line towards right. + SendEndEvent(true); +#if defined(OS_MACOSX) + EXPECT_EQ(textfield_->text(), textfield_->GetSelectedText()); +#else + EXPECT_STR_EQ("67 89", textfield_->GetSelectedText()); +#endif + EXPECT_FALSE(textfield_->GetSelectedRange().is_reversed()); +} + +TEST_F(TextfieldTest, MoveUpDownAndModifySelection) { + InitTextfield(); + textfield_->SetText(ASCIIToUTF16("12 34567 89")); + textfield_->SetSelectionRange(gfx::Range(6)); + + // Up/Down keys won't be handled except on Mac where they map to move + // commands. + SendKeyEvent(ui::VKEY_UP); + EXPECT_TRUE(textfield_->key_received()); +#if defined(OS_MACOSX) + EXPECT_TRUE(textfield_->key_handled()); + EXPECT_EQ(gfx::Range(0), textfield_->GetSelectedRange()); +#else + EXPECT_FALSE(textfield_->key_handled()); +#endif + textfield_->clear(); + + SendKeyEvent(ui::VKEY_DOWN); + EXPECT_TRUE(textfield_->key_received()); +#if defined(OS_MACOSX) + EXPECT_TRUE(textfield_->key_handled()); + EXPECT_EQ(gfx::Range(11), textfield_->GetSelectedRange()); +#else + EXPECT_FALSE(textfield_->key_handled()); +#endif + textfield_->clear(); + + textfield_->SetSelectionRange(gfx::Range(6)); + + // Shift+[Up/Down] on Mac should execute the command + // MOVE_[UP/DOWN]_AND_MODIFY_SELECTION. On other platforms, textfield won't + // handle these events. + SendKeyEvent(ui::VKEY_UP, true /* shift */, false /* command */); + EXPECT_TRUE(textfield_->key_received()); +#if defined(OS_MACOSX) + EXPECT_TRUE(textfield_->key_handled()); + EXPECT_EQ(gfx::Range(6, 0), textfield_->GetSelectedRange()); +#else + EXPECT_FALSE(textfield_->key_handled()); +#endif + textfield_->clear(); + + SendKeyEvent(ui::VKEY_DOWN, true /* shift */, false /* command */); + EXPECT_TRUE(textfield_->key_received()); +#if defined(OS_MACOSX) + EXPECT_TRUE(textfield_->key_handled()); + EXPECT_EQ(gfx::Range(6, 11), textfield_->GetSelectedRange()); +#else + EXPECT_FALSE(textfield_->key_handled()); +#endif + textfield_->clear(); +} + +TEST_F(TextfieldTest, MovePageUpDownAndModifySelection) { + InitTextfield(); + +// MOVE_PAGE_[UP/DOWN] and the associated selection commands should only be +// enabled on Mac. +#if defined(OS_MACOSX) + textfield_->SetText(ASCIIToUTF16("12 34567 89")); + textfield_->SetSelectionRange(gfx::Range(6)); + + EXPECT_TRUE( + textfield_->IsTextEditCommandEnabled(ui::TextEditCommand::MOVE_PAGE_UP)); + EXPECT_TRUE(textfield_->IsTextEditCommandEnabled( + ui::TextEditCommand::MOVE_PAGE_DOWN)); + EXPECT_TRUE(textfield_->IsTextEditCommandEnabled( + ui::TextEditCommand::MOVE_PAGE_UP_AND_MODIFY_SELECTION)); + EXPECT_TRUE(textfield_->IsTextEditCommandEnabled( + ui::TextEditCommand::MOVE_PAGE_DOWN_AND_MODIFY_SELECTION)); + + test_api_->ExecuteTextEditCommand(ui::TextEditCommand::MOVE_PAGE_UP); + EXPECT_EQ(gfx::Range(0), textfield_->GetSelectedRange()); + + test_api_->ExecuteTextEditCommand(ui::TextEditCommand::MOVE_PAGE_DOWN); + EXPECT_EQ(gfx::Range(11), textfield_->GetSelectedRange()); + + textfield_->SetSelectionRange(gfx::Range(6)); + test_api_->ExecuteTextEditCommand( + ui::TextEditCommand::MOVE_PAGE_UP_AND_MODIFY_SELECTION); + EXPECT_EQ(gfx::Range(6, 0), textfield_->GetSelectedRange()); + + test_api_->ExecuteTextEditCommand( + ui::TextEditCommand::MOVE_PAGE_DOWN_AND_MODIFY_SELECTION); + EXPECT_EQ(gfx::Range(6, 11), textfield_->GetSelectedRange()); +#else + EXPECT_FALSE( + textfield_->IsTextEditCommandEnabled(ui::TextEditCommand::MOVE_PAGE_UP)); + EXPECT_FALSE(textfield_->IsTextEditCommandEnabled( + ui::TextEditCommand::MOVE_PAGE_DOWN)); + EXPECT_FALSE(textfield_->IsTextEditCommandEnabled( + ui::TextEditCommand::MOVE_PAGE_UP_AND_MODIFY_SELECTION)); + EXPECT_FALSE(textfield_->IsTextEditCommandEnabled( + ui::TextEditCommand::MOVE_PAGE_DOWN_AND_MODIFY_SELECTION)); +#endif +} + +TEST_F(TextfieldTest, MoveParagraphForwardBackwardAndModifySelection) { + InitTextfield(); + textfield_->SetText(ASCIIToUTF16("12 34567 89")); + textfield_->SetSelectionRange(gfx::Range(6)); + + test_api_->ExecuteTextEditCommand( + ui::TextEditCommand::MOVE_PARAGRAPH_FORWARD_AND_MODIFY_SELECTION); + EXPECT_EQ(gfx::Range(6, 11), textfield_->GetSelectedRange()); + + test_api_->ExecuteTextEditCommand( + ui::TextEditCommand::MOVE_PARAGRAPH_BACKWARD_AND_MODIFY_SELECTION); +// On Mac, the selection should reduce to a caret when the selection direction +// is reversed for MOVE_PARAGRAPH_[FORWARD/BACKWARD]_AND_MODIFY_SELECTION. +#if defined(OS_MACOSX) + EXPECT_EQ(gfx::Range(6), textfield_->GetSelectedRange()); +#else + EXPECT_EQ(gfx::Range(6, 0), textfield_->GetSelectedRange()); +#endif + + test_api_->ExecuteTextEditCommand( + ui::TextEditCommand::MOVE_PARAGRAPH_BACKWARD_AND_MODIFY_SELECTION); + EXPECT_EQ(gfx::Range(6, 0), textfield_->GetSelectedRange()); + + test_api_->ExecuteTextEditCommand( + ui::TextEditCommand::MOVE_PARAGRAPH_FORWARD_AND_MODIFY_SELECTION); +#if defined(OS_MACOSX) + EXPECT_EQ(gfx::Range(6), textfield_->GetSelectedRange()); +#else + EXPECT_EQ(gfx::Range(6, 11), textfield_->GetSelectedRange()); +#endif } TEST_F(TextfieldTest, InsertionDeletionTest) { @@ -1049,26 +1261,6 @@ TEST_F(TextfieldTest, OnKeyPress) { #endif EXPECT_FALSE(textfield_->key_handled()); textfield_->clear(); - - // Up/Down keys won't be handled except on Mac where they map to move - // commands. - SendKeyEvent(ui::VKEY_UP); - EXPECT_TRUE(textfield_->key_received()); -#if defined(OS_MACOSX) - EXPECT_TRUE(textfield_->key_handled()); -#else - EXPECT_FALSE(textfield_->key_handled()); -#endif - textfield_->clear(); - - SendKeyEvent(ui::VKEY_DOWN); - EXPECT_TRUE(textfield_->key_received()); -#if defined(OS_MACOSX) - EXPECT_TRUE(textfield_->key_handled()); -#else - EXPECT_FALSE(textfield_->key_handled()); -#endif - textfield_->clear(); } // Tests that default key bindings are handled even with a delegate installed. @@ -1838,8 +2030,9 @@ TEST_F(TextfieldTest, UndoRedoTest) { } // Most platforms support Ctrl+Y as an alternative to Ctrl+Shift+Z, but on Mac -// that is bound to "Show full history", so is not mapped as an editing -// command. So, on Mac, send Cmd+Shift+Z. +// Ctrl+Y is bound to "Yank" and Cmd+Y is bound to "Show full history". So, on +// Mac, Cmd+Shift+Z is sent for the tests above and the Ctrl+Y test below is +// skipped. #if !defined(OS_MACOSX) // Test that Ctrl+Y works for Redo, as well as Ctrl+Shift+Z. @@ -1859,6 +2052,69 @@ TEST_F(TextfieldTest, RedoWithCtrlY) { #endif // !defined(OS_MACOSX) +// Non-Mac platforms don't have a key binding for Yank. Since this test is only +// run on Mac, it uses some Mac specific key bindings. +#if defined(OS_MACOSX) + +TEST_F(TextfieldTest, Yank) { + InitTextfields(2); + textfield_->SetText(ASCIIToUTF16("abcdef")); + textfield_->SelectRange(gfx::Range(2, 4)); + + // Press Ctrl+Y to yank. + SendKeyPress(ui::VKEY_Y, ui::EF_CONTROL_DOWN); + + // Initially the kill buffer should be empty. Hence yanking should delete the + // selected text. + EXPECT_STR_EQ("abef", textfield_->text()); + EXPECT_EQ(gfx::Range(2), textfield_->GetSelectedRange()); + + // Press Ctrl+K to delete to end of paragraph. This should place the deleted + // text in the kill buffer. + SendKeyPress(ui::VKEY_K, ui::EF_CONTROL_DOWN); + + EXPECT_STR_EQ("ab", textfield_->text()); + EXPECT_EQ(gfx::Range(2), textfield_->GetSelectedRange()); + + // Yank twice. + SendKeyPress(ui::VKEY_Y, ui::EF_CONTROL_DOWN); + SendKeyPress(ui::VKEY_Y, ui::EF_CONTROL_DOWN); + EXPECT_STR_EQ("abefef", textfield_->text()); + EXPECT_EQ(gfx::Range(6), textfield_->GetSelectedRange()); + + // Verify pressing backspace does not modify the kill buffer. + SendKeyEvent(ui::VKEY_BACK); + SendKeyPress(ui::VKEY_Y, ui::EF_CONTROL_DOWN); + EXPECT_STR_EQ("abefeef", textfield_->text()); + EXPECT_EQ(gfx::Range(7), textfield_->GetSelectedRange()); + + // Move focus to next textfield. + widget_->GetFocusManager()->AdvanceFocus(false); + EXPECT_EQ(2, GetFocusedView()->id()); + Textfield* textfield2 = static_cast<Textfield*>(GetFocusedView()); + EXPECT_TRUE(textfield2->text().empty()); + + // Verify yanked text persists across multiple textfields and that yanking + // into a password textfield works. + textfield2->SetTextInputType(ui::TEXT_INPUT_TYPE_PASSWORD); + SendKeyPress(ui::VKEY_Y, ui::EF_CONTROL_DOWN); + EXPECT_STR_EQ("ef", textfield2->text()); + EXPECT_EQ(gfx::Range(2), textfield2->GetSelectedRange()); + + // Verify deletion in a password textfield does not modify the kill buffer. + textfield2->SetText(ASCIIToUTF16("hello")); + textfield2->SelectRange(gfx::Range(0)); + SendKeyPress(ui::VKEY_K, ui::EF_CONTROL_DOWN); + EXPECT_TRUE(textfield2->text().empty()); + + textfield_->RequestFocus(); + textfield_->SelectRange(gfx::Range(0)); + SendKeyPress(ui::VKEY_Y, ui::EF_CONTROL_DOWN); + EXPECT_STR_EQ("efabefeef", textfield_->text()); +} + +#endif // defined(OS_MACOSX) + TEST_F(TextfieldTest, CutCopyPaste) { InitTextfield(); @@ -2490,6 +2746,46 @@ TEST_F(TextfieldTest, DISABLED_SelectionClipboard) { EXPECT_STR_EQ("other", GetClipboardText(ui::CLIPBOARD_TYPE_SELECTION)); EXPECT_EQ(ui::CLIPBOARD_TYPE_LAST, GetAndResetCopiedToClipboard()); } + +// Verify that the selection clipboard is not updated for selections on a +// password textfield. Disabled due to http://crbug.com/396477. +TEST_F(TextfieldTest, DISABLED_SelectionClipboard_Password) { + InitTextfields(2); + textfield_->SetText(ASCIIToUTF16("abcd")); + + // Select-all should update the selection clipboard for a non-password + // textfield. + SendKeyEvent(ui::VKEY_A, false, true); + EXPECT_EQ(gfx::Range(0, 4), textfield_->GetSelectedRange()); + EXPECT_STR_EQ("abcd", GetClipboardText(ui::CLIPBOARD_TYPE_SELECTION)); + EXPECT_EQ(ui::CLIPBOARD_TYPE_SELECTION, GetAndResetCopiedToClipboard()); + + // Move focus to the next textfield. + widget_->GetFocusManager()->AdvanceFocus(false); + EXPECT_EQ(2, GetFocusedView()->id()); + Textfield* textfield2 = static_cast<Textfield*>(GetFocusedView()); + + // Select-all should not modify the selection clipboard for a password + // textfield. + textfield2->SetText(ASCIIToUTF16("1234")); + textfield2->SetTextInputType(ui::TEXT_INPUT_TYPE_PASSWORD); + SendKeyEvent(ui::VKEY_A, false, true); + EXPECT_EQ(gfx::Range(0, 4), textfield2->GetSelectedRange()); + EXPECT_STR_EQ("abcd", GetClipboardText(ui::CLIPBOARD_TYPE_SELECTION)); + EXPECT_EQ(ui::CLIPBOARD_TYPE_LAST, GetAndResetCopiedToClipboard()); + + // Shift-Left/Right should not modify the selection clipboard for a password + // textfield. + SendKeyEvent(ui::VKEY_LEFT, true, false); + EXPECT_EQ(gfx::Range(0, 3), textfield2->GetSelectedRange()); + EXPECT_STR_EQ("abcd", GetClipboardText(ui::CLIPBOARD_TYPE_SELECTION)); + EXPECT_EQ(ui::CLIPBOARD_TYPE_LAST, GetAndResetCopiedToClipboard()); + + SendKeyEvent(ui::VKEY_RIGHT, true, false); + EXPECT_EQ(gfx::Range(0, 4), textfield2->GetSelectedRange()); + EXPECT_STR_EQ("abcd", GetClipboardText(ui::CLIPBOARD_TYPE_SELECTION)); + EXPECT_EQ(ui::CLIPBOARD_TYPE_LAST, GetAndResetCopiedToClipboard()); +} #endif // Long_Press gesture in Textfield can initiate a drag and drop now. @@ -2544,6 +2840,16 @@ TEST_F(TextfieldTest, DestroyingTextfieldFromOnKeyEvent) { EXPECT_FALSE(controller.target()); } +TEST_F(TextfieldTest, CursorBlinkRestartsOnInsertOrReplace) { + InitTextfield(); + textfield_->SetText(ASCIIToUTF16("abc")); + EXPECT_TRUE(test_api_->IsCursorBlinkTimerRunning()); + textfield_->SelectRange(gfx::Range(1, 2)); + EXPECT_FALSE(test_api_->IsCursorBlinkTimerRunning()); + textfield_->InsertOrReplaceText(base::ASCIIToUTF16("foo")); + EXPECT_TRUE(test_api_->IsCursorBlinkTimerRunning()); +} + class TextfieldTouchSelectionTest : public TextfieldTest { protected: // Simulates a complete tap. diff --git a/chromium/ui/views/controls/tree/tree_view.cc b/chromium/ui/views/controls/tree/tree_view.cc index 8a10bbdcef7..a87875e7870 100644 --- a/chromium/ui/views/controls/tree/tree_view.cc +++ b/chromium/ui/views/controls/tree/tree_view.cc @@ -7,6 +7,7 @@ #include <algorithm> #include "base/i18n/rtl.h" +#include "base/memory/ptr_util.h" #include "base/message_loop/message_loop.h" #include "ui/accessibility/ax_view_state.h" #include "ui/base/ime/input_method.h" @@ -25,6 +26,7 @@ #include "ui/views/controls/textfield/textfield.h" #include "ui/views/controls/tree/tree_view_controller.h" #include "ui/views/resources/grit/views_resources.h" +#include "ui/views/style/platform_style.h" using ui::TreeModel; using ui::TreeModelNode; @@ -68,6 +70,12 @@ ui::NativeTheme::ColorId text_color_id(bool has_focus, bool is_selected) { return ui::NativeTheme::kColorId_TreeText; } +bool EventIsDoubleTapOrClick(const ui::LocatedEvent& event) { + if (event.type() == ui::ET_GESTURE_TAP) + return event.AsGestureEvent()->details().tap_count() == 2; + return !!(event.flags() & ui::EF_IS_DOUBLE_CLICK); +} + } // namespace TreeView::TreeView() @@ -129,7 +137,7 @@ void TreeView::SetModel(TreeModel* model) { model_->AddObserver(this); model_->GetIcons(&icons_); - root_.RemoveAll(); + root_.DeleteAll(); ConfigureInternalNode(model_->GetRoot(), &root_); LoadChildren(&root_); root_.set_is_expanded(true); @@ -243,7 +251,7 @@ void TreeView::SetSelectedNode(TreeModelNode* model_node) { } if (selected_node_) - ScrollRectToVisible(GetBoundsForNode(selected_node_)); + ScrollRectToVisible(GetForegroundBoundsForNode(selected_node_)); // Notify controller if the old selection was empty to handle the case of // remove explicitly resetting selected_node_ before invoking this. @@ -392,13 +400,7 @@ void TreeView::ShowContextMenu(const gfx::Point& p, // ContextMenuController) if over a node. gfx::Point local_point(p); ConvertPointFromScreen(this, &local_point); - int row = (local_point.y() - kVerticalInset) / row_height_; - int depth = 0; - InternalNode* node = GetNodeByRow(row, &depth); - if (!node) - return; - gfx::Rect bounds(GetBoundsForNodeImpl(node, row, depth)); - if (!bounds.Contains(local_point)) + if (!GetNodeAtPoint(local_point)) return; } View::ShowContextMenu(p, source_type); @@ -428,9 +430,9 @@ void TreeView::TreeNodesAdded(TreeModel* model, if (!parent_node || !parent_node->loaded_children()) return; for (int i = 0; i < count; ++i) { - InternalNode* child = new InternalNode; - ConfigureInternalNode(model_->GetChild(parent, start + i), child); - parent_node->Add(child, start + i); + std::unique_ptr<InternalNode> child = base::MakeUnique<InternalNode>(); + ConfigureInternalNode(model_->GetChild(parent, start + i), child.get()); + parent_node->Add(std::move(child), start + i); } if (IsExpanded(parent)) DrawnNodesChanged(); @@ -449,7 +451,7 @@ void TreeView::TreeNodesRemoved(TreeModel* model, InternalNode* child_removing = parent_node->GetChild(start); if (selected_node_ && selected_node_->HasAncestor(child_removing)) reset_selection = true; - delete parent_node->Remove(child_removing); + parent_node->Remove(child_removing); } if (reset_selection) { // selected_node_ is no longer valid (at the time we enter this function @@ -536,7 +538,7 @@ base::string16 TreeView::GetTextForRow(int row) { gfx::Point TreeView::GetKeyboardContextMenuLocation() { int y = height() / 2; if (selected_node_) { - gfx::Rect node_bounds(GetBoundsForNode(selected_node_)); + gfx::Rect node_bounds(GetForegroundBoundsForNode(selected_node_)); gfx::Rect vis_bounds(GetVisibleBounds()); if (node_bounds.y() >= vis_bounds.y() && node_bounds.y() < vis_bounds.bottom()) { @@ -643,39 +645,19 @@ bool TreeView::OnClickOrTap(const ui::LocatedEvent& event) { CommitEdit(); RequestFocus(); - int row = (event.y() - kVerticalInset) / row_height_; - int depth = 0; - InternalNode* node = GetNodeByRow(row, &depth); - if (node) { - gfx::Rect bounds(GetBoundsForNodeImpl(node, row, depth)); - if (bounds.Contains(event.location())) { - int relative_x = event.x() - bounds.x(); - if (base::i18n::IsRTL()) - relative_x = bounds.width() - relative_x; - if (relative_x < kArrowRegionSize && - model_->GetChildCount(node->model_node())) { - if (node->is_expanded()) - Collapse(node->model_node()); - else - Expand(node->model_node()); - } else if (relative_x > kArrowRegionSize) { - SetSelectedNode(node->model_node()); - bool should_toggle = false; - if (event.type() == ui::ET_GESTURE_TAP) { - const ui::GestureEvent& gesture = - static_cast<const ui::GestureEvent&>(event); - should_toggle = gesture.details().tap_count() == 2; - } else { - should_toggle = (event.flags() & ui::EF_IS_DOUBLE_CLICK) != 0; - } - if (should_toggle) { - if (node->is_expanded()) - Collapse(node->model_node()); - else - Expand(node->model_node()); - } - } - } + InternalNode* node = GetNodeAtPoint(event.location()); + if (!node) + return true; + + bool hits_arrow = IsPointInExpandControl(node, event.location()); + if (!hits_arrow) + SetSelectedNode(node->model_node()); + + if (hits_arrow || EventIsDoubleTapOrClick(event)) { + if (node->is_expanded()) + Collapse(node->model_node()); + else + Expand(node->model_node()); } return true; } @@ -686,9 +668,9 @@ void TreeView::LoadChildren(InternalNode* node) { node->set_loaded_children(true); for (int i = 0, child_count = model_->GetChildCount(node->model_node()); i < child_count; ++i) { - InternalNode* child = new InternalNode; - ConfigureInternalNode(model_->GetChild(node->model_node(), i), child); - node->Add(child, node->child_count()); + std::unique_ptr<InternalNode> child = base::MakeUnique<InternalNode>(); + ConfigureInternalNode(model_->GetChild(node->model_node(), i), child.get()); + node->Add(std::move(child), node->child_count()); } } @@ -728,7 +710,7 @@ void TreeView::LayoutEditor() { DCHECK(selected_node_); // Position the editor so that its text aligns with the text we drew. - gfx::Rect row_bounds = GetBoundsForNode(selected_node_); + gfx::Rect row_bounds = GetForegroundBoundsForNode(selected_node_); row_bounds.set_x( GetMirroredXWithWidthInView(row_bounds.x(), row_bounds.width())); row_bounds.set_x(row_bounds.x() + text_offset_); @@ -745,7 +727,7 @@ void TreeView::LayoutEditor() { void TreeView::SchedulePaintForNode(InternalNode* node) { if (!node) return; // Explicitly allow NULL to be passed in. - SchedulePaintInRect(GetBoundsForNode(node)); + SchedulePaintInRect(GetBackgroundBoundsForNode(node)); } void TreeView::PaintRows(gfx::Canvas* canvas, @@ -771,7 +753,15 @@ void TreeView::PaintRow(gfx::Canvas* canvas, InternalNode* node, int row, int depth) { - gfx::Rect bounds(GetBoundsForNodeImpl(node, row, depth)); + gfx::Rect bounds(GetForegroundBoundsForNodeImpl(node, row, depth)); + const SkColor selected_row_bg_color = GetNativeTheme()->GetSystemColor( + text_background_color_id(HasFocus() || editing_)); + + // Paint the row background. + if (PlatformStyle::kTreeViewSelectionPaintsEntireRow && + selected_node_ == node) { + canvas->FillRect(GetBackgroundBoundsForNode(node), selected_row_bg_color); + } if (model_->GetChildCount(node->model_node())) PaintExpandControl(canvas, bounds, node->is_expanded()); @@ -795,29 +785,34 @@ void TreeView::PaintRow(gfx::Canvas* canvas, icon, icon_x, bounds.y() + (bounds.height() - icon.height()) / 2); - if (!editing_ || node != selected_node_) { - gfx::Rect text_bounds(bounds.x() + text_offset_, bounds.y(), - bounds.width() - text_offset_, bounds.height()); - if (base::i18n::IsRTL()) - text_bounds.set_x(bounds.x()); - if (node == selected_node_) { - const SkColor bg_color = GetNativeTheme()->GetSystemColor( - text_background_color_id(HasFocus())); - canvas->FillRect(text_bounds, bg_color); - if (HasFocus()) - canvas->DrawFocusRect(text_bounds); - } - const ui::NativeTheme::ColorId color_id = - text_color_id(HasFocus(), node == selected_node_); - const gfx::Rect internal_bounds( - text_bounds.x() + kTextHorizontalPadding, - text_bounds.y() + kTextVerticalPadding, - text_bounds.width() - kTextHorizontalPadding * 2, - text_bounds.height() - kTextVerticalPadding * 2); - canvas->DrawStringRect(node->model_node()->GetTitle(), font_list_, - GetNativeTheme()->GetSystemColor(color_id), - internal_bounds); + // Paint the text background and text. In edit mode, the selected node is a + // separate editing control, so it does not need to be painted here. + if (editing_ && selected_node_ == node) + return; + + gfx::Rect text_bounds(GetTextBoundsForNode(node)); + if (base::i18n::IsRTL()) + text_bounds.set_x(bounds.x()); + + // Paint the background on the selected row. + if (!PlatformStyle::kTreeViewSelectionPaintsEntireRow && + node == selected_node_) { + canvas->FillRect(text_bounds, selected_row_bg_color); + if (HasFocus()) + canvas->DrawFocusRect(text_bounds); } + + // Paint the text. + const ui::NativeTheme::ColorId color_id = + text_color_id(HasFocus(), node == selected_node_); + const gfx::Rect internal_bounds( + text_bounds.x() + kTextHorizontalPadding, + text_bounds.y() + kTextVerticalPadding, + text_bounds.width() - kTextHorizontalPadding * 2, + text_bounds.height() - kTextVerticalPadding * 2); + canvas->DrawStringRect(node->model_node()->GetTitle(), font_list_, + GetNativeTheme()->GetSystemColor(color_id), + internal_bounds); } void TreeView::PaintExpandControl(gfx::Canvas* canvas, @@ -868,15 +863,31 @@ TreeView::InternalNode* TreeView::GetInternalNodeForModelNode( model_->GetIndexOf(parent_internal_node->model_node(), model_node)); } -gfx::Rect TreeView::GetBoundsForNode(InternalNode* node) { +gfx::Rect TreeView::GetBackgroundBoundsForNode(InternalNode* node) { + if (!PlatformStyle::kTreeViewSelectionPaintsEntireRow) + return GetForegroundBoundsForNode(node); + + int row, ignored_depth; + row = GetRowForInternalNode(node, &ignored_depth); + return gfx::Rect(bounds().x(), row * row_height_ + kVerticalInset, + bounds().width(), row_height_); +} + +gfx::Rect TreeView::GetForegroundBoundsForNode(InternalNode* node) { int row, depth; row = GetRowForInternalNode(node, &depth); - return GetBoundsForNodeImpl(node, row, depth); + return GetForegroundBoundsForNodeImpl(node, row, depth); } -gfx::Rect TreeView::GetBoundsForNodeImpl(InternalNode* node, - int row, - int depth) { +gfx::Rect TreeView::GetTextBoundsForNode(InternalNode* node) { + gfx::Rect bounds(GetForegroundBoundsForNode(node)); + bounds.Inset(text_offset_, 0, 0, 0); + return bounds; +} + +gfx::Rect TreeView::GetForegroundBoundsForNodeImpl(InternalNode* node, + int row, + int depth) { gfx::Rect rect(depth * kIndent + kHorizontalInset, row * row_height_ + kVerticalInset, text_offset_ + node->text_width() + @@ -906,6 +917,21 @@ int TreeView::GetRowForInternalNode(InternalNode* node, int* depth) { return row; } +TreeView::InternalNode* TreeView::GetNodeAtPoint(const gfx::Point& point) { + int row = (point.y() - kVerticalInset) / row_height_; + int depth = -1; + InternalNode* node = GetNodeByRow(row, &depth); + if (!node) + return nullptr; + + // If the entire row gets a selected background, clicking anywhere in the row + // serves to hit this node. + if (PlatformStyle::kTreeViewSelectionPaintsEntireRow) + return node; + gfx::Rect bounds(GetForegroundBoundsForNodeImpl(node, row, depth)); + return bounds.Contains(point) ? node : nullptr; +} + TreeView::InternalNode* TreeView::GetNodeByRow(int row, int* depth) { int current_row = root_row(); *depth = 0; @@ -1010,10 +1036,27 @@ bool TreeView::ExpandImpl(TreeModelNode* model_node) { PrefixSelector* TreeView::GetPrefixSelector() { if (!selector_) - selector_.reset(new PrefixSelector(this)); + selector_.reset(new PrefixSelector(this, this)); return selector_.get(); } +bool TreeView::IsPointInExpandControl(InternalNode* node, + const gfx::Point& point) { + if (!model_->GetChildCount(node->model_node())) + return false; + + int depth = -1; + int row = GetRowForInternalNode(node, &depth); + + int arrow_dx = depth * kIndent + kHorizontalInset; + gfx::Rect arrow_bounds(bounds().x() + arrow_dx, + row * row_height_ + kVerticalInset, kArrowRegionSize, + row_height_); + if (base::i18n::IsRTL()) + arrow_bounds.set_x(bounds().width() - arrow_dx - kArrowRegionSize); + return arrow_bounds.Contains(point); +} + // InternalNode ---------------------------------------------------------------- TreeView::InternalNode::InternalNode() diff --git a/chromium/ui/views/controls/tree/tree_view.h b/chromium/ui/views/controls/tree/tree_view.h index 2dc764e7f56..5eb096e3d2f 100644 --- a/chromium/ui/views/controls/tree/tree_view.h +++ b/chromium/ui/views/controls/tree/tree_view.h @@ -35,7 +35,8 @@ class PrefixSelector; // Note on implementation. This implementation doesn't scale well. In particular // it does not store any row information, but instead calculates it as // necessary. But it's more than adequate for current uses. -class VIEWS_EXPORT TreeView : public ui::TreeModelObserver, +class VIEWS_EXPORT TreeView : public View, + public ui::TreeModelObserver, public TextfieldController, public FocusChangeListener, public PrefixDelegate { @@ -294,16 +295,29 @@ class VIEWS_EXPORT TreeView : public ui::TreeModelObserver, ui::TreeModelNode* model_node, GetInternalNodeCreateType create_type); - // Returns the bounds for a node. - gfx::Rect GetBoundsForNode(InternalNode* node); + // Returns the bounds for a node's background. + gfx::Rect GetBackgroundBoundsForNode(InternalNode* node); - // Implementation of GetBoundsForNode. Separated out as some callers already - // know the row/depth. - gfx::Rect GetBoundsForNodeImpl(InternalNode* node, int row, int depth); + // Return the bounds for a node's foreground, which is the part containing the + // expand/collapse symbol (if any), the icon (if any), and the text label. + gfx::Rect GetForegroundBoundsForNode(InternalNode* node); + + // Returns the bounds for a node's text label. + gfx::Rect GetTextBoundsForNode(InternalNode* node); + + // Implementation of GetTextBoundsForNode. Separated out as some callers + // already know the row/depth. + gfx::Rect GetForegroundBoundsForNodeImpl(InternalNode* node, + int row, + int depth); // Returns the row and depth of a node. int GetRowForInternalNode(InternalNode* node, int* depth); + // Returns the InternalNode (if any) whose foreground bounds contain |point|. + // If no node's foreground contains |point|, this function returns nullptr. + InternalNode* GetNodeAtPoint(const gfx::Point& point); + // Returns the row and depth of the specified node. InternalNode* GetNodeByRow(int row, int* depth); @@ -322,7 +336,7 @@ class VIEWS_EXPORT TreeView : public ui::TreeModelObserver, void CollapseOrSelectParent(); // If the selected node is collapsed, it's expanded. Otherwise the first child - // is seleected. + // is selected. void ExpandOrSelectChild(); // Implementation of Expand(). Returns true if at least one node was expanded @@ -331,6 +345,10 @@ class VIEWS_EXPORT TreeView : public ui::TreeModelObserver, PrefixSelector* GetPrefixSelector(); + // Returns whether |point| is in the bounds of |node|'s expand/collapse + // control. + bool IsPointInExpandControl(InternalNode* node, const gfx::Point& point); + // The model, may be null. ui::TreeModel* model_; diff --git a/chromium/ui/views/controls/tree/tree_view_unittest.cc b/chromium/ui/views/controls/tree/tree_view_unittest.cc index e87956a254e..873f07bf70e 100644 --- a/chromium/ui/views/controls/tree/tree_view_unittest.cc +++ b/chromium/ui/views/controls/tree/tree_view_unittest.cc @@ -7,6 +7,7 @@ #include <string> #include "base/macros.h" +#include "base/memory/ptr_util.h" #include "base/strings/string_util.h" #include "base/strings/utf_string_conversions.h" #include "ui/base/models/tree_node_model.h" @@ -39,7 +40,7 @@ class TestNode : public TreeNode<TestNode> { // 'c' class TreeViewTest : public ViewsTestBase { public: - TreeViewTest() : model_(new TestNode) { + TreeViewTest() : model_(base::MakeUnique<TestNode>()) { static_cast<TestNode*>(model_.GetRoot())->SetTitle(ASCIIToUTF16("root")); Add(model_.GetRoot(), 0, "a"); Add(Add(model_.GetRoot(), 1, "b"), 0, "b1"); @@ -65,7 +66,7 @@ class TreeViewTest : public ViewsTestBase { int GetRowCount(); PrefixSelector* selector() { return tree_.GetPrefixSelector(); } - ui::TreeNodeModel<TestNode > model_; + ui::TreeNodeModel<TestNode> model_; TreeView tree_; private: @@ -79,10 +80,9 @@ class TreeViewTest : public ViewsTestBase { TestNode* TreeViewTest::Add(TestNode* parent, int index, const std::string& title) { - TestNode* new_node = new TestNode; + std::unique_ptr<TestNode> new_node = base::MakeUnique<TestNode>(); new_node->SetTitle(ASCIIToUTF16(title)); - model_.Add(parent, new_node, index); - return new_node; + return model_.Add(parent, std::move(new_node), index); } std::string TreeViewTest::TreeViewContentsAsString() { @@ -254,19 +254,19 @@ TEST_F(TreeViewTest, TreeNodesRemoved) { // effect the tree. tree_.Expand(GetNodeByTitle("b")); tree_.Collapse(GetNodeByTitle("b")); - delete model_.Remove(GetNodeByTitle("b1")->parent(), GetNodeByTitle("b1")); + model_.Remove(GetNodeByTitle("b1")->parent(), GetNodeByTitle("b1")); EXPECT_EQ("root [a b c]", TreeViewContentsAsString()); EXPECT_EQ("root", GetSelectedNodeTitle()); EXPECT_EQ(4, GetRowCount()); // Remove 'b'. - delete model_.Remove(GetNodeByTitle("b")->parent(), GetNodeByTitle("b")); + model_.Remove(GetNodeByTitle("b")->parent(), GetNodeByTitle("b")); EXPECT_EQ("root [a c]", TreeViewContentsAsString()); EXPECT_EQ("root", GetSelectedNodeTitle()); EXPECT_EQ(3, GetRowCount()); // Remove 'c11', shouldn't visually change anything. - delete model_.Remove(GetNodeByTitle("c11")->parent(), GetNodeByTitle("c11")); + model_.Remove(GetNodeByTitle("c11")->parent(), GetNodeByTitle("c11")); EXPECT_EQ("root [a c]", TreeViewContentsAsString()); EXPECT_EQ("root", GetSelectedNodeTitle()); EXPECT_EQ(3, GetRowCount()); @@ -274,7 +274,7 @@ TEST_F(TreeViewTest, TreeNodesRemoved) { // Select 'c1', remove 'c' and make sure selection changes. tree_.SetSelectedNode(GetNodeByTitle("c1")); EXPECT_EQ("c1", GetSelectedNodeTitle()); - delete model_.Remove(GetNodeByTitle("c")->parent(), GetNodeByTitle("c")); + model_.Remove(GetNodeByTitle("c")->parent(), GetNodeByTitle("c")); EXPECT_EQ("root [a]", TreeViewContentsAsString()); EXPECT_EQ("root", GetSelectedNodeTitle()); EXPECT_EQ(2, GetRowCount()); @@ -284,7 +284,7 @@ TEST_F(TreeViewTest, TreeNodesRemoved) { // selection should change to 'a'. Add(GetNodeByTitle("root"), 1, "b"); tree_.SetSelectedNode(GetNodeByTitle("b")); - delete model_.Remove(GetNodeByTitle("b")->parent(), GetNodeByTitle("b")); + model_.Remove(GetNodeByTitle("b")->parent(), GetNodeByTitle("b")); EXPECT_EQ("root [a]", TreeViewContentsAsString()); EXPECT_EQ("a", GetSelectedNodeTitle()); EXPECT_EQ(1, GetRowCount()); diff --git a/chromium/ui/views/controls/webview/BUILD.gn b/chromium/ui/views/controls/webview/BUILD.gn index 2c16f507e1e..b8daeebab2a 100644 --- a/chromium/ui/views/controls/webview/BUILD.gn +++ b/chromium/ui/views/controls/webview/BUILD.gn @@ -44,7 +44,7 @@ component("webview") { } } -source_set("test_support") { +static_library("test_support") { testonly = true sources = [ "../../test/webview_test_helper.cc", diff --git a/chromium/ui/views/controls/webview/webview.gyp b/chromium/ui/views/controls/webview/webview.gyp deleted file mode 100644 index f814e404aae..00000000000 --- a/chromium/ui/views/controls/webview/webview.gyp +++ /dev/null @@ -1,51 +0,0 @@ -# Copyright (c) 2012 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. - -{ - 'variables': { - 'chromium_code': 1, - }, - 'targets': [ - { - # GN version: //ui/views/controls/webview - 'target_name': 'webview', - 'type': '<(component)', - 'dependencies': [ - '../../../../base/base.gyp:base', - '../../../../base/base.gyp:base_i18n', - '../../../../base/third_party/dynamic_annotations/dynamic_annotations.gyp:dynamic_annotations', - '../../../../content/content.gyp:content_browser', - '../../../../skia/skia.gyp:skia', - '../../../../url/url.gyp:url_lib', - '../../../base/ui_base.gyp:ui_base', - '../../../content_accelerators/ui_content_accelerators.gyp:ui_content_accelerators', - '../../../events/events.gyp:events', - '../../../gfx/gfx.gyp:gfx', - '../../../gfx/gfx.gyp:gfx_geometry', - '../../../web_dialogs/web_dialogs.gyp:web_dialogs', - '../../views.gyp:views', - ], - 'defines': [ - 'WEBVIEW_IMPLEMENTATION', - ], - 'sources': [ - # Note: file list duplicated in GN build. - 'unhandled_keyboard_event_handler.cc', - 'unhandled_keyboard_event_handler.h', - 'unhandled_keyboard_event_handler_mac.mm', - 'unhandled_keyboard_event_handler_win.cc', - 'web_dialog_view.cc', - 'web_dialog_view.h', - 'webview.cc', - 'webview.h', - 'webview_export.h', - ], - 'conditions': [ - ['OS=="linux" or OS=="android"', { - 'sources': [ 'unhandled_keyboard_event_handler_default.cc', ], - }], - ], - }, - ], -} diff --git a/chromium/ui/views/controls/webview/webview_tests.gyp b/chromium/ui/views/controls/webview/webview_tests.gyp deleted file mode 100644 index f85385b415f..00000000000 --- a/chromium/ui/views/controls/webview/webview_tests.gyp +++ /dev/null @@ -1,45 +0,0 @@ -# Copyright 2014 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. - -{ - 'variables': { - 'chromium_code': 1, - }, - 'targets': [ - { - # GN version: //ui/views/controls/webview:test_support - 'target_name': 'webview_test_support', - 'type': 'static_library', - 'dependencies': [ - '../../../../base/base.gyp:base', - '../../../../content/content.gyp:content', - '../../../../content/content_shell_and_tests.gyp:test_support_content', - '../../../../ipc/ipc.gyp:test_support_ipc', - '../../../../skia/skia.gyp:skia', - '../../../../testing/gtest.gyp:gtest', - '../../../base/ui_base.gyp:ui_base', - '../../../events/events.gyp:events', - '../../../gfx/gfx.gyp:gfx', - '../../../gfx/gfx.gyp:gfx_geometry', - '../../views.gyp:views', - '../../views.gyp:views_test_support', - 'webview.gyp:webview', - ], - 'include_dirs': [ - '../../../..', - ], - 'sources': [ - '../../test/webview_test_helper.cc', - '../../test/webview_test_helper.h', - ], - 'conditions': [ - ['use_aura==1', { - 'dependencies': [ - '../../../aura/aura.gyp:aura', - ], - }], - ], - }, - ], -} diff --git a/chromium/ui/views/corewm/DEPS b/chromium/ui/views/corewm/DEPS index c50f6946b18..7acdcd1c9c0 100644 --- a/chromium/ui/views/corewm/DEPS +++ b/chromium/ui/views/corewm/DEPS @@ -15,6 +15,7 @@ specific_include_rules = { "tooltip_aura.cc": [ "+ui/views/background.h", "+ui/views/border.h", + "+ui/views/painter.h", "+ui/views/widget/widget.h", "+ui/views/view.h", ], diff --git a/chromium/ui/views/corewm/tooltip_aura.cc b/chromium/ui/views/corewm/tooltip_aura.cc index e51a87265a5..3c58d0329d5 100644 --- a/chromium/ui/views/corewm/tooltip_aura.cc +++ b/chromium/ui/views/corewm/tooltip_aura.cc @@ -18,6 +18,7 @@ #include "ui/native_theme/native_theme.h" #include "ui/views/background.h" #include "ui/views/border.h" +#include "ui/views/painter.h" #include "ui/views/view.h" #include "ui/views/widget/widget.h" @@ -31,6 +32,15 @@ const int kTooltipMaxWidthPixels = 400; const int kCursorOffsetX = 10; const int kCursorOffsetY = 15; +// TODO(varkha): Update if native widget can be transparent on Linux. +bool CanUseTranslucentTooltipWidget() { +#if defined(OS_LINUX) && !defined(OS_CHROMEOS) + return false; +#else + return true; +#endif +} + // Creates a widget of type TYPE_TOOLTIP views::Widget* CreateTooltipWidget(aura::Window* tooltip_window) { views::Widget* widget = new views::Widget; @@ -42,6 +52,9 @@ views::Widget* CreateTooltipWidget(aura::Window* tooltip_window) { DCHECK(params.context); params.keep_on_top = true; params.accept_events = false; + if (CanUseTranslucentTooltipWidget()) + params.opacity = views::Widget::InitParams::TRANSLUCENT_WINDOW; + params.shadow_type = views::Widget::InitParams::SHADOW_TYPE_NONE; widget->Init(params); return widget; } @@ -57,11 +70,12 @@ class TooltipAura::TooltipView : public views::View { TooltipView() : render_text_(gfx::RenderText::CreateInstance()), max_width_(0) { - const int kHorizontalPadding = 3; - const int kVerticalPadding = 2; - SetBorder(Border::CreateEmptyBorder( - kVerticalPadding, kHorizontalPadding, - kVerticalPadding, kHorizontalPadding)); + const int kHorizontalPadding = 8; + const int kVerticalPaddingTop = 4; + const int kVerticalPaddingBottom = 5; + SetBorder(Border::CreateEmptyBorder(kVerticalPaddingTop, kHorizontalPadding, + kVerticalPaddingBottom, + kHorizontalPadding)); set_owned_by_client(); render_text_->SetWordWrapBehavior(gfx::WRAP_LONG_WORDS); @@ -107,6 +121,22 @@ class TooltipAura::TooltipView : public views::View { render_text_->SetColor(color); } + void SetBackgroundColor(SkColor background_color) { + // Corner radius of tooltip background. + const float kTooltipCornerRadius = 2.f; + views::Background* background = + CanUseTranslucentTooltipWidget() + ? views::Background::CreateBackgroundPainter( + true, views::Painter::CreateSolidRoundRectPainter( + background_color, kTooltipCornerRadius)) + : views::Background::CreateSolidBackground(background_color); + set_background(background); + // Force the text color to be readable when |background_color| is not + // opaque. + render_text_->set_subpixel_rendering_suppressed( + SkColorGetA(background_color) != 0xFF); + } + void SetMaxWidth(int width) { max_width_ = width; ResetDisplayRect(); @@ -188,10 +218,8 @@ void TooltipAura::SetText(aura::Window* window, SetTooltipBounds(location, tooltip_view_->GetPreferredSize()); ui::NativeTheme* native_theme = widget_->GetNativeTheme(); - tooltip_view_->set_background( - views::Background::CreateSolidBackground( - native_theme->GetSystemColor( - ui::NativeTheme::kColorId_TooltipBackground))); + tooltip_view_->SetBackgroundColor(native_theme->GetSystemColor( + ui::NativeTheme::kColorId_TooltipBackground)); tooltip_view_->SetForegroundColor(native_theme->GetSystemColor( ui::NativeTheme::kColorId_TooltipText)); } diff --git a/chromium/ui/views/examples/BUILD.gn b/chromium/ui/views/examples/BUILD.gn index 0d3371ddb94..ef70dd3a90d 100644 --- a/chromium/ui/views/examples/BUILD.gn +++ b/chromium/ui/views/examples/BUILD.gn @@ -12,12 +12,12 @@ component("views_examples_lib") { "bubble_example.h", "button_example.cc", "button_example.h", + "button_sticker_sheet.cc", + "button_sticker_sheet.h", "checkbox_example.cc", "checkbox_example.h", "combobox_example.cc", "combobox_example.h", - "double_split_view_example.cc", - "double_split_view_example.h", "example_base.cc", "example_base.h", "example_combobox_model.cc", @@ -40,8 +40,6 @@ component("views_examples_lib") { "radio_button_example.h", "scroll_view_example.cc", "scroll_view_example.h", - "single_split_view_example.cc", - "single_split_view_example.h", "slider_example.cc", "slider_example.h", "tabbed_pane_example.cc", @@ -54,6 +52,8 @@ component("views_examples_lib") { "textfield_example.h", "throbber_example.cc", "throbber_example.h", + "toggle_button_example.cc", + "toggle_button_example.h", "tree_view_example.cc", "tree_view_example.h", "vector_example.cc", @@ -107,6 +107,7 @@ executable("views_examples_exe") { "//base/test:test_support", "//build/config/sanitizers:deps", "//build/win:default_exe_manifest", + "//cc/surfaces", "//ui/base", "//ui/compositor", "//ui/compositor:test_support", diff --git a/chromium/ui/views/examples/DEPS b/chromium/ui/views/examples/DEPS index 47e7bcdb2eb..dcab1974d68 100644 --- a/chromium/ui/views/examples/DEPS +++ b/chromium/ui/views/examples/DEPS @@ -1,4 +1,5 @@ include_rules = [ + "+cc/surfaces/surface_manager.h", "+content/public", "+content/shell", "+sandbox", diff --git a/chromium/ui/views/examples/button_example.cc b/chromium/ui/views/examples/button_example.cc index b3a2f13a4fc..18410cf614a 100644 --- a/chromium/ui/views/examples/button_example.cc +++ b/chromium/ui/views/examples/button_example.cc @@ -59,15 +59,15 @@ void ButtonExample::CreateExampleView(View* container) { container->AddChildView(new BlueButton(this, ASCIIToUTF16("Blue Button"))); - container->AddChildView(MdTextButton::CreateMdButton( - nullptr, base::ASCIIToUTF16("Material design"))); - MdTextButton* md_button = MdTextButton::CreateMdButton( - nullptr, base::ASCIIToUTF16("Default")); + container->AddChildView( + MdTextButton::Create(nullptr, base::ASCIIToUTF16("Material design"))); + MdTextButton* md_button = + MdTextButton::Create(nullptr, base::ASCIIToUTF16("Default")); md_button->SetIsDefault(true); container->AddChildView(md_button); - md_button = MdTextButton::CreateMdButton( - nullptr, base::ASCIIToUTF16("Call to action")); - md_button->SetCallToAction(true); + md_button = + MdTextButton::Create(nullptr, base::ASCIIToUTF16("Call to action")); + md_button->SetProminent(true); container->AddChildView(md_button); ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance(); diff --git a/chromium/ui/views/examples/button_sticker_sheet.cc b/chromium/ui/views/examples/button_sticker_sheet.cc new file mode 100644 index 00000000000..dc98a833cd2 --- /dev/null +++ b/chromium/ui/views/examples/button_sticker_sheet.cc @@ -0,0 +1,131 @@ +// Copyright 2016 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/examples/button_sticker_sheet.h" + +#include "base/strings/utf_string_conversions.h" +#include "ui/base/material_design/material_design_controller.h" +#include "ui/views/controls/button/button.h" +#include "ui/views/controls/button/md_text_button.h" +#include "ui/views/layout/grid_layout.h" + +namespace views { +namespace examples { + +namespace { + +// The id of the only ColumnSet in a stretchy grid (see below). +const int kStretchyGridColumnSetId = 0; + +// Creates a stretchy grid layout: there are |ncols| columns, separated from +// each other by padding columns, and all non-padding columns have equal flex +// weight and will flex in either dimension as needed. The resulting grid layout +// has only one ColumnSet (numbered kStretchyGridColumnSetId). +GridLayout* MakeStretchyGridLayout(View* host, int ncols) { + const float kPaddingResizesEqually = 1.0; + const int kPaddingWidth = 30; + const GridLayout::Alignment kColumnStretchesHorizontally = GridLayout::FILL; + const GridLayout::Alignment kColumnStretchesVertically = GridLayout::FILL; + const float kColumnDoesNotResize = 0.0; + const GridLayout::SizeType kColumnUsesFixedSize = GridLayout::FIXED; + const int kColumnWidth = 96; + + GridLayout* layout = new GridLayout(host); + ColumnSet* columns = layout->AddColumnSet(kStretchyGridColumnSetId); + for (int i = 0; i < ncols; ++i) { + if (i != 0) + columns->AddPaddingColumn(kPaddingResizesEqually, kPaddingWidth); + columns->AddColumn(kColumnStretchesHorizontally, kColumnStretchesVertically, + kColumnDoesNotResize, kColumnUsesFixedSize, kColumnWidth, + kColumnWidth); + } + return layout; +} + +View* MakePlainLabel(const std::string& text) { + return new Label(base::ASCIIToUTF16(text)); +} + +// Add a row containing a label whose text is |label_text| and then all the +// views in |views| to the supplied GridLayout, with padding between rows. +void AddLabelledRowToGridLayout(GridLayout* layout, + const std::string& label_text, + std::vector<View*> views) { + const float kRowDoesNotResizeVertically = 0.0; + const int kPaddingRowHeight = 8; + layout->StartRow(kRowDoesNotResizeVertically, kStretchyGridColumnSetId); + layout->AddView(MakePlainLabel(label_text)); + for (const auto& view : views) + layout->AddView(view); + // This gets added extraneously after the last row, but it doesn't hurt and + // means there's no need to keep track of whether to add it or not. + layout->AddPaddingRow(kRowDoesNotResizeVertically, kPaddingRowHeight); +} + +// Constructs a pair of MdTextButtons in the specified |state| with the +// specified |listener|, and returns them in |*primary| and |*secondary|. The +// button in |*primary| is a call-to-action button, and the button in +// |*secondary| is a regular button. +void MakeButtonsInState(MdTextButton** primary, + MdTextButton** secondary, + ButtonListener* listener, + Button::ButtonState state) { + const base::string16 button_text = base::ASCIIToUTF16("Button"); + *primary = MdTextButton::Create(listener, button_text); + (*primary)->SetProminent(true); + (*primary)->SetState(state); + + *secondary = MdTextButton::Create(listener, button_text); + (*secondary)->SetState(state); +} + +} // namespace + +ButtonStickerSheet::ButtonStickerSheet() + : ExampleBase("Button (Sticker Sheet)") {} + +ButtonStickerSheet::~ButtonStickerSheet() {} + +void ButtonStickerSheet::CreateExampleView(View* container) { + GridLayout* layout = MakeStretchyGridLayout(container, 3); + container->SetLayoutManager(layout); + + if (!ui::MaterialDesignController::IsSecondaryUiMaterial()) { + const char* kNeedsMdWarning = + "This will look wrong without --secondary-ui-md."; + layout->StartRow(0, 0); + layout->AddView(MakePlainLabel(kNeedsMdWarning), 3, 1); + } + + // The title row has an empty row label. + AddLabelledRowToGridLayout( + layout, std::string(), + {MakePlainLabel("Primary"), MakePlainLabel("Secondary")}); + + MdTextButton* primary = nullptr; + MdTextButton* secondary = nullptr; + + MakeButtonsInState(&primary, &secondary, this, Button::STATE_NORMAL); + AddLabelledRowToGridLayout(layout, "Default", {primary, secondary}); + MakeButtonsInState(&primary, &secondary, this, Button::STATE_NORMAL); + AddLabelledRowToGridLayout(layout, "Normal", {primary, secondary}); + MakeButtonsInState(&primary, &secondary, this, Button::STATE_HOVERED); + AddLabelledRowToGridLayout(layout, "Hovered", {primary, secondary}); + MakeButtonsInState(&primary, &secondary, this, Button::STATE_PRESSED); + AddLabelledRowToGridLayout(layout, "Pressed", {primary, secondary}); + MakeButtonsInState(&primary, &secondary, this, Button::STATE_DISABLED); + AddLabelledRowToGridLayout(layout, "Disabled", {primary, secondary}); + + MakeButtonsInState(&primary, &secondary, this, Button::STATE_NORMAL); + primary->OnFocus(); + secondary->OnFocus(); + AddLabelledRowToGridLayout(layout, "Focused", {primary, secondary}); +} + +void ButtonStickerSheet::ButtonPressed(Button* button, const ui::Event& event) { + // Ignore button presses. +} + +} // namespace examples +} // namespace views diff --git a/chromium/ui/views/examples/button_sticker_sheet.h b/chromium/ui/views/examples/button_sticker_sheet.h new file mode 100644 index 00000000000..3b8888e06ee --- /dev/null +++ b/chromium/ui/views/examples/button_sticker_sheet.h @@ -0,0 +1,38 @@ +// Copyright 2016 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_EXAMPLES_BUTTON_STICKER_SHEET_H_ +#define UI_VIEWS_EXAMPLES_BUTTON_STICKER_SHEET_H_ + +#include "base/macros.h" +#include "ui/views/controls/button/button.h" +#include "ui/views/examples/example_base.h" + +namespace views { +namespace examples { + +// An "example" that displays a sticker sheet of all the available material +// design button styles. This example only looks right with `--secondary-ui-md`. +// It is designed to be as visually similar to the UI Harmony spec's sticker +// sheet for buttons as possible. +class VIEWS_EXAMPLES_EXPORT ButtonStickerSheet : public ExampleBase, + public ButtonListener { + public: + ButtonStickerSheet(); + ~ButtonStickerSheet() override; + + // ExampleBase: + void CreateExampleView(View* container) override; + + // ButtonListener: + void ButtonPressed(Button* sender, const ui::Event& event) override; + + private: + DISALLOW_COPY_AND_ASSIGN(ButtonStickerSheet); +}; + +} // namespace examples +} // namespace views + +#endif // UI_VIEWS_EXAMPLES_BUTTON_STICKER_SHEET_H_ diff --git a/chromium/ui/views/examples/combobox_example.cc b/chromium/ui/views/examples/combobox_example.cc index aedc8fbdef7..790d024e37c 100644 --- a/chromium/ui/views/examples/combobox_example.cc +++ b/chromium/ui/views/examples/combobox_example.cc @@ -23,7 +23,7 @@ int ComboboxModelExample::GetItemCount() const { } base::string16 ComboboxModelExample::GetItemAt(int index) { - return base::UTF8ToUTF16(base::StringPrintf("Item %d", index)); + return base::UTF8ToUTF16(base::StringPrintf("%c item", 'A' + index)); } ComboboxExample::ComboboxExample() : ExampleBase("Combo Box") { @@ -56,7 +56,7 @@ void ComboboxExample::CreateExampleView(View* container) { container->SetLayoutManager(new BoxLayout( BoxLayout::kVertical, - 1, 1, 1)); + 0, 10, 5)); container->AddChildView(combobox_); container->AddChildView(disabled_combobox_); container->AddChildView(action_combobox_); diff --git a/chromium/ui/views/examples/double_split_view_example.cc b/chromium/ui/views/examples/double_split_view_example.cc deleted file mode 100644 index 78ecfc1d833..00000000000 --- a/chromium/ui/views/examples/double_split_view_example.cc +++ /dev/null @@ -1,83 +0,0 @@ -// Copyright (c) 2012 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/examples/double_split_view_example.h" - -#include "base/macros.h" -#include "ui/views/background.h" -#include "ui/views/controls/single_split_view.h" -#include "ui/views/layout/grid_layout.h" - -namespace views { -namespace examples { - -namespace { - -// DoubleSplitViews's content, which draws gradient color on background. -class SplittedView : public View { - public: - SplittedView(); - ~SplittedView() override; - - void SetColor(SkColor from, SkColor to); - - // View: - gfx::Size GetMinimumSize() const override; - - private: - DISALLOW_COPY_AND_ASSIGN(SplittedView); -}; - -SplittedView::SplittedView() { - SetColor(SK_ColorRED, SK_ColorGREEN); -} - -SplittedView::~SplittedView() { -} - -void SplittedView::SetColor(SkColor from, SkColor to) { - set_background(Background::CreateVerticalGradientBackground(from, to)); -} - -gfx::Size SplittedView::GetMinimumSize() const { - return gfx::Size(10, 10); -} - -} // namespace - -DoubleSplitViewExample::DoubleSplitViewExample() - : ExampleBase("Double Split View") { -} - -DoubleSplitViewExample::~DoubleSplitViewExample() { -} - -void DoubleSplitViewExample::CreateExampleView(View* container) { - SplittedView* splitted_view_1 = new SplittedView(); - SplittedView* splitted_view_2 = new SplittedView(); - SplittedView* splitted_view_3 = new SplittedView(); - - inner_single_split_view_ = new SingleSplitView( - splitted_view_1, splitted_view_2, - SingleSplitView::HORIZONTAL_SPLIT, - NULL); - - outer_single_split_view_ = new SingleSplitView( - inner_single_split_view_, splitted_view_3, - SingleSplitView::HORIZONTAL_SPLIT, - NULL); - - GridLayout* layout = new GridLayout(container); - container->SetLayoutManager(layout); - - // Add scroll view. - ColumnSet* column_set = layout->AddColumnSet(0); - column_set->AddColumn(GridLayout::FILL, GridLayout::FILL, 1, - GridLayout::USE_PREF, 0, 0); - layout->StartRow(1, 0); - layout->AddView(outer_single_split_view_); -} - -} // namespace examples -} // namespace views diff --git a/chromium/ui/views/examples/double_split_view_example.h b/chromium/ui/views/examples/double_split_view_example.h deleted file mode 100644 index 231ddc67adc..00000000000 --- a/chromium/ui/views/examples/double_split_view_example.h +++ /dev/null @@ -1,35 +0,0 @@ -// 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_EXAMPLES_DOUBLE_SPLIT_VIEW_EXAMPLE_H_ -#define UI_VIEWS_EXAMPLES_DOUBLE_SPLIT_VIEW_EXAMPLE_H_ - -#include "base/macros.h" -#include "ui/views/examples/example_base.h" - -namespace views { -class SingleSplitView; - -namespace examples { - -class VIEWS_EXAMPLES_EXPORT DoubleSplitViewExample : public ExampleBase { - public: - DoubleSplitViewExample(); - ~DoubleSplitViewExample() override; - - // ExampleBase: - void CreateExampleView(View* container) override; - - private: - // The SingleSplitViews to be embedded. - SingleSplitView* outer_single_split_view_; - SingleSplitView* inner_single_split_view_; - - DISALLOW_COPY_AND_ASSIGN(DoubleSplitViewExample); -}; - -} // namespace examples -} // namespace views - -#endif // UI_VIEWS_EXAMPLES_DOUBLE_SPLIT_VIEW_EXAMPLE_H_ diff --git a/chromium/ui/views/examples/examples.gyp b/chromium/ui/views/examples/examples.gyp deleted file mode 100644 index 4038d615027..00000000000 --- a/chromium/ui/views/examples/examples.gyp +++ /dev/null @@ -1,205 +0,0 @@ -# Copyright 2014 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. -{ - 'variables': { - 'chromium_code': 1, - }, - 'targets': [ - { - # GN version: //ui/views/examples - 'target_name': 'views_examples_lib', - 'type': '<(component)', - 'dependencies': [ - '../../../base/base.gyp:base', - '../../../skia/skia.gyp:skia', - '../../../third_party/icu/icu.gyp:icui18n', - '../../../third_party/icu/icu.gyp:icuuc', - '../../base/ui_base.gyp:ui_base', - '../../events/events.gyp:events', - '../../gfx/gfx.gyp:gfx', - '../../gfx/gfx.gyp:gfx_geometry', - '../../gfx/gfx.gyp:gfx_range', - '../../gfx/gfx.gyp:gfx_vector_icons', - '../../resources/ui_resources.gyp:ui_resources', - '../../resources/ui_resources.gyp:ui_test_pak', - '../views.gyp:views', - ], - 'include_dirs': [ - '../../..', - ], - 'defines': [ - 'GFX_VECTOR_ICONS_UNSAFE', - 'VIEWS_EXAMPLES_IMPLEMENTATION', - ], - 'sources': [ - # Note: sources list duplicated in GN build. - 'bubble_example.cc', - 'bubble_example.h', - 'button_example.cc', - 'button_example.h', - 'checkbox_example.cc', - 'checkbox_example.h', - 'combobox_example.cc', - 'combobox_example.h', - 'double_split_view_example.cc', - 'double_split_view_example.h', - 'example_base.cc', - 'example_base.h', - 'example_combobox_model.cc', - 'example_combobox_model.h', - 'examples_window.cc', - 'examples_window.h', - 'label_example.cc', - 'label_example.h', - 'link_example.cc', - 'link_example.h', - 'menu_example.cc', - 'menu_example.h', - 'message_box_example.cc', - 'message_box_example.h', - 'multiline_example.cc', - 'multiline_example.h', - 'progress_bar_example.cc', - 'progress_bar_example.h', - 'radio_button_example.cc', - 'radio_button_example.h', - 'scroll_view_example.cc', - 'scroll_view_example.h', - 'single_split_view_example.cc', - 'single_split_view_example.h', - 'slider_example.cc', - 'slider_example.h', - 'tabbed_pane_example.cc', - 'tabbed_pane_example.h', - 'table_example.cc', - 'table_example.h', - 'text_example.cc', - 'text_example.h', - 'textfield_example.cc', - 'textfield_example.h', - 'throbber_example.cc', - 'throbber_example.h', - 'tree_view_example.cc', - 'tree_view_example.h', - 'views_examples_export.h', - 'vector_example.cc', - 'vector_example.h', - 'widget_example.cc', - 'widget_example.h', - ], - 'conditions': [ - ['OS=="win"', { - 'include_dirs': [ - '../../../third_party/wtl/include', - ], - # TODO(jschuh): crbug.com/167187 fix size_t to int truncations. - 'msvs_disabled_warnings': [ 4267, ], - }], - ['use_aura==1', { - 'dependencies': [ - '../../aura/aura.gyp:aura', - ], - }], - ], - }, # target_name: views_examples_lib - { - # GN version: //ui/views/examples:views_examples_exe - 'target_name': 'views_examples_exe', - 'type': 'executable', - 'dependencies': [ - '../../../base/base.gyp:base', - '../../../base/base.gyp:base_i18n', - '../../base/ui_base.gyp:ui_base', - '../../compositor/compositor.gyp:compositor', - '../../compositor/compositor.gyp:compositor_test_support', - '../../gfx/gfx.gyp:gfx', - '../../resources/ui_resources.gyp:ui_test_pak', - '../views.gyp:views', - '../views.gyp:views_test_support', - 'views_examples_lib', - ], - 'sources': [ - # Note: sources list duplicated in GN build. - 'examples_main.cc', - ], - 'conditions': [ - ['use_aura==1', { - 'dependencies': [ - '../../aura/aura.gyp:aura', - ], - }], - ], - }, # target_name: views_examples_exe - { - # GN version: //ui/views/examples:views_examples_with_content_lib - 'target_name': 'views_examples_with_content_lib', - 'type': '<(component)', - 'dependencies': [ - '../../../base/base.gyp:base', - '../../../content/content.gyp:content', - '../../../skia/skia.gyp:skia', - '../../../url/url.gyp:url_lib', - '../../events/events.gyp:events', - '../controls/webview/webview.gyp:webview', - '../views.gyp:views', - 'views_examples_lib', - ], - 'defines': [ - 'VIEWS_EXAMPLES_WITH_CONTENT_IMPLEMENTATION', - ], - 'sources': [ - # Note: sources list duplicated in GN build. - 'examples_window_with_content.cc', - 'examples_window_with_content.h', - 'views_examples_with_content_export.h', - 'webview_example.cc', - 'webview_example.h', - ], - }, # target_name: views_examples_with_content_lib - { - # GN version: //ui/views/examples/views_examples_with_content_exe - 'target_name': 'views_examples_with_content_exe', - 'type': 'executable', - 'dependencies': [ - '../resources/views_resources.gyp:views_resources', - '../../views_content_client/views_content_client.gyp:views_content_client', - 'views_examples_with_content_lib', - ], - 'sources': [ - # Note: sources list duplicated in GN build. - 'examples_with_content_main_exe.cc', - ], - 'conditions': [ - ['component=="shared_library"', { - 'dependencies': [ - '../../../base/base.gyp:base', - '../../../content/content.gyp:content', - ], - }], - ['OS=="win"', { - 'link_settings': { - 'libraries': [ - '-limm32.lib', - '-loleacc.lib', - ] - }, - 'msvs_settings': { - 'VCManifestTool': { - 'AdditionalManifestFiles': [ - 'views_examples.exe.manifest', - ], - }, - 'VCLinkerTool': { - 'SubSystem': '2', # Set /SUBSYSTEM:WINDOWS - }, - }, - 'dependencies': [ - '../../../sandbox/sandbox.gyp:sandbox', - '../../../content/content.gyp:sandbox_helper_win', - ], - }], - ], - }, # target_name: views_examples_with_content_exe - ], -} diff --git a/chromium/ui/views/examples/examples_main.cc b/chromium/ui/views/examples/examples_main.cc index bb074a0dabe..98f73c0c3b8 100644 --- a/chromium/ui/views/examples/examples_main.cc +++ b/chromium/ui/views/examples/examples_main.cc @@ -16,6 +16,7 @@ #include "base/run_loop.h" #include "base/test/test_discardable_memory_allocator.h" #include "build/build_config.h" +#include "cc/surfaces/surface_manager.h" #include "ui/base/ime/input_method_initializer.h" #include "ui/base/material_design/material_design_controller.h" #include "ui/base/resource/resource_bundle.h" @@ -67,8 +68,10 @@ int main(int argc, char** argv) { // The ContextFactory must exist before any Compositors are created. bool context_factory_for_test = false; + cc::SurfaceManager surface_manager; std::unique_ptr<ui::InProcessContextFactory> context_factory( - new ui::InProcessContextFactory(context_factory_for_test, nullptr)); + new ui::InProcessContextFactory(context_factory_for_test, + &surface_manager)); context_factory->set_use_test_surface(false); base::MessageLoopForUI message_loop; @@ -109,9 +112,8 @@ int main(int argc, char** argv) { display::Screen::SetScreenInstance(desktop_screen.get()); #endif - views::examples::ShowExamplesWindow( - views::examples::QUIT_ON_CLOSE, nullptr, - std::unique_ptr<ScopedVector<views::examples::ExampleBase>>()); + views::examples::ShowExamplesWindow(views::examples::QUIT_ON_CLOSE, nullptr, + nullptr); base::RunLoop().Run(); diff --git a/chromium/ui/views/examples/examples_window.cc b/chromium/ui/views/examples/examples_window.cc index 57be047069c..3809c2fbf9f 100644 --- a/chromium/ui/views/examples/examples_window.cc +++ b/chromium/ui/views/examples/examples_window.cc @@ -11,6 +11,7 @@ #include "base/macros.h" #include "base/memory/scoped_vector.h" #include "base/strings/utf_string_conversions.h" +#include "ui/base/l10n/l10n_util.h" #include "ui/base/models/combobox_model.h" #include "ui/base/ui_base_paths.h" #include "ui/views/background.h" @@ -18,9 +19,9 @@ #include "ui/views/controls/label.h" #include "ui/views/examples/bubble_example.h" #include "ui/views/examples/button_example.h" +#include "ui/views/examples/button_sticker_sheet.h" #include "ui/views/examples/checkbox_example.h" #include "ui/views/examples/combobox_example.h" -#include "ui/views/examples/double_split_view_example.h" #include "ui/views/examples/label_example.h" #include "ui/views/examples/link_example.h" #include "ui/views/examples/menu_example.h" @@ -29,13 +30,13 @@ #include "ui/views/examples/progress_bar_example.h" #include "ui/views/examples/radio_button_example.h" #include "ui/views/examples/scroll_view_example.h" -#include "ui/views/examples/single_split_view_example.h" #include "ui/views/examples/slider_example.h" #include "ui/views/examples/tabbed_pane_example.h" #include "ui/views/examples/table_example.h" #include "ui/views/examples/text_example.h" #include "ui/views/examples/textfield_example.h" #include "ui/views/examples/throbber_example.h" +#include "ui/views/examples/toggle_button_example.h" #include "ui/views/examples/tree_view_example.h" #include "ui/views/examples/vector_example.h" #include "ui/views/examples/widget_example.h" @@ -56,9 +57,9 @@ ScopedExamples CreateExamples() { ScopedExamples examples(new ScopedVector<ExampleBase>); examples->push_back(new BubbleExample); examples->push_back(new ButtonExample); + examples->push_back(new ButtonStickerSheet); examples->push_back(new CheckboxExample); examples->push_back(new ComboboxExample); - examples->push_back(new DoubleSplitViewExample); examples->push_back(new LabelExample); examples->push_back(new LinkExample); examples->push_back(new MenuExample); @@ -67,12 +68,12 @@ ScopedExamples CreateExamples() { examples->push_back(new ProgressBarExample); examples->push_back(new RadioButtonExample); examples->push_back(new ScrollViewExample); - examples->push_back(new SingleSplitViewExample); examples->push_back(new SliderExample); examples->push_back(new TabbedPaneExample); examples->push_back(new TableExample); examples->push_back(new TextExample); examples->push_back(new TextfieldExample); + examples->push_back(new ToggleButtonExample); examples->push_back(new ThrobberExample); examples->push_back(new TreeViewExample); examples->push_back(new VectorExample); @@ -182,7 +183,6 @@ class ExamplesWindowContents : public WidgetDelegateView, base::string16 GetWindowTitle() const override { return base::ASCIIToUTF16("Views Examples"); } - View* GetContentsView() override { return this; } void WindowClosing() override { instance_ = NULL; if (operation_ == QUIT_ON_CLOSE) diff --git a/chromium/ui/views/examples/menu_example.cc b/chromium/ui/views/examples/menu_example.cc index 378c90a76bc..c8c8368a73f 100644 --- a/chromium/ui/views/examples/menu_example.cc +++ b/chromium/ui/views/examples/menu_example.cc @@ -31,8 +31,6 @@ class ExampleMenuModel : public ui::SimpleMenuModel, // ui::SimpleMenuModel::Delegate: bool IsCommandIdChecked(int command_id) const override; bool IsCommandIdEnabled(int command_id) const override; - bool GetAcceleratorForCommandId(int command_id, - ui::Accelerator* accelerator) override; void ExecuteCommand(int command_id, int event_flags) override; private: @@ -119,13 +117,6 @@ bool ExampleMenuModel::IsCommandIdEnabled(int command_id) const { return command_id != COMMAND_GO_HOME; } -bool ExampleMenuModel::GetAcceleratorForCommandId( - int command_id, - ui::Accelerator* accelerator) { - // We don't use this in the example. - return false; -} - void ExampleMenuModel::ExecuteCommand(int command_id, int event_flags) { switch (command_id) { case COMMAND_DO_SOMETHING: { @@ -188,16 +179,12 @@ ExampleMenuButton::~ExampleMenuButton() { void ExampleMenuButton::OnMenuButtonClicked(MenuButton* source, const gfx::Point& point, const ui::Event* event) { - menu_runner_.reset(new MenuRunner(GetMenuModel(), MenuRunner::HAS_MNEMONICS)); - - if (menu_runner_->RunMenuAt(source->GetWidget()->GetTopLevelWidget(), - this, - gfx::Rect(point, gfx::Size()), - MENU_ANCHOR_TOPRIGHT, - ui::MENU_SOURCE_NONE) == - MenuRunner::MENU_DELETED) { - return; - } + menu_runner_.reset(new MenuRunner( + GetMenuModel(), MenuRunner::HAS_MNEMONICS | MenuRunner::ASYNC)); + + menu_runner_->RunMenuAt(source->GetWidget()->GetTopLevelWidget(), this, + gfx::Rect(point, gfx::Size()), MENU_ANCHOR_TOPRIGHT, + ui::MENU_SOURCE_NONE); } ui::SimpleMenuModel* ExampleMenuButton::GetMenuModel() { diff --git a/chromium/ui/views/examples/progress_bar_example.cc b/chromium/ui/views/examples/progress_bar_example.cc index 8a860f212af..4cb75a156e7 100644 --- a/chromium/ui/views/examples/progress_bar_example.cc +++ b/chromium/ui/views/examples/progress_bar_example.cc @@ -7,7 +7,7 @@ #include <algorithm> #include "base/strings/utf_string_conversions.h" -#include "ui/views/controls/button/label_button.h" +#include "ui/views/controls/button/md_text_button.h" #include "ui/views/controls/progress_bar.h" #include "ui/views/layout/grid_layout.h" #include "ui/views/view.h" @@ -27,11 +27,10 @@ namespace examples { ProgressBarExample::ProgressBarExample() : ExampleBase("Progress Bar"), - minus_button_(NULL), - plus_button_(NULL), - progress_bar_(NULL), - current_percent_(0.0) { -} + minus_button_(nullptr), + plus_button_(nullptr), + progress_bar_(nullptr), + current_percent_(0.0) {} ProgressBarExample::~ProgressBarExample() { } @@ -41,22 +40,35 @@ void ProgressBarExample::CreateExampleView(View* container) { container->SetLayoutManager(layout); ColumnSet* column_set = layout->AddColumnSet(0); - column_set->AddColumn(GridLayout::LEADING, GridLayout::FILL, - 0, GridLayout::USE_PREF, 0, 0); + column_set->AddColumn(GridLayout::TRAILING, GridLayout::CENTER, 0, + GridLayout::USE_PREF, 0, 0); column_set->AddPaddingColumn(0, 8); - column_set->AddColumn(GridLayout::FILL, GridLayout::FILL, - 1, GridLayout::USE_PREF, 0, 0); + column_set->AddColumn(GridLayout::FILL, GridLayout::CENTER, 1, + GridLayout::FIXED, 200, 0); column_set->AddPaddingColumn(0, 8); - column_set->AddColumn(GridLayout::TRAILING, GridLayout::FILL, - 0, GridLayout::USE_PREF, 0, 0); + column_set->AddColumn(GridLayout::LEADING, GridLayout::CENTER, 0, + GridLayout::USE_PREF, 0, 0); layout->StartRow(0, 0); - minus_button_ = new LabelButton(this, base::ASCIIToUTF16("-")); + minus_button_ = MdTextButton::Create(this, base::ASCIIToUTF16("-")); layout->AddView(minus_button_); progress_bar_ = new ProgressBar(); layout->AddView(progress_bar_); - plus_button_ = new LabelButton(this, base::ASCIIToUTF16("+")); + plus_button_ = MdTextButton::Create(this, base::ASCIIToUTF16("+")); layout->AddView(plus_button_); + + layout->StartRowWithPadding(0, 0, 0, 10); + layout->AddView(new Label(base::ASCIIToUTF16("Infinite loader:"))); + ProgressBar* infinite_bar = new ProgressBar(); + infinite_bar->SetValue(-1); + layout->AddView(infinite_bar); + + layout->StartRowWithPadding(0, 0, 0, 10); + layout->AddView( + new Label(base::ASCIIToUTF16("Infinite loader (very short):"))); + ProgressBar* shorter_bar = new ProgressBar(2); + shorter_bar->SetValue(-1); + layout->AddView(shorter_bar); } void ProgressBarExample::ButtonPressed(Button* sender, const ui::Event& event) { diff --git a/chromium/ui/views/examples/single_split_view_example.cc b/chromium/ui/views/examples/single_split_view_example.cc deleted file mode 100644 index 8f3deac15c5..00000000000 --- a/chromium/ui/views/examples/single_split_view_example.cc +++ /dev/null @@ -1,92 +0,0 @@ -// 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/examples/single_split_view_example.h" - -#include "base/macros.h" -#include "ui/views/background.h" -#include "ui/views/controls/single_split_view.h" -#include "ui/views/layout/grid_layout.h" - -namespace views { -namespace examples { -namespace { - -// SingleSplitView's content, which draws gradient color on background. -class SplittedView : public View { - public: - SplittedView(); - ~SplittedView() override; - - void SetColor(SkColor from, SkColor to); - - private: - // View: - gfx::Size GetPreferredSize() const override; - gfx::Size GetMinimumSize() const override; - void Layout() override; - - DISALLOW_COPY_AND_ASSIGN(SplittedView); -}; - -SplittedView::SplittedView() { - SetColor(SK_ColorRED, SK_ColorGREEN); -} - -SplittedView::~SplittedView() { -} - -void SplittedView::SetColor(SkColor from, SkColor to) { - set_background(Background::CreateVerticalGradientBackground(from, to)); -} - -gfx::Size SplittedView::GetPreferredSize() const { - return gfx::Size(width(), height()); -} - -gfx::Size SplittedView::GetMinimumSize() const { - return gfx::Size(10, 10); -} - -void SplittedView::Layout() { - SizeToPreferredSize(); -} - -} // namespace - -SingleSplitViewExample::SingleSplitViewExample() - : ExampleBase("Single Split View") { -} - -SingleSplitViewExample::~SingleSplitViewExample() { -} - -void SingleSplitViewExample::CreateExampleView(View* container) { - SplittedView* splitted_view_1 = new SplittedView; - SplittedView* splitted_view_2 = new SplittedView; - - splitted_view_1->SetColor(SK_ColorYELLOW, SK_ColorCYAN); - - single_split_view_ = new SingleSplitView( - splitted_view_1, splitted_view_2, - SingleSplitView::HORIZONTAL_SPLIT, - this); - - GridLayout* layout = new GridLayout(container); - container->SetLayoutManager(layout); - - ColumnSet* column_set = layout->AddColumnSet(0); - column_set->AddColumn(GridLayout::FILL, GridLayout::FILL, 1, - GridLayout::USE_PREF, 0, 0); - layout->StartRow(1, 0); - layout->AddView(single_split_view_); -} - -bool SingleSplitViewExample::SplitHandleMoved(SingleSplitView* sender) { - PrintStatus("Splitter moved"); - return true; -} - -} // namespace examples -} // namespace views diff --git a/chromium/ui/views/examples/single_split_view_example.h b/chromium/ui/views/examples/single_split_view_example.h deleted file mode 100644 index 8f2d50664f9..00000000000 --- a/chromium/ui/views/examples/single_split_view_example.h +++ /dev/null @@ -1,37 +0,0 @@ -// 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_EXAMPLES_SINGLE_SPLIT_VIEW_EXAMPLE_H_ -#define UI_VIEWS_EXAMPLES_SINGLE_SPLIT_VIEW_EXAMPLE_H_ - -#include "base/macros.h" -#include "ui/views/controls/single_split_view_listener.h" -#include "ui/views/examples/example_base.h" - -namespace views { -namespace examples { - -class VIEWS_EXAMPLES_EXPORT SingleSplitViewExample - : public ExampleBase, - public SingleSplitViewListener { - public: - SingleSplitViewExample(); - ~SingleSplitViewExample() override; - - // ExampleBase: - void CreateExampleView(View* container) override; - - private: - // SingleSplitViewListener: - bool SplitHandleMoved(SingleSplitView* sender) override; - - SingleSplitView* single_split_view_; - - DISALLOW_COPY_AND_ASSIGN(SingleSplitViewExample); -}; - -} // namespace examples -} // namespace views - -#endif // UI_VIEWS_EXAMPLES_SINGLE_SPLIT_VIEW_EXAMPLE_H_ diff --git a/chromium/ui/views/examples/slider_example.cc b/chromium/ui/views/examples/slider_example.cc index 89f78568e23..9367d120c78 100644 --- a/chromium/ui/views/examples/slider_example.cc +++ b/chromium/ui/views/examples/slider_example.cc @@ -7,6 +7,7 @@ #include "base/strings/stringprintf.h" #include "base/strings/utf_string_conversions.h" #include "ui/views/controls/label.h" +#include "ui/views/controls/slider.h" #include "ui/views/layout/box_layout.h" #include "ui/views/view.h" @@ -24,7 +25,8 @@ SliderExample::~SliderExample() { void SliderExample::CreateExampleView(View* container) { label_ = new Label(); - slider_ = new Slider(this, Slider::HORIZONTAL); + // Create a material design slider in this example. + slider_ = Slider::CreateSlider(true /** is_material_design **/, this); slider_->SetValue(0.5); diff --git a/chromium/ui/views/examples/textfield_example.cc b/chromium/ui/views/examples/textfield_example.cc index ffcf464e9e5..5b3144455d4 100644 --- a/chromium/ui/views/examples/textfield_example.cc +++ b/chromium/ui/views/examples/textfield_example.cc @@ -8,6 +8,7 @@ #include "base/strings/utf_string_conversions.h" #include "ui/events/event.h" +#include "ui/gfx/color_palette.h" #include "ui/gfx/range/range.h" #include "ui/gfx/render_text.h" #include "ui/views/controls/button/label_button.h" @@ -24,14 +25,15 @@ namespace examples { TextfieldExample::TextfieldExample() : ExampleBase("Textfield"), - name_(NULL), - password_(NULL), - read_only_(NULL), - show_password_(NULL), - clear_all_(NULL), - append_(NULL), - set_(NULL), - set_style_(NULL) { + name_(nullptr), + password_(nullptr), + disabled_(nullptr), + read_only_(nullptr), + show_password_(nullptr), + clear_all_(nullptr), + append_(nullptr), + set_(nullptr), + set_style_(nullptr) { } TextfieldExample::~TextfieldExample() { @@ -42,10 +44,15 @@ void TextfieldExample::CreateExampleView(View* container) { password_ = new Textfield(); password_->SetTextInputType(ui::TEXT_INPUT_TYPE_PASSWORD); password_->set_placeholder_text(ASCIIToUTF16("password")); + disabled_ = new Textfield(); + disabled_->SetEnabled(false); + disabled_->SetText(ASCIIToUTF16("disabled")); read_only_ = new Textfield(); read_only_->SetReadOnly(true); read_only_->SetText(ASCIIToUTF16("read only")); show_password_ = new LabelButton(this, ASCIIToUTF16("Show password")); + set_background_ = + new LabelButton(this, ASCIIToUTF16("Set non-default background")); clear_all_ = new LabelButton(this, ASCIIToUTF16("Clear All")); append_ = new LabelButton(this, ASCIIToUTF16("Append")); set_ = new LabelButton(this, ASCIIToUTF16("Set")); @@ -61,25 +68,24 @@ void TextfieldExample::CreateExampleView(View* container) { 0.2f, GridLayout::USE_PREF, 0, 0); column_set->AddColumn(GridLayout::FILL, GridLayout::FILL, 0.8f, GridLayout::USE_PREF, 0, 0); - layout->StartRow(0, 0); - layout->AddView(new Label(ASCIIToUTF16("Name:"))); - layout->AddView(name_); - layout->StartRow(0, 0); - layout->AddView(new Label(ASCIIToUTF16("Password:"))); - layout->AddView(password_); - layout->StartRow(0, 0); - layout->AddView(new Label(ASCIIToUTF16("Read Only:"))); - layout->AddView(read_only_); - layout->StartRow(0, 0); - layout->AddView(show_password_); - layout->StartRow(0, 0); - layout->AddView(clear_all_); - layout->StartRow(0, 0); - layout->AddView(append_); - layout->StartRow(0, 0); - layout->AddView(set_); - layout->StartRow(0, 0); - layout->AddView(set_style_); + + auto MakeRow = [layout](View* view1, View* view2) { + layout->StartRowWithPadding(0, 0, 0, 5); + layout->AddView(view1); + if (view2) + layout->AddView(view2); + }; + MakeRow(new Label(ASCIIToUTF16("Name:")), name_); + MakeRow(new Label(ASCIIToUTF16("Password:")), password_); + MakeRow(new Label(ASCIIToUTF16("Disabled:")), disabled_); + MakeRow(new Label(ASCIIToUTF16("Read Only:")), read_only_); + MakeRow(new Label(ASCIIToUTF16("Name:")), nullptr); + MakeRow(show_password_, nullptr); + MakeRow(set_background_, nullptr); + MakeRow(clear_all_, nullptr); + MakeRow(append_, nullptr); + MakeRow(set_, nullptr); + MakeRow(set_style_, nullptr); } void TextfieldExample::ContentsChanged(Textfield* sender, @@ -88,8 +94,8 @@ void TextfieldExample::ContentsChanged(Textfield* sender, PrintStatus("Name [%s]", UTF16ToUTF8(new_contents).c_str()); } else if (sender == password_) { PrintStatus("Password [%s]", UTF16ToUTF8(new_contents).c_str()); - } else if (sender == read_only_) { - PrintStatus("Read Only [%s]", UTF16ToUTF8(new_contents).c_str()); + } else { + NOTREACHED(); } } @@ -107,18 +113,23 @@ bool TextfieldExample::HandleMouseEvent(Textfield* sender, void TextfieldExample::ButtonPressed(Button* sender, const ui::Event& event) { if (sender == show_password_) { PrintStatus("Password [%s]", UTF16ToUTF8(password_->text()).c_str()); + } else if (sender == set_background_) { + password_->SetBackgroundColor(gfx::kGoogleRed300); } else if (sender == clear_all_) { base::string16 empty; name_->SetText(empty); password_->SetText(empty); + disabled_->SetText(empty); read_only_->SetText(empty); } else if (sender == append_) { name_->AppendText(ASCIIToUTF16("[append]")); password_->AppendText(ASCIIToUTF16("[append]")); + disabled_->SetText(ASCIIToUTF16("[append]")); read_only_->AppendText(ASCIIToUTF16("[append]")); } else if (sender == set_) { name_->SetText(ASCIIToUTF16("[set]")); password_->SetText(ASCIIToUTF16("[set]")); + disabled_->SetText(ASCIIToUTF16("[set]")); read_only_->SetText(ASCIIToUTF16("[set]")); } else if (sender == set_style_) { if (!name_->text().empty()) { diff --git a/chromium/ui/views/examples/textfield_example.h b/chromium/ui/views/examples/textfield_example.h index 90ec331b4a3..c40e3743625 100644 --- a/chromium/ui/views/examples/textfield_example.h +++ b/chromium/ui/views/examples/textfield_example.h @@ -44,10 +44,12 @@ class VIEWS_EXAMPLES_EXPORT TextfieldExample : public ExampleBase, // Textfields for name and password. Textfield* name_; Textfield* password_; + Textfield* disabled_; Textfield* read_only_; // Various buttons to control textfield. LabelButton* show_password_; + LabelButton* set_background_; LabelButton* clear_all_; LabelButton* append_; LabelButton* set_; diff --git a/chromium/ui/views/examples/throbber_example.cc b/chromium/ui/views/examples/throbber_example.cc index 5a1981219c1..c17cbb03cd6 100644 --- a/chromium/ui/views/examples/throbber_example.cc +++ b/chromium/ui/views/examples/throbber_example.cc @@ -16,25 +16,40 @@ namespace { class ThrobberView : public View { public: - ThrobberView() : throbber_(new Throbber()) { + ThrobberView() : throbber_(new Throbber()), is_checked_(false) { AddChildView(throbber_); throbber_->Start(); } + // View:: gfx::Size GetPreferredSize() const override { return gfx::Size(width(), height()); } void Layout() override { - int diameter = 64; + int diameter = 16; throbber_->SetBounds((width() - diameter) / 2, (height() - diameter) / 2, diameter, diameter); SizeToPreferredSize(); } + bool OnMousePressed(const ui::MouseEvent& event) override { + if (GetEventHandlerForPoint(event.location()) != throbber_) + return false; + + if (is_checked_) + throbber_->Start(); + else + throbber_->Stop(); + throbber_->SetChecked(!is_checked_); + is_checked_ = !is_checked_; + return true; + } + private: Throbber* throbber_; + bool is_checked_; DISALLOW_COPY_AND_ASSIGN(ThrobberView); }; diff --git a/chromium/ui/views/examples/toggle_button_example.cc b/chromium/ui/views/examples/toggle_button_example.cc new file mode 100644 index 00000000000..903fef24666 --- /dev/null +++ b/chromium/ui/views/examples/toggle_button_example.cc @@ -0,0 +1,34 @@ +// Copyright 2016 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/examples/toggle_button_example.h" + +#include "base/strings/stringprintf.h" +#include "base/strings/utf_string_conversions.h" +#include "ui/views/controls/button/toggle_button.h" +#include "ui/views/layout/box_layout.h" + +namespace views { +namespace examples { + +ToggleButtonExample::ToggleButtonExample() + : ExampleBase("Toggle button"), button_(nullptr), count_(0) {} + +ToggleButtonExample::~ToggleButtonExample() {} + +void ToggleButtonExample::CreateExampleView(View* container) { + button_ = new ToggleButton(this); + BoxLayout* layout = new BoxLayout(BoxLayout::kVertical, 0, 0, 0); + layout->set_cross_axis_alignment(BoxLayout::CROSS_AXIS_ALIGNMENT_CENTER); + container->SetLayoutManager(layout); + container->AddChildView(button_); +} + +void ToggleButtonExample::ButtonPressed(Button* sender, + const ui::Event& event) { + PrintStatus("Pressed! count: %d", ++count_); +} + +} // namespace examples +} // namespace views diff --git a/chromium/ui/views/examples/toggle_button_example.h b/chromium/ui/views/examples/toggle_button_example.h new file mode 100644 index 00000000000..50c4b9f6787 --- /dev/null +++ b/chromium/ui/views/examples/toggle_button_example.h @@ -0,0 +1,42 @@ +// Copyright 2016 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_EXAMPLES_TOGGLE_BUTTON_EXAMPLE_H_ +#define UI_VIEWS_EXAMPLES_TOGGLE_BUTTON_EXAMPLE_H_ + +#include "base/macros.h" +#include "ui/views/controls/button/button.h" +#include "ui/views/examples/example_base.h" + +namespace views { +class ToggleButton; + +namespace examples { + +// ToggleButtonExample exercises a ToggleButton control. +class VIEWS_EXAMPLES_EXPORT ToggleButtonExample : public ExampleBase, + public ButtonListener { + public: + ToggleButtonExample(); + ~ToggleButtonExample() override; + + // ExampleBase: + void CreateExampleView(View* container) override; + + private: + // ButtonListener: + void ButtonPressed(Button* sender, const ui::Event& event) override; + + // The only control in this test. + ToggleButton* button_; + + int count_; + + DISALLOW_COPY_AND_ASSIGN(ToggleButtonExample); +}; + +} // namespace examples +} // namespace views + +#endif // UI_VIEWS_EXAMPLES_TOGGLE_BUTTON_EXAMPLE_H_ diff --git a/chromium/ui/views/examples/tree_view_example.cc b/chromium/ui/views/examples/tree_view_example.cc index 68f99e2fa8c..14d3dce2ac9 100644 --- a/chromium/ui/views/examples/tree_view_example.cc +++ b/chromium/ui/views/examples/tree_view_example.cc @@ -4,6 +4,7 @@ #include "ui/views/examples/tree_view_example.h" +#include "base/memory/ptr_util.h" #include "base/strings/utf_string_conversions.h" #include "ui/views/controls/button/label_button.h" #include "ui/views/controls/menu/menu_model_adapter.h" @@ -18,30 +19,27 @@ namespace examples { TreeViewExample::TreeViewExample() : ExampleBase("Tree View"), - tree_view_(NULL), - model_(new NodeType(ASCIIToUTF16("root"), 1)) { -} + model_(base::MakeUnique<NodeType>(ASCIIToUTF16("root"), 1)) {} TreeViewExample::~TreeViewExample() { // Delete the view before the model. - delete tree_view_; - tree_view_ = NULL; + tree_view_.reset(); } void TreeViewExample::CreateExampleView(View* container) { // Add some sample data. - NodeType* colors_node = new NodeType(ASCIIToUTF16("colors"), 1); - model_.GetRoot()->Add(colors_node, 0); - colors_node->Add(new NodeType(ASCIIToUTF16("red"), 1), 0); - colors_node->Add(new NodeType(ASCIIToUTF16("green"), 1), 1); - colors_node->Add(new NodeType(ASCIIToUTF16("blue"), 1), 2); - - NodeType* sheep_node = new NodeType(ASCIIToUTF16("sheep"), 1); - model_.GetRoot()->Add(sheep_node, 0); - sheep_node->Add(new NodeType(ASCIIToUTF16("Sheep 1"), 1), 0); - sheep_node->Add(new NodeType(ASCIIToUTF16("Sheep 2"), 1), 1); - - tree_view_ = new TreeView(); + NodeType* colors_node = model_.GetRoot()->Add( + base::MakeUnique<NodeType>(ASCIIToUTF16("colors"), 1), 0); + colors_node->Add(base::MakeUnique<NodeType>(ASCIIToUTF16("red"), 1), 0); + colors_node->Add(base::MakeUnique<NodeType>(ASCIIToUTF16("green"), 1), 1); + colors_node->Add(base::MakeUnique<NodeType>(ASCIIToUTF16("blue"), 1), 2); + + NodeType* sheep_node = model_.GetRoot()->Add( + base::MakeUnique<NodeType>(ASCIIToUTF16("sheep"), 1), 0); + sheep_node->Add(base::MakeUnique<NodeType>(ASCIIToUTF16("Sheep 1"), 1), 0); + sheep_node->Add(base::MakeUnique<NodeType>(ASCIIToUTF16("Sheep 2"), 1), 1); + + tree_view_ = base::MakeUnique<TreeView>(); tree_view_->set_context_menu_controller(this); tree_view_->SetRootShown(false); tree_view_->SetModel(&model_); @@ -85,8 +83,9 @@ void TreeViewExample::AddNewNode() { static_cast<NodeType*>(tree_view_->GetSelectedNode()); if (!selected_node) selected_node = model_.GetRoot(); - NodeType* new_node = new NodeType(selected_node->GetTitle(), 1); - model_.Add(selected_node, new_node, selected_node->child_count()); + NodeType* new_node = model_.Add( + selected_node, base::MakeUnique<NodeType>(selected_node->GetTitle(), 1), + selected_node->child_count()); tree_view_->SetSelectedNode(new_node); } @@ -130,19 +129,15 @@ bool TreeViewExample::CanEdit(TreeView* tree_view, void TreeViewExample::ShowContextMenuForView(View* source, const gfx::Point& point, ui::MenuSourceType source_type) { - ui::SimpleMenuModel context_menu_model(this); - context_menu_model.AddItem(ID_EDIT, ASCIIToUTF16("Edit")); - context_menu_model.AddItem(ID_REMOVE, ASCIIToUTF16("Remove")); - context_menu_model.AddItem(ID_ADD, ASCIIToUTF16("Add")); - context_menu_runner_.reset(new MenuRunner(&context_menu_model, 0)); - if (context_menu_runner_->RunMenuAt(source->GetWidget(), - NULL, - gfx::Rect(point, gfx::Size()), - MENU_ANCHOR_TOPLEFT, - source_type) == - MenuRunner::MENU_DELETED) { - return; - } + context_menu_model_.reset(new ui::SimpleMenuModel(this)); + context_menu_model_->AddItem(ID_EDIT, ASCIIToUTF16("Edit")); + context_menu_model_->AddItem(ID_REMOVE, ASCIIToUTF16("Remove")); + context_menu_model_->AddItem(ID_ADD, ASCIIToUTF16("Add")); + context_menu_runner_.reset( + new MenuRunner(context_menu_model_.get(), MenuRunner::ASYNC)); + context_menu_runner_->RunMenuAt(source->GetWidget(), nullptr, + gfx::Rect(point, gfx::Size()), + MENU_ANCHOR_TOPLEFT, source_type); } bool TreeViewExample::IsCommandIdChecked(int command_id) const { @@ -153,12 +148,6 @@ bool TreeViewExample::IsCommandIdEnabled(int command_id) const { return const_cast<TreeViewExample*>(this)->IsCommandIdEnabled(command_id); } -bool TreeViewExample::GetAcceleratorForCommandId( - int command_id, - ui::Accelerator* accelerator) { - return false; -} - void TreeViewExample::ExecuteCommand(int command_id, int event_flags) { NodeType* selected_node = static_cast<NodeType*>(tree_view_->GetSelectedNode()); diff --git a/chromium/ui/views/examples/tree_view_example.h b/chromium/ui/views/examples/tree_view_example.h index fb0ccf93db6..9ec5e1058fd 100644 --- a/chromium/ui/views/examples/tree_view_example.h +++ b/chromium/ui/views/examples/tree_view_example.h @@ -13,6 +13,10 @@ #include "ui/views/controls/tree/tree_view_controller.h" #include "ui/views/examples/example_base.h" +namespace ui { +class SimpleMenuModel; +} + namespace views { class LabelButton; @@ -63,12 +67,10 @@ class VIEWS_EXAMPLES_EXPORT TreeViewExample // SimpleMenuModel::Delegate: bool IsCommandIdChecked(int command_id) const override; bool IsCommandIdEnabled(int command_id) const override; - bool GetAcceleratorForCommandId(int command_id, - ui::Accelerator* accelerator) override; void ExecuteCommand(int command_id, int event_flags) override; // The tree view to be tested. - TreeView* tree_view_; + std::unique_ptr<TreeView> tree_view_; // Control buttons to modify the model. LabelButton* add_; @@ -79,6 +81,7 @@ class VIEWS_EXAMPLES_EXPORT TreeViewExample ui::TreeNodeModel<NodeType> model_; + std::unique_ptr<ui::SimpleMenuModel> context_menu_model_; std::unique_ptr<MenuRunner> context_menu_runner_; DISALLOW_COPY_AND_ASSIGN(TreeViewExample); diff --git a/chromium/ui/views/examples/vector_example.cc b/chromium/ui/views/examples/vector_example.cc index 94f89729564..9802acffd82 100644 --- a/chromium/ui/views/examples/vector_example.cc +++ b/chromium/ui/views/examples/vector_example.cc @@ -4,8 +4,6 @@ #include "ui/views/examples/vector_example.h" -#include <stddef.h> - #include "base/files/file_path.h" #include "base/files/file_util.h" #include "base/macros.h" @@ -15,8 +13,8 @@ #include "ui/gfx/paint_vector_icon.h" #include "ui/gfx/vector_icons_public.h" #include "ui/views/border.h" -#include "ui/views/controls/button/blue_button.h" #include "ui/views/controls/button/button.h" +#include "ui/views/controls/button/md_text_button.h" #include "ui/views/controls/image_view.h" #include "ui/views/controls/textfield/textfield.h" #include "ui/views/controls/textfield/textfield_controller.h" @@ -39,7 +37,8 @@ class VectorIconGallery : public View, size_input_(new Textfield()), color_input_(new Textfield()), file_chooser_(new Textfield()), - file_go_button_(new BlueButton(this, base::ASCIIToUTF16("Render"))), + file_go_button_( + MdTextButton::Create(this, base::ASCIIToUTF16("Render"))), vector_id_(0), // 36dp is one of the natural sizes for MD icons, and corresponds // roughly to a 32dp usable area. @@ -99,7 +98,7 @@ class VectorIconGallery : public View, void ContentsChanged(Textfield* sender, const base::string16& new_contents) override { if (sender == size_input_) { - if (base::StringToSizeT(new_contents, &size_)) + if (base::StringToInt(new_contents, &size_) && (size_ > 0)) UpdateImage(); else size_input_->SetText(base::string16()); @@ -128,6 +127,12 @@ class VectorIconGallery : public View, base::FilePath path(file_chooser_->text()); #endif base::ReadFileToString(path, &contents); + // Skip over comments. + for (size_t slashes = contents.find("//"); slashes != std::string::npos; + slashes = contents.find("//")) { + size_t eol = contents.find("\n", slashes); + contents.erase(slashes, eol - slashes); + } image_view_->SetImage( gfx::CreateVectorIconFromSource(contents, size_, color_)); } @@ -147,7 +152,7 @@ class VectorIconGallery : public View, Button* file_go_button_; int vector_id_; - size_t size_; + int size_; SkColor color_; DISALLOW_COPY_AND_ASSIGN(VectorIconGallery); diff --git a/chromium/ui/views/focus/focus_manager_unittest.cc b/chromium/ui/views/focus/focus_manager_unittest.cc index da6ededa69e..a655eafcc45 100644 --- a/chromium/ui/views/focus/focus_manager_unittest.cc +++ b/chromium/ui/views/focus/focus_manager_unittest.cc @@ -15,7 +15,6 @@ #include "ui/base/accelerators/accelerator.h" #include "ui/events/keycodes/keyboard_codes.h" #include "ui/views/accessible_pane_view.h" -#include "ui/views/controls/button/label_button.h" #include "ui/views/focus/focus_manager_factory.h" #include "ui/views/focus/widget_focus_manager.h" #include "ui/views/test/focus_manager_test.h" @@ -482,21 +481,6 @@ class FocusManagerDtorTest : public FocusManagerTest { DISALLOW_COPY_AND_ASSIGN(TestFocusManagerFactory); }; - class LabelButtonDtorTracked : public LabelButton { - public: - LabelButtonDtorTracked(const base::string16& text, - DtorTrackVector* dtor_tracker) - : LabelButton(NULL, text), - dtor_tracker_(dtor_tracker) { - SetStyle(STYLE_BUTTON); - }; - ~LabelButtonDtorTracked() override { - dtor_tracker_->push_back("LabelButtonDtorTracked"); - } - - DtorTrackVector* dtor_tracker_; - }; - class WindowDtorTracked : public Widget { public: explicit WindowDtorTracked(DtorTrackVector* dtor_tracker) diff --git a/chromium/ui/views/focus/focus_traversal_unittest.cc b/chromium/ui/views/focus/focus_traversal_unittest.cc index 4bc43614590..823904576c7 100644 --- a/chromium/ui/views/focus/focus_traversal_unittest.cc +++ b/chromium/ui/views/focus/focus_traversal_unittest.cc @@ -13,6 +13,7 @@ #include "ui/views/border.h" #include "ui/views/controls/button/checkbox.h" #include "ui/views/controls/button/label_button.h" +#include "ui/views/controls/button/md_text_button.h" #include "ui/views/controls/button/radio_button.h" #include "ui/views/controls/combobox/combobox.h" #include "ui/views/controls/label.h" @@ -375,8 +376,7 @@ void FocusTraversalTest::InitContentView() { y += label_height + gap_between_labels; - LabelButton* button = new LabelButton(NULL, ASCIIToUTF16("Click me")); - button->SetStyle(Button::STYLE_BUTTON); + LabelButton* button = MdTextButton::Create(NULL, ASCIIToUTF16("Click me")); button->SetBounds(label_x, y + 10, 80, 30); button->set_id(kFruitButtonID); left_container_->AddChildView(button); @@ -471,22 +471,19 @@ void FocusTraversalTest::InitContentView() { y = 250; int width = 60; - button = new LabelButton(NULL, ASCIIToUTF16("OK")); - button->SetStyle(Button::STYLE_BUTTON); + button = MdTextButton::Create(NULL, ASCIIToUTF16("OK")); button->set_id(kOKButtonID); button->SetIsDefault(true); GetContentsView()->AddChildView(button); button->SetBounds(150, y, width, 30); - button = new LabelButton(NULL, ASCIIToUTF16("Cancel")); - button->SetStyle(Button::STYLE_BUTTON); + button = MdTextButton::Create(NULL, ASCIIToUTF16("Cancel")); button->set_id(kCancelButtonID); GetContentsView()->AddChildView(button); button->SetBounds(220, y, width, 30); - button = new LabelButton(NULL, ASCIIToUTF16("Help")); - button->SetStyle(Button::STYLE_BUTTON); + button = MdTextButton::Create(NULL, ASCIIToUTF16("Help")); button->set_id(kHelpButtonID); GetContentsView()->AddChildView(button); button->SetBounds(290, y, width, 30); @@ -539,8 +536,7 @@ void FocusTraversalTest::InitContentView() { text_field->SetBounds(10, 10, 100, 20); text_field->set_id(kSearchTextfieldID); - button = new LabelButton(NULL, ASCIIToUTF16("Search")); - button->SetStyle(Button::STYLE_BUTTON); + button = MdTextButton::Create(NULL, ASCIIToUTF16("Search")); contents->AddChildView(button); button->SetBounds(112, 5, 60, 30); button->set_id(kSearchButtonID); @@ -563,13 +559,11 @@ void FocusTraversalTest::InitContentView() { contents->SetFocusBehavior(View::FocusBehavior::ALWAYS); contents->set_background(Background::CreateSolidBackground(SK_ColorBLUE)); contents->set_id(kThumbnailContainerID); - button = new LabelButton(NULL, ASCIIToUTF16("Star")); - button->SetStyle(Button::STYLE_BUTTON); + button = MdTextButton::Create(NULL, ASCIIToUTF16("Star")); contents->AddChildView(button); button->SetBounds(5, 5, 50, 30); button->set_id(kThumbnailStarID); - button = new LabelButton(NULL, ASCIIToUTF16("SuperStar")); - button->SetStyle(Button::STYLE_BUTTON); + button = MdTextButton::Create(NULL, ASCIIToUTF16("SuperStar")); contents->AddChildView(button); button->SetBounds(60, 5, 100, 30); button->set_id(kThumbnailSuperStarID); diff --git a/chromium/ui/views/focus/view_storage.cc b/chromium/ui/views/focus/view_storage.cc index 204b31d0892..4c519f3f8cb 100644 --- a/chromium/ui/views/focus/view_storage.cc +++ b/chromium/ui/views/focus/view_storage.cc @@ -8,7 +8,6 @@ #include "base/logging.h" #include "base/memory/singleton.h" -#include "base/stl_util.h" namespace views { @@ -20,10 +19,7 @@ ViewStorage* ViewStorage::GetInstance() { ViewStorage::ViewStorage() : view_storage_next_id_(0) { } -ViewStorage::~ViewStorage() { - STLDeleteContainerPairSecondPointers(view_to_ids_.begin(), - view_to_ids_.end()); -} +ViewStorage::~ViewStorage() {} int ViewStorage::CreateStorageID() { return view_storage_next_id_++; @@ -31,31 +27,20 @@ int ViewStorage::CreateStorageID() { void ViewStorage::StoreView(int storage_id, View* view) { DCHECK(view); - std::map<int, View*>::iterator iter = id_to_view_.find(storage_id); - if (iter != id_to_view_.end()) { + if (id_to_view_.find(storage_id) != id_to_view_.end()) { NOTREACHED(); RemoveView(storage_id); } id_to_view_[storage_id] = view; - - std::vector<int>* ids = NULL; - std::map<View*, std::vector<int>*>::iterator id_iter = - view_to_ids_.find(view); - if (id_iter == view_to_ids_.end()) { - ids = new std::vector<int>(); - view_to_ids_[view] = ids; - } else { - ids = id_iter->second; - } - ids->push_back(storage_id); + view_to_ids_[view].push_back(storage_id); } View* ViewStorage::RetrieveView(int storage_id) { - std::map<int, View*>::iterator iter = id_to_view_.find(storage_id); + auto iter = id_to_view_.find(storage_id); if (iter == id_to_view_.end()) - return NULL; + return nullptr; return iter->second; } @@ -65,22 +50,21 @@ void ViewStorage::RemoveView(int storage_id) { void ViewStorage::ViewRemoved(View* removed) { // Let's first retrieve the ids for that view. - std::map<View*, std::vector<int>*>::iterator ids_iter = - view_to_ids_.find(removed); + auto ids_iter = view_to_ids_.find(removed); if (ids_iter == view_to_ids_.end()) { // That view is not in the view storage. return; } - std::vector<int>* ids = ids_iter->second; - DCHECK(!ids->empty()); - EraseView((*ids)[0], true); + const std::vector<int>& ids = ids_iter->second; + DCHECK(!ids.empty()); + EraseView(ids[0], true); } void ViewStorage::EraseView(int storage_id, bool remove_all_ids) { // Remove the view from id_to_view_location_. - std::map<int, View*>::iterator view_iter = id_to_view_.find(storage_id); + auto view_iter = id_to_view_.find(storage_id); if (view_iter == id_to_view_.end()) return; @@ -88,28 +72,20 @@ void ViewStorage::EraseView(int storage_id, bool remove_all_ids) { id_to_view_.erase(view_iter); // Also update view_to_ids_. - std::map<View*, std::vector<int>*>::iterator ids_iter = - view_to_ids_.find(view); + auto ids_iter = view_to_ids_.find(view); DCHECK(ids_iter != view_to_ids_.end()); - std::vector<int>* ids = ids_iter->second; + std::vector<int>& ids = ids_iter->second; if (remove_all_ids) { - for (size_t i = 0; i < ids->size(); ++i) { - view_iter = id_to_view_.find((*ids)[i]); - if (view_iter != id_to_view_.end()) - id_to_view_.erase(view_iter); - } - ids->clear(); - } else { - std::vector<int>::iterator id_iter = - std::find(ids->begin(), ids->end(), storage_id); - DCHECK(id_iter != ids->end()); - ids->erase(id_iter); - } - - if (ids->empty()) { - delete ids; + for (int id : ids) + id_to_view_.erase(id); view_to_ids_.erase(ids_iter); + } else if (ids.size() == 1) { + view_to_ids_.erase(ids_iter); + } else { + auto id_iter = std::find(ids.begin(), ids.end(), storage_id); + DCHECK(id_iter != ids.end()); + ids.erase(id_iter); } } diff --git a/chromium/ui/views/focus/view_storage.h b/chromium/ui/views/focus/view_storage.h index d543415e490..73235c1c3f7 100644 --- a/chromium/ui/views/focus/view_storage.h +++ b/chromium/ui/views/focus/view_storage.h @@ -69,7 +69,7 @@ class VIEWS_EXPORT ViewStorage { std::map<int, View*> id_to_view_; // Association View to id, used to speed up view notification removal. - std::map<View*, std::vector<int>*> view_to_ids_; + std::map<View*, std::vector<int>> view_to_ids_; DISALLOW_COPY_AND_ASSIGN(ViewStorage); }; diff --git a/chromium/ui/views/layout/grid_layout.cc b/chromium/ui/views/layout/grid_layout.cc index c79b600ffef..77a567ca514 100644 --- a/chromium/ui/views/layout/grid_layout.cc +++ b/chromium/ui/views/layout/grid_layout.cc @@ -374,7 +374,7 @@ ColumnSet::ColumnSet(int id) : id_(id) { } ColumnSet::~ColumnSet() { - STLDeleteElements(&columns_); + base::STLDeleteElements(&columns_); } void ColumnSet::AddPaddingColumn(float resize_percent, int width) { @@ -662,9 +662,9 @@ GridLayout::GridLayout(View* host) } GridLayout::~GridLayout() { - STLDeleteElements(&column_sets_); - STLDeleteElements(&view_states_); - STLDeleteElements(&rows_); + base::STLDeleteElements(&column_sets_); + base::STLDeleteElements(&view_states_); + base::STLDeleteElements(&rows_); } // static @@ -937,8 +937,8 @@ void GridLayout::SizeRowsAndColumns(bool layout, int width, int height, LayoutElement::CalculateLocationsFromSize(&rows_); // We now know the preferred height, set it here. - pref->set_height(rows_[rows_.size() - 1]->Location() + - rows_[rows_.size() - 1]->Size() + insets_.height()); + pref->set_height(rows_.back()->Location() + rows_.back()->Size() + + insets_.height()); if (layout && height != pref->height()) { // We're doing a layout, and the height differs from the preferred height, diff --git a/chromium/ui/views/layout/grid_layout.h b/chromium/ui/views/layout/grid_layout.h index 856fb6f1a44..c82f3a8db6a 100644 --- a/chromium/ui/views/layout/grid_layout.h +++ b/chromium/ui/views/layout/grid_layout.h @@ -18,7 +18,7 @@ // define the structure of the Grid first, then add the Views. // The following creates a trivial grid with two columns separated by // a column with padding: -// ColumnSet* columns = layout->AddColumnSet(0); // Give this column an +// ColumnSet* columns = layout->AddColumnSet(0); // Give this column set an // // identifier of 0. // columns->AddColumn(FILL, // Views are horizontally resized to fill column. // FILL, // Views starting in this column are vertically diff --git a/chromium/ui/views/linux_ui/linux_ui.gyp b/chromium/ui/views/linux_ui/linux_ui.gyp deleted file mode 100644 index 5ba78e52baf..00000000000 --- a/chromium/ui/views/linux_ui/linux_ui.gyp +++ /dev/null @@ -1,33 +0,0 @@ -# Copyright 2013 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. - -{ - 'variables': { - 'chromium_code': 1, - }, - 'targets': [ - { - 'target_name': 'linux_ui', - 'type': '<(component)', - 'dependencies': [ - '../../base/base.gyp:base', - '../../base/third_party/dynamic_annotations/dynamic_annotations.gyp:dynamic_annotations', - '../../skia/skia.gyp:skia', - '../base/ui_base.gyp:ui_base', - '../native_theme/native_theme.gyp:native_theme', - '../resources/ui_resources.gyp:ui_resources', - ], - 'defines': [ - 'LINUX_UI_IMPLEMENTATION', - ], - 'sources': [ - 'linux_ui.cc', - 'linux_ui.h', - 'linux_ui_export.h', - 'status_icon_linux.cc', - 'status_icon_linux.h', - ], - }, - ], -} diff --git a/chromium/ui/views/linux_ui/linux_ui.h b/chromium/ui/views/linux_ui/linux_ui.h index 86a3d5a1bb0..bbbaf572b72 100644 --- a/chromium/ui/views/linux_ui/linux_ui.h +++ b/chromium/ui/views/linux_ui/linux_ui.h @@ -84,11 +84,8 @@ class VIEWS_EXPORT LinuxUI : public ui::LinuxInputMethodContextFactory, // unconditionally. virtual void MaterialDesignControllerReady() = 0; - // Returns a themed image per theme_provider.h - virtual gfx::Image GetThemeImageNamed(int id) const = 0; virtual bool GetTint(int id, color_utils::HSL* tint) const = 0; virtual bool GetColor(int id, SkColor* color) const = 0; - virtual bool HasCustomImage(int id) const = 0; // Returns the preferences that we pass to WebKit. virtual SkColor GetFocusRingColor() const = 0; diff --git a/chromium/ui/views/mus/BUILD.gn b/chromium/ui/views/mus/BUILD.gn index ff5631e3242..94d53fb62c4 100644 --- a/chromium/ui/views/mus/BUILD.gn +++ b/chromium/ui/views/mus/BUILD.gn @@ -4,15 +4,11 @@ import("//build/config/features.gni") import("//build/config/ui.gni") -import("//mojo/public/mojo_application.gni") -import("//mojo/public/mojo_application_manifest.gni") +import("//services/shell/public/cpp/service.gni") +import("//services/shell/public/service_manifest.gni") import("//testing/test.gni") import("//tools/grit/repack.gni") -gypi = exec_script("//build/gypi_to_gn.py", - [ rebase_path("../views.gyp") ], - "scope", - [ "../views.gyp" ]) component("mus") { output_name = "ui_views_mus_lib" @@ -21,20 +17,26 @@ component("mus") { "aura_init.h", "clipboard_mus.cc", "clipboard_mus.h", - "display_list.cc", - "display_list.h", + "drag_drop_client_mus.cc", + "drag_drop_client_mus.h", + "drop_target_mus.cc", + "drop_target_mus.h", "input_method_mus.cc", "input_method_mus.h", "mus_export.h", "native_widget_mus.cc", "native_widget_mus.h", + "os_exchange_data_provider_mus.cc", + "os_exchange_data_provider_mus.h", + "pointer_watcher_event_router.cc", + "pointer_watcher_event_router.h", "screen_mus.cc", "screen_mus.h", "screen_mus_delegate.h", - "surface_binding.cc", - "surface_binding.h", "surface_context_factory.cc", "surface_context_factory.h", + "text_input_client_impl.cc", + "text_input_client_impl.h", "window_manager_connection.cc", "window_manager_connection.h", "window_manager_constants_converters.cc", @@ -49,7 +51,7 @@ component("mus") { public_deps = [ ":resources", - "//components/mus/public/cpp", + "//services/ui/public/cpp", "//ui/aura", ] deps = [ @@ -58,22 +60,19 @@ component("mus") { "//base/third_party/dynamic_annotations", "//cc", "//cc/surfaces", - "//components/bitmap_uploader", - "//components/mus/common:mus_common", - "//components/mus/gles2:lib", - "//components/mus/public/cpp", - "//components/mus/public/interfaces", "//mojo/common", "//mojo/public/cpp/bindings", + "//net", "//services/catalog/public/cpp", "//services/shell/public/cpp", "//services/shell/public/interfaces", + "//services/ui/public/cpp", + "//services/ui/public/interfaces", "//skia", "//third_party/icu", "//ui/aura", "//ui/compositor", "//ui/display", - "//ui/display/mojo", "//ui/events", "//ui/events:events_base", "//ui/gfx", @@ -118,7 +117,7 @@ group("for_mojo_application") { ] } -source_set("test_support") { +static_library("test_support") { testonly = true sources = [ @@ -131,10 +130,10 @@ source_set("test_support") { ":mus", "//base", "//base/test:test_support", - "//components/mus/common:mus_common", "//services/shell/background:lib", "//services/shell/background/tests:test_support", "//services/shell/public/cpp:sources", + "//services/ui/common:mus_common", "//testing/gtest", "//ui/aura", "//ui/gl:test_support", @@ -152,33 +151,16 @@ source_set("test_support") { test("views_mus_unittests") { testonly = true - configs += [ "//build/config:precompiled_headers" ] - sources = [ - "display_list_unittest.cc", + "input_method_mus_unittest.cc", "native_widget_mus_unittest.cc", + "os_exchange_data_provider_mus_unittest.cc", + "pointer_watcher_event_router_unittest.cc", "run_all_unittests_mus.cc", "screen_mus_unittest.cc", - "window_manager_connection_unittest.cc", ] - sources += rebase_path(gypi.views_unittests_sources, ".", "//ui/views") - sources += rebase_path(gypi.views_unittests_aura_sources, ".", "//ui/views") - - sources -= [ - # Mus has its own runner. - "../run_all_unittests_main.cc", - - # EventGenerator doesn't work well with IME in mus. - # crbug.com/615033 crbug.com/548407 - "../controls/textfield/textfield_unittest.cc", - - # Tooltips. crbug.com/599558 - "../corewm/tooltip_controller_unittest.cc", - - # Some of the tests need drag-drop support. crbug.com/614037 - "../touchui/touch_selection_controller_impl_unittest.cc", - ] + configs += [ "//build/config:precompiled_headers" ] deps = [ ":mus", @@ -187,10 +169,11 @@ test("views_mus_unittests") { "//base:i18n", "//base/test:test_support", "//cc", - "//components/mus/public/cpp", - "//components/mus/public/cpp/tests:unittest_support", - "//components/mus/public/interfaces", + "//net", "//services/shell/background:main", # Provides main(). + "//services/ui/public/cpp", + "//services/ui/public/cpp/tests:unittest_support", + "//services/ui/public/interfaces", "//skia", "//testing/gtest", "//third_party/icu", @@ -212,13 +195,15 @@ test("views_mus_unittests") { "//ui/touch_selection", "//ui/views", "//ui/views:test_support_internal", + "//ui/views:views_unittests_sources", "//ui/wm", "//url", ] data_deps = [ ":unittests_manifest", - "//components/mus/test_wm", + "//services/ui/ime/test_ime_driver", + "//services/ui/test_wm", ] if (is_win) { @@ -252,8 +237,6 @@ test("views_mus_unittests") { test("views_mus_interactive_ui_tests") { testonly = true - configs += [ "//build/config:precompiled_headers" ] - sources = [ "../widget/widget_interactive_uitest.cc", "clipboard_unittest.cc", @@ -281,7 +264,7 @@ test("views_mus_interactive_ui_tests") { data_deps = [ ":interactive_ui_tests_manifest", - "//components/mus/test_wm", + "//services/ui/test_wm", ] if (is_win) { @@ -298,14 +281,14 @@ test("views_mus_interactive_ui_tests") { } } -mojo_application_manifest("unittests_manifest") { +service_manifest("unittests_manifest") { type = "exe" - application_name = "views_mus_unittests" + name = "views_mus_unittests" source = "unittests_manifest.json" } -mojo_application_manifest("interactive_ui_tests_manifest") { +service_manifest("interactive_ui_tests_manifest") { type = "exe" - application_name = "views_mus_interactive_ui_tests" + name = "views_mus_interactive_ui_tests" source = "interactive_ui_tests_manifest.json" } diff --git a/chromium/ui/views/mus/DEPS b/chromium/ui/views/mus/DEPS index 84766d2fe9a..83723b80ea5 100644 --- a/chromium/ui/views/mus/DEPS +++ b/chromium/ui/views/mus/DEPS @@ -2,15 +2,15 @@ include_rules = [ "+cc", "-cc/blink", "+components/font_service/public", - "+components/bitmap_uploader", "+components/gpu", - "+components/mus", + "+net", "+mojo/cc", "+mojo/common", "+mojo/converters", "+mojo/public", "+services/catalog/public", "+services/shell/public", + "+services/ui", "+skia", "+ui/aura", "+ui/base", diff --git a/chromium/ui/views/mus/aura_init.cc b/chromium/ui/views/mus/aura_init.cc index 5335188700b..7499e8629f0 100644 --- a/chromium/ui/views/mus/aura_init.cc +++ b/chromium/ui/views/mus/aura_init.cc @@ -19,7 +19,7 @@ #include "ui/base/ui_base_paths.h" #include "ui/views/views_delegate.h" -#if defined(OS_LINUX) && !defined(OS_ANDROID) +#if defined(OS_LINUX) #include "components/font_service/public/cpp/font_loader.h" #endif @@ -27,12 +27,6 @@ namespace views { namespace { -std::set<std::string> GetResourcePaths(const std::string& resource_file) { - std::set<std::string> paths; - paths.insert(resource_file); - return paths; -} - class MusViewsDelegate : public ViewsDelegate { public: MusViewsDelegate() {} @@ -52,18 +46,30 @@ class MusViewsDelegate : public ViewsDelegate { } // namespace AuraInit::AuraInit(shell::Connector* connector, - const std::string& resource_file) + const std::string& resource_file, + const std::string& resource_file_200) : resource_file_(resource_file), + resource_file_200_(resource_file_200), env_(aura::Env::CreateInstance()), views_delegate_(new MusViewsDelegate) { ui::MaterialDesignController::Initialize(); InitializeResources(connector); +// Initialize the skia font code to go ask fontconfig underneath. +#if defined(OS_LINUX) + font_loader_ = sk_make_sp<font_service::FontLoader>(connector); + SkFontConfigInterface::SetGlobal(font_loader_.get()); +#endif + + // There is a bunch of static state in gfx::Font, by running this now, + // before any other apps load, we ensure all the state is set up. + gfx::Font(); + ui::InitializeInputMethodForTesting(); } AuraInit::~AuraInit() { -#if defined(OS_LINUX) && !defined(OS_ANDROID) +#if defined(OS_LINUX) if (font_loader_.get()) { SkFontConfigInterface::SetGlobal(nullptr); // FontLoader is ref counted. We need to explicitly shutdown the background @@ -75,13 +81,19 @@ AuraInit::~AuraInit() { } void AuraInit::InitializeResources(shell::Connector* connector) { + // Resources may have already been initialized (e.g. when 'chrome --mash' is + // used to launch the current app). if (ui::ResourceBundle::HasSharedInstance()) return; + + std::set<std::string> resource_paths({resource_file_}); + if (!resource_file_200_.empty()) + resource_paths.insert(resource_file_200_); + catalog::ResourceLoader loader; filesystem::mojom::DirectoryPtr directory; - connector->ConnectToInterface("mojo:catalog", &directory); - CHECK(loader.OpenFiles(std::move(directory), - GetResourcePaths(resource_file_))); + connector->ConnectToInterface("service:catalog", &directory); + CHECK(loader.OpenFiles(std::move(directory), resource_paths)); ui::RegisterPathProvider(); base::File pak_file = loader.TakeFile(resource_file_); base::File pak_file_2 = pak_file.Duplicate(); @@ -89,16 +101,9 @@ void AuraInit::InitializeResources(shell::Connector* connector) { std::move(pak_file), base::MemoryMappedFile::Region::kWholeFile); ui::ResourceBundle::GetSharedInstance().AddDataPackFromFile( std::move(pak_file_2), ui::SCALE_FACTOR_100P); - -// Initialize the skia font code to go ask fontconfig underneath. -#if defined(OS_LINUX) && !defined(OS_ANDROID) - font_loader_ = sk_make_sp<font_service::FontLoader>(connector); - SkFontConfigInterface::SetGlobal(font_loader_.get()); -#endif - - // There is a bunch of static state in gfx::Font, by running this now, - // before any other apps load, we ensure all the state is set up. - gfx::Font(); + if (!resource_file_200_.empty()) + ui::ResourceBundle::GetSharedInstance().AddDataPackFromFile( + loader.TakeFile(resource_file_200_), ui::SCALE_FACTOR_200P); } } // namespace views diff --git a/chromium/ui/views/mus/aura_init.h b/chromium/ui/views/mus/aura_init.h index 9330845f17b..ac828327e98 100644 --- a/chromium/ui/views/mus/aura_init.h +++ b/chromium/ui/views/mus/aura_init.h @@ -32,17 +32,24 @@ class ViewsDelegate; // |resource_file| is the path to the apk file containing the resources. class VIEWS_MUS_EXPORT AuraInit { public: - AuraInit(shell::Connector* connector, const std::string& resource_file); + // |resource_file| is the file to load strings and 1x icons from. + // |resource_file_200| can be an empty string, otherwise it is the file to + // load 2x icons from. + AuraInit(shell::Connector* connector, + const std::string& resource_file, + const std::string& resource_file_200 = std::string()); + ~AuraInit(); private: void InitializeResources(shell::Connector* connector); -#if defined(OS_LINUX) && !defined(OS_ANDROID) +#if defined(OS_LINUX) sk_sp<font_service::FontLoader> font_loader_; #endif const std::string resource_file_; + const std::string resource_file_200_; std::unique_ptr<aura::Env> env_; std::unique_ptr<ViewsDelegate> views_delegate_; diff --git a/chromium/ui/views/mus/clipboard_mus.cc b/chromium/ui/views/mus/clipboard_mus.cc index 7561c42db49..5738e932f09 100644 --- a/chromium/ui/views/mus/clipboard_mus.cc +++ b/chromium/ui/views/mus/clipboard_mus.cc @@ -4,6 +4,10 @@ #include "ui/views/mus/clipboard_mus.h" +#include <string> +#include <utility> +#include <vector> + #include "base/logging.h" #include "base/stl_util.h" #include "base/strings/utf_string_conversions.h" @@ -17,18 +21,19 @@ namespace views { namespace { -mus::mojom::Clipboard::Type GetType(ui::ClipboardType type) { +ui::mojom::Clipboard::Type GetType(ui::ClipboardType type) { switch (type) { case ui::CLIPBOARD_TYPE_COPY_PASTE: - return mus::mojom::Clipboard::Type::COPY_PASTE; + return ui::mojom::Clipboard::Type::COPY_PASTE; case ui::CLIPBOARD_TYPE_SELECTION: - return mus::mojom::Clipboard::Type::SELECTION; + return ui::mojom::Clipboard::Type::SELECTION; case ui::CLIPBOARD_TYPE_DRAG: - return mus::mojom::Clipboard::Type::DRAG; + // Only OSX uses a drag clipboard. + break; } NOTREACHED(); - return mus::mojom::Clipboard::Type::COPY_PASTE; + return ui::mojom::Clipboard::Type::COPY_PASTE; } // The source URL of copied HTML. @@ -41,7 +46,7 @@ ClipboardMus::ClipboardMus() {} ClipboardMus::~ClipboardMus() {} void ClipboardMus::Init(shell::Connector* connector) { - connector->ConnectToInterface("mojo:mus", &clipboard_); + connector->ConnectToInterface("service:ui", &clipboard_); } // TODO(erg): This isn't optimal. It would be better to move the entire @@ -49,19 +54,19 @@ void ClipboardMus::Init(shell::Connector* connector) { // change. mojo::String ClipboardMus::GetMimeTypeFor(const FormatType& format) { if (format.Equals(GetUrlFormatType()) || format.Equals(GetUrlWFormatType())) - return mus::mojom::kMimeTypeURIList; + return ui::mojom::kMimeTypeURIList; if (format.Equals(GetMozUrlFormatType())) - return mus::mojom::kMimeTypeMozillaURL; + return ui::mojom::kMimeTypeMozillaURL; if (format.Equals(GetPlainTextFormatType()) || format.Equals(GetPlainTextWFormatType())) { - return mus::mojom::kMimeTypeText; + return ui::mojom::kMimeTypeText; } if (format.Equals(GetHtmlFormatType())) - return mus::mojom::kMimeTypeHTML; + return ui::mojom::kMimeTypeHTML; if (format.Equals(GetRtfFormatType())) - return mus::mojom::kMimeTypeRTF; + return ui::mojom::kMimeTypeRTF; if (format.Equals(GetBitmapFormatType())) - return mus::mojom::kMimeTypePNG; + return ui::mojom::kMimeTypePNG; if (format.Equals(GetWebKitSmartPasteFormatType())) return kMimeTypeWebkitSmartPaste; if (format.Equals(GetWebCustomDataFormatType())) @@ -78,7 +83,7 @@ mojo::String ClipboardMus::GetMimeTypeFor(const FormatType& format) { bool ClipboardMus::HasMimeType(const mojo::Array<mojo::String>& available_types, const std::string& type) const { - return ContainsValue(available_types, type); + return base::ContainsValue(available_types, type); } uint64_t ClipboardMus::GetSequenceNumber(ui::ClipboardType type) const { @@ -98,7 +103,7 @@ bool ClipboardMus::IsFormatAvailable(const FormatType& format, &available_types); mojo::String format_in_mime = GetMimeTypeFor(format); - return ContainsValue(available_types, format_in_mime); + return base::ContainsValue(available_types, format_in_mime); } void ClipboardMus::Clear(ui::ClipboardType type) { @@ -120,14 +125,14 @@ void ClipboardMus::ReadAvailableTypes(ui::ClipboardType type, &available_types); types->clear(); - if (HasMimeType(available_types, mus::mojom::kMimeTypeText)) - types->push_back(base::UTF8ToUTF16(mus::mojom::kMimeTypeText)); - if (HasMimeType(available_types, mus::mojom::kMimeTypeHTML)) - types->push_back(base::UTF8ToUTF16(mus::mojom::kMimeTypeHTML)); - if (HasMimeType(available_types, mus::mojom::kMimeTypeRTF)) - types->push_back(base::UTF8ToUTF16(mus::mojom::kMimeTypeRTF)); - if (HasMimeType(available_types, mus::mojom::kMimeTypePNG)) - types->push_back(base::UTF8ToUTF16(mus::mojom::kMimeTypePNG)); + if (HasMimeType(available_types, ui::mojom::kMimeTypeText)) + types->push_back(base::UTF8ToUTF16(ui::mojom::kMimeTypeText)); + if (HasMimeType(available_types, ui::mojom::kMimeTypeHTML)) + types->push_back(base::UTF8ToUTF16(ui::mojom::kMimeTypeHTML)); + if (HasMimeType(available_types, ui::mojom::kMimeTypeRTF)) + types->push_back(base::UTF8ToUTF16(ui::mojom::kMimeTypeRTF)); + if (HasMimeType(available_types, ui::mojom::kMimeTypePNG)) + types->push_back(base::UTF8ToUTF16(ui::mojom::kMimeTypePNG)); if (HasMimeType(available_types, kMimeTypeWebCustomData)) { mojo::Array<uint8_t> custom_data; @@ -147,7 +152,7 @@ void ClipboardMus::ReadText(ui::ClipboardType type, mojo::Array<uint8_t> text_data; uint64_t sequence_number = 0; if (clipboard_->ReadClipboardData(GetType(type), - mojo::String(mus::mojom::kMimeTypeText), + mojo::String(ui::mojom::kMimeTypeText), &sequence_number, &text_data)) { std::string text = text_data.To<std::string>(); *result = base::UTF8ToUTF16(text); @@ -160,7 +165,7 @@ void ClipboardMus::ReadAsciiText(ui::ClipboardType type, mojo::Array<uint8_t> text_data; uint64_t sequence_number = 0; if (clipboard_->ReadClipboardData(GetType(type), - mojo::String(mus::mojom::kMimeTypeText), + mojo::String(ui::mojom::kMimeTypeText), &sequence_number, &text_data)) { *result = text_data.To<std::string>(); } @@ -181,7 +186,7 @@ void ClipboardMus::ReadHTML(ui::ClipboardType type, mojo::Array<uint8_t> html_data; uint64_t sequence_number = 0; if (clipboard_->ReadClipboardData(GetType(type), - mojo::String(mus::mojom::kMimeTypeHTML), + mojo::String(ui::mojom::kMimeTypeHTML), &sequence_number, &html_data)) { *markup = base::UTF8ToUTF16(html_data.To<std::string>()); *fragment_end = static_cast<uint32_t>(markup->length()); @@ -200,9 +205,9 @@ void ClipboardMus::ReadRTF(ui::ClipboardType type, std::string* result) const { mojo::SyncCallRestrictions::ScopedAllowSyncCall allow_sync_call; mojo::Array<uint8_t> rtf_data; uint64_t sequence_number = 0; - if (clipboard_->ReadClipboardData( - GetType(type), mojo::String(mus::mojom::kMimeTypeRTF), - &sequence_number, &rtf_data)) { + if (clipboard_->ReadClipboardData(GetType(type), + mojo::String(ui::mojom::kMimeTypeRTF), + &sequence_number, &rtf_data)) { *result = rtf_data.To<std::string>(); } } @@ -211,9 +216,9 @@ SkBitmap ClipboardMus::ReadImage(ui::ClipboardType type) const { mojo::SyncCallRestrictions::ScopedAllowSyncCall allow_sync_call; mojo::Array<uint8_t> data; uint64_t sequence_number = 0; - if (clipboard_->ReadClipboardData( - GetType(type), mojo::String(mus::mojom::kMimeTypePNG), - &sequence_number, &data)) { + if (clipboard_->ReadClipboardData(GetType(type), + mojo::String(ui::mojom::kMimeTypePNG), + &sequence_number, &data)) { SkBitmap bitmap; if (gfx::PNGCodec::Decode(&data.front(), data.size(), &bitmap)) return SkBitmap(bitmap); @@ -246,16 +251,17 @@ void ClipboardMus::ReadData(const FormatType& format, mojo::SyncCallRestrictions::ScopedAllowSyncCall allow_sync_call; mojo::Array<uint8_t> data; uint64_t sequence_number = 0; - if (clipboard_->ReadClipboardData(mus::mojom::Clipboard::Type::COPY_PASTE, - GetMimeTypeFor(format), - &sequence_number, &data)) { + if (clipboard_->ReadClipboardData(ui::mojom::Clipboard::Type::COPY_PASTE, + GetMimeTypeFor(format), &sequence_number, + &data)) { *result = data.To<std::string>(); } } void ClipboardMus::WriteObjects(ui::ClipboardType type, const ObjectMap& objects) { - current_clipboard_.reset(new mojo::Map<mojo::String, mojo::Array<uint8_t>>); + current_clipboard_ = + base::MakeUnique<mojo::Map<mojo::String, mojo::Array<uint8_t>>>(); for (const auto& p : objects) DispatchObject(static_cast<ObjectType>(p.first), p.second); @@ -270,7 +276,7 @@ void ClipboardMus::WriteObjects(ui::ClipboardType type, void ClipboardMus::WriteText(const char* text_data, size_t text_len) { DCHECK(current_clipboard_); current_clipboard_->insert( - mus::mojom::kMimeTypeText, + ui::mojom::kMimeTypeText, mojo::Array<uint8_t>::From(base::StringPiece(text_data, text_len))); } @@ -280,7 +286,7 @@ void ClipboardMus::WriteHTML(const char* markup_data, size_t url_len) { DCHECK(current_clipboard_); current_clipboard_->insert( - mus::mojom::kMimeTypeHTML, + ui::mojom::kMimeTypeHTML, mojo::Array<uint8_t>::From(base::StringPiece(markup_data, markup_len))); if (url_len > 0) { current_clipboard_->insert( @@ -292,7 +298,7 @@ void ClipboardMus::WriteHTML(const char* markup_data, void ClipboardMus::WriteRTF(const char* rtf_data, size_t data_len) { DCHECK(current_clipboard_); current_clipboard_->insert( - mus::mojom::kMimeTypeRTF, + ui::mojom::kMimeTypeRTF, mojo::Array<uint8_t>::From(base::StringPiece(rtf_data, data_len))); } @@ -307,7 +313,7 @@ void ClipboardMus::WriteBookmark(const char* title_data, base::UTF8ToUTF16(base::StringPiece(title_data, title_len)); DCHECK(current_clipboard_); - current_clipboard_->insert(mus::mojom::kMimeTypeMozillaURL, + current_clipboard_->insert(ui::mojom::kMimeTypeMozillaURL, mojo::Array<uint8_t>::From(bookmark)); } @@ -321,7 +327,7 @@ void ClipboardMus::WriteBitmap(const SkBitmap& bitmap) { // Encode the bitmap as a PNG for transport. std::vector<unsigned char> output; if (gfx::PNGCodec::FastEncodeBGRASkBitmap(bitmap, false, &output)) { - current_clipboard_->insert(mus::mojom::kMimeTypePNG, + current_clipboard_->insert(ui::mojom::kMimeTypePNG, mojo::Array<uint8_t>::From(output)); } } diff --git a/chromium/ui/views/mus/clipboard_mus.h b/chromium/ui/views/mus/clipboard_mus.h index ddfa9fa6d1b..9781a9a6753 100644 --- a/chromium/ui/views/mus/clipboard_mus.h +++ b/chromium/ui/views/mus/clipboard_mus.h @@ -5,7 +5,7 @@ #ifndef UI_VIEWS_MUS_CLIPBOARD_MUS_H_ #define UI_VIEWS_MUS_CLIPBOARD_MUS_H_ -#include "components/mus/public/interfaces/clipboard.mojom.h" +#include "services/ui/public/interfaces/clipboard.mojom.h" #include "ui/base/clipboard/clipboard.h" #include "ui/views/mus/mus_export.h" @@ -70,7 +70,7 @@ class VIEWS_MUS_EXPORT ClipboardMus : public ui::Clipboard { static mojo::String GetMimeTypeFor(const FormatType& format); - mus::mojom::ClipboardPtr clipboard_; + ui::mojom::ClipboardPtr clipboard_; // Internal buffer used to accumulate data types. The public interface is // WriteObjects(), which then calls our base class DispatchObject() which diff --git a/chromium/ui/views/mus/display_list.cc b/chromium/ui/views/mus/display_list.cc deleted file mode 100644 index 5be844ca52f..00000000000 --- a/chromium/ui/views/mus/display_list.cc +++ /dev/null @@ -1,109 +0,0 @@ -// Copyright 2016 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/mus/display_list.h" - -#include "ui/display/display_finder.h" -#include "ui/display/display_observer.h" - -namespace views { - -DisplayList::DisplayList() {} - -DisplayList::~DisplayList() {} - -void DisplayList::AddObserver(display::DisplayObserver* observer) { - observers_.AddObserver(observer); -} - -void DisplayList::RemoveObserver(display::DisplayObserver* observer) { - observers_.RemoveObserver(observer); -} - -DisplayList::Displays::const_iterator DisplayList::FindDisplayById( - int64_t id) const { - for (auto iter = displays_.begin(); iter != displays_.end(); ++iter) { - if (iter->id() == id) - return iter; - } - return displays_.end(); -} - -DisplayList::Displays::iterator DisplayList::FindDisplayById(int64_t id) { - for (auto iter = displays_.begin(); iter != displays_.end(); ++iter) { - if (iter->id() == id) - return iter; - } - return displays_.end(); -} - -DisplayList::Displays::const_iterator DisplayList::GetPrimaryDisplayIterator() - const { - return primary_display_index_ == -1 - ? displays_.end() - : displays_.begin() + primary_display_index_; -} - -void DisplayList::UpdateDisplay(const display::Display& display, Type type) { - auto iter = FindDisplayById(display.id()); - DCHECK(iter != displays_.end()); - - display::Display* local_display = &(*iter); - uint32_t changed_values = 0; - if (type == Type::PRIMARY && - static_cast<int>(iter - displays_.begin()) != - static_cast<int>(GetPrimaryDisplayIterator() - displays_.begin())) { - primary_display_index_ = static_cast<int>(iter - displays_.begin()); - // ash::DisplayManager only notifies for the Display gaining primary, not - // the one losing it. - changed_values |= display::DisplayObserver::DISPLAY_METRIC_PRIMARY; - } - if (local_display->bounds() != display.bounds()) { - local_display->set_bounds(display.bounds()); - changed_values |= display::DisplayObserver::DISPLAY_METRIC_BOUNDS; - } - if (local_display->work_area() != display.work_area()) { - local_display->set_work_area(display.work_area()); - changed_values |= display::DisplayObserver::DISPLAY_METRIC_WORK_AREA; - } - if (local_display->rotation() != display.rotation()) { - local_display->set_rotation(display.rotation()); - changed_values |= display::DisplayObserver::DISPLAY_METRIC_ROTATION; - } - if (local_display->device_scale_factor() != display.device_scale_factor()) { - local_display->set_device_scale_factor(display.device_scale_factor()); - changed_values |= - display::DisplayObserver::DISPLAY_METRIC_DEVICE_SCALE_FACTOR; - } - FOR_EACH_OBSERVER(display::DisplayObserver, observers_, - OnDisplayMetricsChanged(*local_display, changed_values)); -} - -void DisplayList::AddDisplay(const display::Display& display, Type type) { - DCHECK(displays_.end() == FindDisplayById(display.id())); - displays_.push_back(display); - if (type == Type::PRIMARY) - primary_display_index_ = static_cast<int>(displays_.size()) - 1; - FOR_EACH_OBSERVER(display::DisplayObserver, observers_, - OnDisplayAdded(display)); -} - -void DisplayList::RemoveDisplay(int64_t id) { - auto iter = FindDisplayById(id); - DCHECK(displays_.end() != iter); - if (primary_display_index_ == static_cast<int>(iter - displays_.begin())) { - // We expect the primary to change before removing it. The only case we - // allow removal of the primary is if it is the list display. - DCHECK_EQ(1u, displays_.size()); - primary_display_index_ = -1; - } else if (primary_display_index_ > - static_cast<int>(iter - displays_.begin())) { - primary_display_index_--; - } - const display::Display display = *iter; - displays_.erase(iter); - FOR_EACH_OBSERVER(display::DisplayObserver, observers_, - OnDisplayRemoved(display)); -} -} // namespace views diff --git a/chromium/ui/views/mus/display_list.h b/chromium/ui/views/mus/display_list.h deleted file mode 100644 index 19b0ac97f11..00000000000 --- a/chromium/ui/views/mus/display_list.h +++ /dev/null @@ -1,69 +0,0 @@ -// Copyright 2016 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_MUS_DISPLAY_LIST_H_ -#define UI_VIEWS_MUS_DISPLAY_LIST_H_ - -#include <stdint.h> - -#include <vector> - -#include "base/observer_list.h" -#include "mojo/public/cpp/bindings/binding.h" -#include "ui/display/display.h" -#include "ui/views/mus/mus_export.h" - -namespace display { -class Display; -class DisplayObserver; -} - -namespace views { - -// Maintains an ordered list of display::Displays as well as operations to add, -// remove and update said list. Additionally maintains display::DisplayObservers -// and updates them as appropriate. -class VIEWS_MUS_EXPORT DisplayList { - public: - using Displays = std::vector<display::Display>; - - enum class Type { - PRIMARY, - NOT_PRIMARY, - }; - - DisplayList(); - ~DisplayList(); - - void AddObserver(display::DisplayObserver* observer); - void RemoveObserver(display::DisplayObserver* observer); - - const Displays& displays() const { return displays_; } - - Displays::const_iterator FindDisplayById(int64_t id) const; - Displays::iterator FindDisplayById(int64_t id); - - Displays::const_iterator GetPrimaryDisplayIterator() const; - - // Updates the cached id based on display.id() as well as whether the Display - // is the primary display. - void UpdateDisplay(const display::Display& display, Type type); - - // Adds a new Display. - void AddDisplay(const display::Display& display, Type type); - - // Removes the Display with the specified id. - void RemoveDisplay(int64_t id); - - private: - std::vector<display::Display> displays_; - int primary_display_index_ = -1; - base::ObserverList<display::DisplayObserver> observers_; - - DISALLOW_COPY_AND_ASSIGN(DisplayList); -}; - -} // namespace views - -#endif // UI_VIEWS_MUS_DISPLAY_LIST_H_ diff --git a/chromium/ui/views/mus/display_list_unittest.cc b/chromium/ui/views/mus/display_list_unittest.cc deleted file mode 100644 index 93cdc81e608..00000000000 --- a/chromium/ui/views/mus/display_list_unittest.cc +++ /dev/null @@ -1,113 +0,0 @@ -// Copyright 2016 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/mus/display_list.h" - -#include <string> -#include <vector> - -#include "base/strings/string_number_conversions.h" -#include "testing/gtest/include/gtest/gtest.h" -#include "ui/display/display.h" -#include "ui/display/display_observer.h" - -using display::Display; - -namespace views { -namespace { - -class DisplayObserverImpl : public display::DisplayObserver { - public: - DisplayObserverImpl() {} - ~DisplayObserverImpl() override {} - - std::string GetAndClearChanges() { - std::string changes; - std::swap(changes, changes_); - return changes; - } - - private: - static void AddPartChange(uint32_t changed, - uint32_t part, - const std::string& description, - std::string* changed_string) { - if ((changed & part) != part) - return; - - *changed_string += " "; - *changed_string += description; - } - - void AddChange(const std::string& change) { - if (!changes_.empty()) - changes_ += "\n"; - changes_ += change; - } - - void OnDisplayAdded(const Display& new_display) override { - AddChange("Added id=" + base::Int64ToString(new_display.id())); - } - void OnDisplayRemoved(const Display& old_display) override { - AddChange("Removed id=" + base::Int64ToString(old_display.id())); - } - void OnDisplayMetricsChanged(const Display& display, - uint32_t changed_metrics) override { - std::string parts; - AddPartChange(changed_metrics, DISPLAY_METRIC_BOUNDS, "bounds", &parts); - AddPartChange(changed_metrics, DISPLAY_METRIC_WORK_AREA, "work_area", - &parts); - AddPartChange(changed_metrics, DISPLAY_METRIC_DEVICE_SCALE_FACTOR, - "scale_factor", &parts); - AddPartChange(changed_metrics, DISPLAY_METRIC_ROTATION, "rotation", &parts); - AddPartChange(changed_metrics, DISPLAY_METRIC_PRIMARY, "primary", &parts); - - AddChange("Changed id=" + base::Int64ToString(display.id()) + parts); - } - - std::string changes_; - - DISALLOW_COPY_AND_ASSIGN(DisplayObserverImpl); -}; - -TEST(DisplayListTest, AddUpdateRemove) { - DisplayList display_list; - DisplayObserverImpl observer; - display_list.AddObserver(&observer); - display_list.AddDisplay(display::Display(2, gfx::Rect(0, 0, 801, 802)), - DisplayList::Type::PRIMARY); - EXPECT_EQ("Added id=2", observer.GetAndClearChanges()); - - // Update the bounds. - { - display::Display updated_display = *(display_list.displays().begin()); - updated_display.set_bounds(gfx::Rect(0, 0, 803, 802)); - display_list.UpdateDisplay(updated_display, DisplayList::Type::PRIMARY); - EXPECT_EQ("Changed id=2 bounds", observer.GetAndClearChanges()); - } - - // Add another. - display_list.AddDisplay(display::Display(3, gfx::Rect(0, 0, 809, 802)), - DisplayList::Type::NOT_PRIMARY); - EXPECT_EQ("Added id=3", observer.GetAndClearChanges()); - ASSERT_EQ(2u, display_list.displays().size()); - EXPECT_EQ(2, display_list.displays()[0].id()); - EXPECT_EQ(3, display_list.displays()[1].id()); - EXPECT_EQ(2, display_list.GetPrimaryDisplayIterator()->id()); - - // Make the second the primary. - display_list.UpdateDisplay(display_list.displays()[1], - DisplayList::Type::PRIMARY); - EXPECT_EQ("Changed id=3 primary", observer.GetAndClearChanges()); - EXPECT_EQ(3, display_list.GetPrimaryDisplayIterator()->id()); - - // Delete the first. - display_list.RemoveDisplay(2); - ASSERT_EQ(1u, display_list.displays().size()); - EXPECT_EQ("Removed id=2", observer.GetAndClearChanges()); - EXPECT_EQ(3, display_list.GetPrimaryDisplayIterator()->id()); -} - -} // namespace -} // namespace views diff --git a/chromium/ui/views/mus/drag_drop_client_mus.cc b/chromium/ui/views/mus/drag_drop_client_mus.cc new file mode 100644 index 00000000000..d4d65cf9fbf --- /dev/null +++ b/chromium/ui/views/mus/drag_drop_client_mus.cc @@ -0,0 +1,92 @@ +// Copyright 2016 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/mus/drag_drop_client_mus.h" + +#include <map> +#include <string> +#include <vector> + +#include "base/message_loop/message_loop.h" +#include "base/run_loop.h" +#include "services/ui/public/cpp/window.h" +#include "ui/aura/window.h" +#include "ui/views/mus/os_exchange_data_provider_mus.h" + +namespace views { +namespace { + +DragDropClientMus* current_dragging_client = nullptr; + +} // namespace + +DragDropClientMus::DragDropClientMus(ui::Window* ui_window) + : ui_window_(ui_window) {} + +DragDropClientMus::~DragDropClientMus() {} + +int DragDropClientMus::StartDragAndDrop( + const ui::OSExchangeData& data, + aura::Window* root_window, + aura::Window* source_window, + const gfx::Point& screen_location, + int drag_operations, + ui::DragDropTypes::DragEventSource source) { + std::map<std::string, std::vector<uint8_t>> drag_data = + static_cast<const OSExchangeDataProviderMus&>(data.provider()).GetData(); + + // TODO(erg): Right now, I'm passing the cursor_location, but maybe I want to + // pass OSExchangeData::GetDragImageOffset() instead? + + bool success = false; + gfx::Point cursor_location = screen_location; + uint32_t action_taken = ui::mojom::kDropEffectNone; + current_dragging_client = this; + ui_window_->PerformDragDrop( + drag_data, drag_operations, cursor_location, + *data.provider().GetDragImage().bitmap(), + base::Bind(&DragDropClientMus::OnMoveLoopEnd, base::Unretained(this), + &success, &action_taken)); + + base::MessageLoop* loop = base::MessageLoop::current(); + base::MessageLoop::ScopedNestableTaskAllower allow_nested(loop); + base::RunLoop run_loop; + + runloop_quit_closure_ = run_loop.QuitClosure(); + run_loop.Run(); + current_dragging_client = nullptr; + + return action_taken; +} + +void DragDropClientMus::DragUpdate(aura::Window* target, + const ui::LocatedEvent& event) { + // Only called on OSX. + NOTREACHED(); +} + +void DragDropClientMus::Drop(aura::Window* target, + const ui::LocatedEvent& event) { + // Only called on OSX. + NOTREACHED(); +} + +void DragDropClientMus::DragCancel() { + ui_window_->CancelDragDrop(); +} + +bool DragDropClientMus::IsDragDropInProgress() { + return !!current_dragging_client; +} + +void DragDropClientMus::OnMoveLoopEnd(bool* out_success, + uint32_t* out_action, + bool in_success, + uint32_t in_action) { + *out_success = in_success; + *out_action = in_action; + runloop_quit_closure_.Run(); +} + +} // namespace views diff --git a/chromium/ui/views/mus/drag_drop_client_mus.h b/chromium/ui/views/mus/drag_drop_client_mus.h new file mode 100644 index 00000000000..bb0b2954591 --- /dev/null +++ b/chromium/ui/views/mus/drag_drop_client_mus.h @@ -0,0 +1,59 @@ +// Copyright 2016 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_MUS_DRAG_DROP_CLIENT_MUS_H_ +#define UI_VIEWS_MUS_DRAG_DROP_CLIENT_MUS_H_ + +#include "base/callback.h" +#include "ui/base/dragdrop/drag_drop_types.h" +#include "ui/wm/public/drag_drop_client.h" + +namespace aura { +class Window; +} + +namespace ui { +class LocatedEvent; +class OSExchangeData; +class Window; +} + +namespace views { + +// An aura client object that translates aura dragging methods to their +// remote ui::Window equivalents. +class DragDropClientMus : public aura::client::DragDropClient { + public: + DragDropClientMus(ui::Window* ui_window); + ~DragDropClientMus() override; + + // Overridden from aura::client::DragDropClient: + int StartDragAndDrop(const ui::OSExchangeData& data, + aura::Window* root_window, + aura::Window* source_window, + const gfx::Point& screen_location, + int drag_operations, + ui::DragDropTypes::DragEventSource source) override; + void DragUpdate(aura::Window* target, const ui::LocatedEvent& event) override; + void Drop(aura::Window* target, const ui::LocatedEvent& event) override; + void DragCancel() override; + bool IsDragDropInProgress() override; + + private: + // Callback for StartDragAndDrop(). + void OnMoveLoopEnd(bool* out_success, + uint32_t* out_action, + bool in_success, + uint32_t in_action); + + ui::Window* ui_window_; + + base::Closure runloop_quit_closure_; + + DISALLOW_COPY_AND_ASSIGN(DragDropClientMus); +}; + +} // namespace views + +#endif // UI_VIEWS_MUS_DRAG_DROP_CLIENT_MUS_H_ diff --git a/chromium/ui/views/mus/drop_target_mus.cc b/chromium/ui/views/mus/drop_target_mus.cc new file mode 100644 index 00000000000..85f6b2446b9 --- /dev/null +++ b/chromium/ui/views/mus/drop_target_mus.cc @@ -0,0 +1,146 @@ +// Copyright 2016 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/mus/drop_target_mus.h" + +#include <map> +#include <string> +#include <utility> +#include <vector> + +#include "services/ui/public/interfaces/window_tree_constants.mojom.h" +#include "ui/aura/window.h" +#include "ui/aura/window_tree_host.h" +#include "ui/base/dragdrop/drag_drop_types.h" +#include "ui/base/dragdrop/drop_target_event.h" +#include "ui/views/mus/os_exchange_data_provider_mus.h" +#include "ui/wm/public/drag_drop_client.h" +#include "ui/wm/public/drag_drop_delegate.h" + +namespace views { + +static_assert(ui::DragDropTypes::DRAG_NONE == ui::mojom::kDropEffectNone, + "Drag constants must be the same"); +static_assert(ui::DragDropTypes::DRAG_MOVE == ui::mojom::kDropEffectMove, + "Drag constants must be the same"); +static_assert(ui::DragDropTypes::DRAG_COPY == ui::mojom::kDropEffectCopy, + "Drag constants must be the same"); +static_assert(ui::DragDropTypes::DRAG_LINK == ui::mojom::kDropEffectLink, + "Drag constants must be the same"); + +DropTargetMus::DropTargetMus(aura::Window* root_window) + : root_window_(root_window), target_window_(nullptr) {} + +DropTargetMus::~DropTargetMus() {} + +void DropTargetMus::Translate(uint32_t key_state, + const gfx::Point& screen_location, + uint32_t effect, + std::unique_ptr<ui::DropTargetEvent>* event, + aura::client::DragDropDelegate** delegate) { + gfx::Point location = screen_location; + gfx::Point root_location = location; + root_window_->GetHost()->ConvertPointFromNativeScreen(&root_location); + aura::Window* target_window = + root_window_->GetEventHandlerForPoint(root_location); + bool target_window_changed = false; + if (target_window != target_window_) { + if (target_window_) + NotifyDragExited(); + target_window_ = target_window; + if (target_window_) + target_window_->AddObserver(this); + target_window_changed = true; + } + *delegate = nullptr; + if (!target_window_) + return; + *delegate = aura::client::GetDragDropDelegate(target_window_); + if (!*delegate) + return; + + location = root_location; + aura::Window::ConvertPointToTarget(root_window_, target_window_, &location); + *event = base::MakeUnique<ui::DropTargetEvent>( + *(os_exchange_data_.get()), location, root_location, effect); + (*event)->set_flags(key_state); + if (target_window_changed) + (*delegate)->OnDragEntered(*event->get()); +} + +void DropTargetMus::NotifyDragExited() { + if (!target_window_) + return; + + aura::client::DragDropDelegate* delegate = + aura::client::GetDragDropDelegate(target_window_); + if (delegate) + delegate->OnDragExited(); + + target_window_->RemoveObserver(this); + target_window_ = nullptr; +} + +void DropTargetMus::OnDragDropStart( + std::map<std::string, std::vector<uint8_t>> mime_data) { + // We store the mime data here because we need to access it during each phase + // of the drag, but we also don't move the data cross-process multiple times. + os_exchange_data_ = base::MakeUnique<ui::OSExchangeData>( + base::MakeUnique<OSExchangeDataProviderMus>(std::move(mime_data))); +} + +uint32_t DropTargetMus::OnDragEnter(uint32_t key_state, + const gfx::Point& position, + uint32_t effect_bitmask) { + std::unique_ptr<ui::DropTargetEvent> event; + aura::client::DragDropDelegate* delegate = nullptr; + // Translate will call OnDragEntered. + Translate(key_state, position, effect_bitmask, &event, &delegate); + return ui::mojom::kDropEffectNone; +} + +uint32_t DropTargetMus::OnDragOver(uint32_t key_state, + const gfx::Point& position, + uint32_t effect) { + int drag_operation = ui::DragDropTypes::DRAG_NONE; + std::unique_ptr<ui::DropTargetEvent> event; + aura::client::DragDropDelegate* delegate = nullptr; + + Translate(key_state, position, effect, &event, &delegate); + if (delegate) + drag_operation = delegate->OnDragUpdated(*event); + return drag_operation; +} + +void DropTargetMus::OnDragLeave() { + NotifyDragExited(); +} + +uint32_t DropTargetMus::OnCompleteDrop(uint32_t key_state, + const gfx::Point& position, + uint32_t effect) { + int drag_operation = ui::DragDropTypes::DRAG_NONE; + std::unique_ptr<ui::DropTargetEvent> event; + aura::client::DragDropDelegate* delegate = nullptr; + Translate(key_state, position, effect, &event, &delegate); + if (delegate) + drag_operation = delegate->OnPerformDrop(*event); + if (target_window_) { + target_window_->RemoveObserver(this); + target_window_ = nullptr; + } + + return drag_operation; +} + +void DropTargetMus::OnDragDropDone() { + os_exchange_data_.reset(); +} + +void DropTargetMus::OnWindowDestroyed(aura::Window* window) { + DCHECK_EQ(window, target_window_); + target_window_ = nullptr; +} + +} // namespace views diff --git a/chromium/ui/views/mus/drop_target_mus.h b/chromium/ui/views/mus/drop_target_mus.h new file mode 100644 index 00000000000..12cb3ff0bbf --- /dev/null +++ b/chromium/ui/views/mus/drop_target_mus.h @@ -0,0 +1,93 @@ +// Copyright 2016 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_MUS_DROP_TARGET_MUS_H_ +#define UI_VIEWS_MUS_DROP_TARGET_MUS_H_ + +#include <map> +#include <memory> +#include <string> +#include <vector> + +#include "base/macros.h" +#include "services/ui/public/cpp/window_drop_target.h" +#include "ui/aura/window_observer.h" + +namespace aura { +class RootWindow; +class Window; + +namespace client { +class DragDropDelegate; +} +} + +namespace ui { +class DropTargetEvent; +class OSExchangeData; +} + +namespace views { + +// An adapter class which takes signals from mus' WindowDropTarget, performs +// targeting on the underlying aura::Window tree, and dispatches them to the +// aura DragDropDelegate of the targeted aura::Window. +class DropTargetMus : public ui::WindowDropTarget, public aura::WindowObserver { + public: + explicit DropTargetMus(aura::Window* root_window); + ~DropTargetMus() override; + + private: + // Common functionality for the WindowDropTarget methods to translate from + // mus data types to Aura ones. This method takes in mus messages and + // performs the common tasks of keeping track of which aura window (along + // with enter/leave messages at that layer), doing coordinate translation, + // and creation of ui layer event objects that get dispatched to aura/views. + void Translate(uint32_t key_state, + const gfx::Point& screen_location, + uint32_t effect_bitmask, + std::unique_ptr<ui::DropTargetEvent>* event, + aura::client::DragDropDelegate** delegate); + + void NotifyDragExited(); + + // Overridden from ui::WindowDropTarget: + void OnDragDropStart( + std::map<std::string, std::vector<uint8_t>> mime_data) override; + uint32_t OnDragEnter(uint32_t key_state, + const gfx::Point& position, + uint32_t effect_bitmask) override; + uint32_t OnDragOver(uint32_t key_state, + const gfx::Point& position, + uint32_t effect_bitmask) override; + void OnDragLeave() override; + uint32_t OnCompleteDrop(uint32_t key_state, + const gfx::Point& position, + uint32_t effect_bitmask) override; + void OnDragDropDone() override; + + // Overridden from aura::WindowObserver: + void OnWindowDestroyed(aura::Window* window) override; + + // The root window associated with this drop target. + aura::Window* root_window_; + + // The Aura window that is currently under the cursor. We need to manually + // keep track of this because mus will only call our drag enter method once + // when the user enters the associated mus::Window. But inside mus there + // could be multiple aura windows, so we need to generate drag enter events + // for them. + aura::Window* target_window_; + + // The entire drag data payload. We receive this during the drag enter event + // and cache it so we don't send this multiple times. We reset this value on + // leave or drop. + std::unique_ptr<ui::OSExchangeData> os_exchange_data_; + + DISALLOW_COPY_AND_ASSIGN(DropTargetMus); +}; + +} // namespace views + +#endif // UI_VIEWS_MUS_DROP_TARGET_MUS_H_ diff --git a/chromium/ui/views/mus/input_method_mus.cc b/chromium/ui/views/mus/input_method_mus.cc index 352c53869c3..8d5bb8b2cfa 100644 --- a/chromium/ui/views/mus/input_method_mus.cc +++ b/chromium/ui/views/mus/input_method_mus.cc @@ -6,44 +6,52 @@ #include <utility> -#include "components/mus/public/cpp/window.h" +#include "services/ui/public/cpp/window.h" +#include "services/ui/public/interfaces/ime.mojom.h" #include "ui/base/ime/text_input_client.h" #include "ui/events/event.h" #include "ui/platform_window/mojo/ime_type_converters.h" #include "ui/platform_window/mojo/text_input_state.mojom.h" +#include "ui/views/mus/text_input_client_impl.h" namespace views { //////////////////////////////////////////////////////////////////////////////// -// InputMethodMUS, public: +// InputMethodMus, public: -InputMethodMUS::InputMethodMUS(ui::internal::InputMethodDelegate* delegate, - mus::Window* window) +InputMethodMus::InputMethodMus(ui::internal::InputMethodDelegate* delegate, + ui::Window* window) : window_(window) { SetDelegate(delegate); } -InputMethodMUS::~InputMethodMUS() {} +InputMethodMus::~InputMethodMus() {} + +void InputMethodMus::Init(shell::Connector* connector) { + connector->ConnectToInterface("service:ui", &ime_server_); +} //////////////////////////////////////////////////////////////////////////////// -// InputMethodMUS, ui::InputMethod implementation: +// InputMethodMus, ui::InputMethod implementation: -void InputMethodMUS::OnFocus() { +void InputMethodMus::OnFocus() { InputMethodBase::OnFocus(); UpdateTextInputType(); } -void InputMethodMUS::OnBlur() { +void InputMethodMus::OnBlur() { InputMethodBase::OnBlur(); UpdateTextInputType(); } -bool InputMethodMUS::OnUntranslatedIMEMessage(const base::NativeEvent& event, +bool InputMethodMus::OnUntranslatedIMEMessage(const base::NativeEvent& event, NativeEventResult* result) { + // This method is not called on non-Windows platforms. See the comments for + // ui::InputMethod::OnUntranslatedIMEMessage(). return false; } -void InputMethodMUS::DispatchKeyEvent(ui::KeyEvent* event) { +void InputMethodMus::DispatchKeyEvent(ui::KeyEvent* event) { DCHECK(event->type() == ui::ET_KEY_PRESSED || event->type() == ui::ET_KEY_RELEASED); @@ -53,12 +61,14 @@ void InputMethodMUS::DispatchKeyEvent(ui::KeyEvent* event) { return; } - // Here is where we change the differ from our base class's logic. Instead of - // always dispatching a key down event, and then sending a synthesized - // character event, we instead check to see if this is a character event and - // send out the key if it is. (We fallback to normal dispatch if it isn't.) + // TODO(moshayedi): crbug.com/641355. Currently if we stop propagation of + // non-char events here, accelerators ddn't work. This is because we send the + // event ack too early in NativeWidgetMus. We should send both char and + // non-char events to the IME driver once we fix this. if (event->is_char()) { - GetTextInputClient()->InsertChar(*event); + // IME driver will notify the text input client if it is not interested in + // event, which in turn will call DispatchKeyEventPostIME(). + input_method_->ProcessKeyEvent(ui::Event::Clone(*event)); event->StopPropagation(); return; } @@ -66,41 +76,59 @@ void InputMethodMUS::DispatchKeyEvent(ui::KeyEvent* event) { ignore_result(DispatchKeyEventPostIME(event)); } -void InputMethodMUS::OnTextInputTypeChanged(const ui::TextInputClient* client) { +void InputMethodMus::OnTextInputTypeChanged(const ui::TextInputClient* client) { if (IsTextInputClientFocused(client)) UpdateTextInputType(); InputMethodBase::OnTextInputTypeChanged(client); -} -void InputMethodMUS::OnCaretBoundsChanged(const ui::TextInputClient* client) {} + if (input_method_) { + input_method_->OnTextInputTypeChanged( + static_cast<ui::mojom::TextInputType>(client->GetTextInputType())); + } +} -void InputMethodMUS::CancelComposition(const ui::TextInputClient* client) {} +void InputMethodMus::OnCaretBoundsChanged(const ui::TextInputClient* client) { + if (input_method_) + input_method_->OnCaretBoundsChanged(client->GetCaretBounds()); +} -void InputMethodMUS::OnInputLocaleChanged() {} +void InputMethodMus::CancelComposition(const ui::TextInputClient* client) { + if (input_method_) + input_method_->CancelComposition(); +} -std::string InputMethodMUS::GetInputLocale() { - return ""; +void InputMethodMus::OnInputLocaleChanged() { + // TODO(moshayedi): crbug.com/637418. Not supported in ChromeOS. Investigate + // whether we want to support this or not. } -bool InputMethodMUS::IsCandidatePopupOpen() const { +bool InputMethodMus::IsCandidatePopupOpen() const { + // TODO(moshayedi): crbug.com/637416. Implement this properly when we have a + // mean for displaying candidate list popup. return false; } -void InputMethodMUS::OnDidChangeFocusedClient( +void InputMethodMus::OnDidChangeFocusedClient( ui::TextInputClient* focused_before, ui::TextInputClient* focused) { InputMethodBase::OnDidChangeFocusedClient(focused_before, focused); UpdateTextInputType(); + + text_input_client_ = base::MakeUnique<TextInputClientImpl>(focused, this); + ime_server_->StartSession(text_input_client_->CreateInterfacePtrAndBind(), + GetProxy(&input_method_)); } -void InputMethodMUS::UpdateTextInputType() { +void InputMethodMus::UpdateTextInputType() { ui::TextInputType type = GetTextInputType(); mojo::TextInputStatePtr state = mojo::TextInputState::New(); state->type = mojo::ConvertTo<mojo::TextInputType>(type); - if (type != ui::TEXT_INPUT_TYPE_NONE) - window_->SetImeVisibility(true, std::move(state)); - else - window_->SetTextInputState(std::move(state)); + if (window_) { + if (type != ui::TEXT_INPUT_TYPE_NONE) + window_->SetImeVisibility(true, std::move(state)); + else + window_->SetTextInputState(std::move(state)); + } } } // namespace views diff --git a/chromium/ui/views/mus/input_method_mus.h b/chromium/ui/views/mus/input_method_mus.h index e5188a3aac0..aa5dd5d2efc 100644 --- a/chromium/ui/views/mus/input_method_mus.h +++ b/chromium/ui/views/mus/input_method_mus.h @@ -8,21 +8,27 @@ #define UI_VIEWS_MUS_INPUT_METHOD_MUS_H_ #include "base/macros.h" +#include "mojo/public/cpp/bindings/strong_binding.h" +#include "services/shell/public/cpp/connector.h" +#include "services/ui/public/interfaces/ime.mojom.h" #include "ui/views/mus/mus_export.h" -namespace mus { +namespace ui { class Window; } // namespace mojo namespace views { -class VIEWS_MUS_EXPORT InputMethodMUS : public ui::InputMethodBase { +class TextInputClientImpl; + +class VIEWS_MUS_EXPORT InputMethodMus : public ui::InputMethodBase { public: - InputMethodMUS(ui::internal::InputMethodDelegate* delegate, - mus::Window* window); - ~InputMethodMUS() override; + InputMethodMus(ui::internal::InputMethodDelegate* delegate, + ui::Window* window); + ~InputMethodMus() override; + + void Init(shell::Connector* connector); - private: // Overridden from ui::InputMethod: void OnFocus() override; void OnBlur() override; @@ -33,19 +39,26 @@ class VIEWS_MUS_EXPORT InputMethodMUS : public ui::InputMethodBase { void OnCaretBoundsChanged(const ui::TextInputClient* client) override; void CancelComposition(const ui::TextInputClient* client) override; void OnInputLocaleChanged() override; - std::string GetInputLocale() override; bool IsCandidatePopupOpen() const override; + private: + friend TextInputClientImpl; + // Overridden from ui::InputMethodBase: void OnDidChangeFocusedClient(ui::TextInputClient* focused_before, ui::TextInputClient* focused) override; void UpdateTextInputType(); - // The toplevel window which is not owned by this class. - mus::Window* window_; + // The toplevel window which is not owned by this class. This may be null + // for tests. + ui::Window* window_; + + ui::mojom::IMEServerPtr ime_server_; + ui::mojom::InputMethodPtr input_method_; + std::unique_ptr<TextInputClientImpl> text_input_client_; - DISALLOW_COPY_AND_ASSIGN(InputMethodMUS); + DISALLOW_COPY_AND_ASSIGN(InputMethodMus); }; } // namespace views diff --git a/chromium/ui/views/mus/input_method_mus_unittest.cc b/chromium/ui/views/mus/input_method_mus_unittest.cc new file mode 100644 index 00000000000..1e312529cfb --- /dev/null +++ b/chromium/ui/views/mus/input_method_mus_unittest.cc @@ -0,0 +1,101 @@ +// Copyright 2016 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/mus/input_method_mus.h" + +#include <memory> + +#include "base/message_loop/message_loop.h" +#include "base/run_loop.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "ui/base/ime/dummy_text_input_client.h" +#include "ui/base/ime/input_method_delegate.h" +#include "ui/events/event.h" +#include "ui/views/mus/window_manager_connection.h" +#include "ui/views/test/scoped_views_test_helper.h" + +namespace views { +namespace { + +class TestInputMethodDelegate : public ui::internal::InputMethodDelegate { + public: + TestInputMethodDelegate() {} + ~TestInputMethodDelegate() override {} + + // ui::internal::InputMethodDelegate: + ui::EventDispatchDetails DispatchKeyEventPostIME( + ui::KeyEvent* key_event) override { + return ui::EventDispatchDetails(); + } + + private: + DISALLOW_COPY_AND_ASSIGN(TestInputMethodDelegate); +}; + +class TestTextInputClient : public ui::DummyTextInputClient { + public: + TestTextInputClient() {} + ~TestTextInputClient() override {} + + ui::KeyEvent* WaitUntilInputReceieved() { + run_loop_ = base::MakeUnique<base::RunLoop>(); + run_loop_->Run(); + run_loop_.reset(); + + return received_event_->AsKeyEvent(); + } + + // ui::DummyTextInputClient: + void InsertChar(const ui::KeyEvent& event) override { + received_event_ = ui::Event::Clone(event); + run_loop_->Quit(); + } + + private: + std::unique_ptr<base::RunLoop> run_loop_; + std::unique_ptr<ui::Event> received_event_; + + DISALLOW_COPY_AND_ASSIGN(TestTextInputClient); +}; + +} // namespace + +class InputMethodMusTest : public testing::Test { + public: + InputMethodMusTest() : message_loop_(base::MessageLoop::TYPE_UI) {} + ~InputMethodMusTest() override {} + + shell::Connector* connector() { + return WindowManagerConnection::Get()->connector(); + } + + private: + base::MessageLoop message_loop_; + ScopedViewsTestHelper helper_; + + DISALLOW_COPY_AND_ASSIGN(InputMethodMusTest); +}; + +TEST_F(InputMethodMusTest, DispatchKeyEvent) { + // test_ime_driver will register itself as the current IMEDriver. It echoes + // back the character key events it receives. + EXPECT_TRUE(connector()->Connect("service:test_ime_driver")); + + TestInputMethodDelegate input_method_delegate; + InputMethodMus input_method(&input_method_delegate, nullptr); + input_method.Init(connector()); + + TestTextInputClient text_input_client; + input_method.SetFocusedTextInputClient(&text_input_client); + + ui::KeyEvent key_event('A', ui::VKEY_A, 0); + input_method.DispatchKeyEvent(&key_event); + + ui::KeyEvent* received_event = text_input_client.WaitUntilInputReceieved(); + EXPECT_EQ(ui::ET_KEY_PRESSED, received_event->type()); + EXPECT_TRUE(received_event->is_char()); + EXPECT_EQ(key_event.GetCharacter(), received_event->GetCharacter()); +} + +} // namespace views diff --git a/chromium/ui/views/mus/interactive_ui_tests_manifest.json b/chromium/ui/views/mus/interactive_ui_tests_manifest.json index a0b81fcb72e..0c326823637 100644 --- a/chromium/ui/views/mus/interactive_ui_tests_manifest.json +++ b/chromium/ui/views/mus/interactive_ui_tests_manifest.json @@ -1,6 +1,6 @@ { "manifest_version": 1, - "name": "mojo:views_mus_interactive_ui_tests", + "name": "service:views_mus_interactive_ui_tests", "display_name": "Views Mus Interactive UI Tests", "capabilities": { "required": { diff --git a/chromium/ui/views/mus/native_widget_mus.cc b/chromium/ui/views/mus/native_widget_mus.cc index 9bed4cca17d..4024de71b40 100644 --- a/chromium/ui/views/mus/native_widget_mus.cc +++ b/chromium/ui/views/mus/native_widget_mus.cc @@ -2,31 +2,36 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +// This has to be before any other includes, else default is picked up. +// See base/logging for details on this. +#define NOTIMPLEMENTED_POLICY 5 + #include "ui/views/mus/native_widget_mus.h" +#include <utility> +#include <vector> + #include "base/callback.h" #include "base/macros.h" #include "base/message_loop/message_loop.h" +#include "base/run_loop.h" #include "base/threading/thread_task_runner_handle.h" -#include "components/bitmap_uploader/bitmap_uploader.h" -#include "components/mus/public/cpp/property_type_converters.h" -#include "components/mus/public/cpp/window.h" -#include "components/mus/public/cpp/window_observer.h" -#include "components/mus/public/cpp/window_property.h" -#include "components/mus/public/cpp/window_tree_client.h" -#include "components/mus/public/interfaces/cursor.mojom.h" -#include "components/mus/public/interfaces/window_manager.mojom.h" -#include "components/mus/public/interfaces/window_manager_constants.mojom.h" -#include "components/mus/public/interfaces/window_tree.mojom.h" +#include "services/ui/public/cpp/property_type_converters.h" +#include "services/ui/public/cpp/window.h" +#include "services/ui/public/cpp/window_observer.h" +#include "services/ui/public/cpp/window_property.h" +#include "services/ui/public/cpp/window_tree_client.h" +#include "services/ui/public/interfaces/cursor.mojom.h" +#include "services/ui/public/interfaces/window_manager.mojom.h" +#include "services/ui/public/interfaces/window_manager_constants.mojom.h" +#include "services/ui/public/interfaces/window_tree.mojom.h" #include "ui/aura/client/default_capture_client.h" #include "ui/aura/client/window_tree_client.h" -#include "ui/aura/env.h" #include "ui/aura/layout_manager.h" #include "ui/aura/mus/mus_util.h" #include "ui/aura/window.h" #include "ui/aura/window_property.h" #include "ui/base/hit_test.h" -#include "ui/base/view_prop.h" #include "ui/display/display.h" #include "ui/display/screen.h" #include "ui/events/event.h" @@ -34,10 +39,14 @@ #include "ui/gfx/path.h" #include "ui/native_theme/native_theme_aura.h" #include "ui/platform_window/platform_window_delegate.h" -#include "ui/views/mus/surface_context_factory.h" +#include "ui/views/drag_utils.h" +#include "ui/views/mus/drag_drop_client_mus.h" +#include "ui/views/mus/drop_target_mus.h" +#include "ui/views/mus/window_manager_connection.h" #include "ui/views/mus/window_manager_constants_converters.h" #include "ui/views/mus/window_manager_frame_values.h" #include "ui/views/mus/window_tree_host_mus.h" +#include "ui/views/widget/drop_helper.h" #include "ui/views/widget/native_widget_aura.h" #include "ui/views/widget/widget_delegate.h" #include "ui/views/window/custom_frame_view.h" @@ -48,14 +57,14 @@ #include "ui/wm/core/focus_controller.h" #include "ui/wm/core/native_cursor_manager.h" -DECLARE_WINDOW_PROPERTY_TYPE(mus::Window*); +DECLARE_WINDOW_PROPERTY_TYPE(ui::Window*); -using mus::mojom::EventResult; +using ui::mojom::EventResult; namespace views { namespace { -DEFINE_WINDOW_PROPERTY_KEY(mus::Window*, kMusWindow, nullptr); +DEFINE_WINDOW_PROPERTY_KEY(ui::Window*, kMusWindow, nullptr); MUS_DEFINE_WINDOW_PROPERTY_KEY(NativeWidgetMus*, kNativeWidgetMusKey, nullptr); @@ -77,7 +86,7 @@ class FocusRulesImpl : public wm::BaseFocusRules { // This makes sure that an aura::Window focused (or activated) through the // aura::client::FocusClient (or ActivationClient) focuses (or activates) the -// corresponding mus::Window too. +// corresponding ui::Window too. class FocusControllerMus : public wm::FocusController { public: explicit FocusControllerMus(wm::FocusRules* rules) : FocusController(rules) {} @@ -87,8 +96,7 @@ class FocusControllerMus : public wm::FocusController { void FocusWindow(aura::Window* window) override { FocusController::FocusWindow(window); if (window) { - mus::Window* mus_window = - window->GetRootWindow()->GetProperty(kMusWindow); + ui::Window* mus_window = window->GetRootWindow()->GetProperty(kMusWindow); if (mus_window) mus_window->SetFocus(); } @@ -99,9 +107,9 @@ class FocusControllerMus : public wm::FocusController { class ContentWindowLayoutManager : public aura::LayoutManager { public: - ContentWindowLayoutManager(aura::Window* outer, aura::Window* inner) + ContentWindowLayoutManager(aura::Window* outer, aura::Window* inner) : outer_(outer), inner_(inner) {} - ~ContentWindowLayoutManager() override {} + ~ContentWindowLayoutManager() override {} private: // aura::LayoutManager: @@ -147,10 +155,10 @@ class NativeWidgetMusWindowTreeClient : public aura::client::WindowTreeClient { DISALLOW_COPY_AND_ASSIGN(NativeWidgetMusWindowTreeClient); }; -// A screen position client that applies the offset of the mus::Window. +// A screen position client that applies the offset of the ui::Window. class ScreenPositionClientMus : public wm::DefaultScreenPositionClient { public: - explicit ScreenPositionClientMus(mus::Window* mus_window) + explicit ScreenPositionClientMus(ui::Window* mus_window) : mus_window_(mus_window) {} ~ScreenPositionClientMus() override {} @@ -169,14 +177,14 @@ class ScreenPositionClientMus : public wm::DefaultScreenPositionClient { } private: - mus::Window* mus_window_; + ui::Window* mus_window_; DISALLOW_COPY_AND_ASSIGN(ScreenPositionClientMus); }; class NativeCursorManagerMus : public wm::NativeCursorManager { public: - explicit NativeCursorManagerMus(mus::Window* mus_window) + explicit NativeCursorManagerMus(ui::Window* mus_window) : mus_window_(mus_window) {} ~NativeCursorManagerMus() override {} @@ -188,7 +196,7 @@ class NativeCursorManagerMus : public wm::NativeCursorManager { void SetCursor(gfx::NativeCursor cursor, wm::NativeCursorManagerDelegate* delegate) override { - mus_window_->SetPredefinedCursor(mus::mojom::Cursor(cursor.native_type())); + mus_window_->SetPredefinedCursor(ui::mojom::Cursor(cursor.native_type())); delegate->CommitCursor(cursor); } @@ -199,7 +207,7 @@ class NativeCursorManagerMus : public wm::NativeCursorManager { if (visible) SetCursor(delegate->GetCursor(), delegate); else - mus_window_->SetPredefinedCursor(mus::mojom::Cursor::NONE); + mus_window_->SetPredefinedCursor(ui::mojom::Cursor::NONE); } void SetCursorSet(ui::CursorSetType cursor_set, @@ -224,7 +232,7 @@ class NativeCursorManagerMus : public wm::NativeCursorManager { } private: - mus::Window* mus_window_; + ui::Window* mus_window_; DISALLOW_COPY_AND_ASSIGN(NativeCursorManagerMus); }; @@ -248,11 +256,16 @@ class ClientSideNonClientFrameView : public NonClientFrameView { // NonClientFrameView: gfx::Rect GetBoundsForClientView() const override { gfx::Rect result(GetLocalBounds()); + if (widget_->IsFullscreen()) + return result; result.Inset(GetDefaultWindowManagerInsets(widget_->IsMaximized())); return result; } gfx::Rect GetWindowBoundsForClientBounds( const gfx::Rect& client_bounds) const override { + if (widget_->IsFullscreen()) + return client_bounds; + const gfx::Insets insets( GetDefaultWindowManagerInsets(widget_->IsMaximized())); return gfx::Rect(client_bounds.x() - insets.left(), @@ -305,15 +318,15 @@ class ClientSideNonClientFrameView : public NonClientFrameView { int ResizeBehaviorFromDelegate(WidgetDelegate* delegate) { if (!delegate) - return mus::mojom::kResizeBehaviorNone; + return ui::mojom::kResizeBehaviorNone; - int32_t behavior = mus::mojom::kResizeBehaviorNone; + int32_t behavior = ui::mojom::kResizeBehaviorNone; if (delegate->CanResize()) - behavior |= mus::mojom::kResizeBehaviorCanResize; + behavior |= ui::mojom::kResizeBehaviorCanResize; if (delegate->CanMaximize()) - behavior |= mus::mojom::kResizeBehaviorCanMaximize; + behavior |= ui::mojom::kResizeBehaviorCanMaximize; if (delegate->CanMinimize()) - behavior |= mus::mojom::kResizeBehaviorCanMinimize; + behavior |= ui::mojom::kResizeBehaviorCanMinimize; return behavior; } @@ -328,7 +341,7 @@ SkBitmap AppIconFromDelegate(WidgetDelegate* delegate) { return app_icon.GetRepresentation(1.f).sk_bitmap(); } -// Handles acknowledgement of an input event, either immediately when a nested +// Handles acknowledgment of an input event, either immediately when a nested // message loop starts, or upon destruction. class EventAckHandler : public base::MessageLoop::NestingObserver { public: @@ -366,13 +379,31 @@ class EventAckHandler : public base::MessageLoop::NestingObserver { DISALLOW_COPY_AND_ASSIGN(EventAckHandler); }; +void OnMoveLoopEnd(bool* out_success, + base::Closure quit_closure, + bool in_success) { + *out_success = in_success; + quit_closure.Run(); +} + +ui::mojom::ShowState GetShowState(const ui::Window* window) { + if (!window || + !window->HasSharedProperty( + ui::mojom::WindowManager::kShowState_Property)) { + return ui::mojom::ShowState::DEFAULT; + } + + return static_cast<ui::mojom::ShowState>(window->GetSharedProperty<int32_t>( + ui::mojom::WindowManager::kShowState_Property)); +} + } // namespace -class NativeWidgetMus::MusWindowObserver : public mus::WindowObserver { +class NativeWidgetMus::MusWindowObserver : public ui::WindowObserver { public: explicit MusWindowObserver(NativeWidgetMus* native_widget_mus) : native_widget_mus_(native_widget_mus), - show_state_(mus::mojom::ShowState::DEFAULT) { + show_state_(ui::mojom::ShowState::DEFAULT) { mus_window()->AddObserver(this); } @@ -380,77 +411,73 @@ class NativeWidgetMus::MusWindowObserver : public mus::WindowObserver { mus_window()->RemoveObserver(this); } - mus::mojom::ShowState show_state() { return show_state_; } - - // mus::WindowObserver: - void OnWindowVisibilityChanging(mus::Window* window) override { - native_widget_mus_->OnMusWindowVisibilityChanging(window); + // ui::WindowObserver: + void OnWindowVisibilityChanging(ui::Window* window, bool visible) override { + native_widget_mus_->OnMusWindowVisibilityChanging(window, visible); } - void OnWindowVisibilityChanged(mus::Window* window) override { - native_widget_mus_->OnMusWindowVisibilityChanged(window); + void OnWindowVisibilityChanged(ui::Window* window, bool visible) override { + native_widget_mus_->OnMusWindowVisibilityChanged(window, visible); } - void OnWindowPredefinedCursorChanged(mus::Window* window, - mus::mojom::Cursor cursor) override { + void OnWindowPredefinedCursorChanged(ui::Window* window, + ui::mojom::Cursor cursor) override { DCHECK_EQ(window, mus_window()); native_widget_mus_->set_last_cursor(cursor); } void OnWindowSharedPropertyChanged( - mus::Window* window, + ui::Window* window, const std::string& name, const std::vector<uint8_t>* old_data, const std::vector<uint8_t>* new_data) override { - if (name != mus::mojom::WindowManager::kShowState_Property) + if (name != ui::mojom::WindowManager::kShowState_Property) return; - mus::mojom::ShowState show_state = - static_cast<mus::mojom::ShowState>(window->GetSharedProperty<int32_t>( - mus::mojom::WindowManager::kShowState_Property)); + const ui::mojom::ShowState show_state = GetShowState(window); if (show_state == show_state_) return; show_state_ = show_state; ui::PlatformWindowState state = ui::PLATFORM_WINDOW_STATE_UNKNOWN; switch (show_state_) { - case mus::mojom::ShowState::MINIMIZED: + case ui::mojom::ShowState::MINIMIZED: state = ui::PLATFORM_WINDOW_STATE_MINIMIZED; break; - case mus::mojom::ShowState::MAXIMIZED: + case ui::mojom::ShowState::MAXIMIZED: state = ui::PLATFORM_WINDOW_STATE_MAXIMIZED; break; - case mus::mojom::ShowState::DEFAULT: - case mus::mojom::ShowState::INACTIVE: - case mus::mojom::ShowState::NORMAL: - case mus::mojom::ShowState::DOCKED: + case ui::mojom::ShowState::DEFAULT: + case ui::mojom::ShowState::INACTIVE: + case ui::mojom::ShowState::NORMAL: + case ui::mojom::ShowState::DOCKED: // TODO(sky): support docked. state = ui::PLATFORM_WINDOW_STATE_NORMAL; break; - case mus::mojom::ShowState::FULLSCREEN: + case ui::mojom::ShowState::FULLSCREEN: state = ui::PLATFORM_WINDOW_STATE_FULLSCREEN; break; } platform_window_delegate()->OnWindowStateChanged(state); } - void OnWindowDestroyed(mus::Window* window) override { + void OnWindowDestroyed(ui::Window* window) override { DCHECK_EQ(mus_window(), window); platform_window_delegate()->OnClosed(); } - void OnWindowBoundsChanging(mus::Window* window, + void OnWindowBoundsChanging(ui::Window* window, const gfx::Rect& old_bounds, const gfx::Rect& new_bounds) override { DCHECK_EQ(window, mus_window()); window_tree_host()->SetBounds(new_bounds); } - void OnWindowFocusChanged(mus::Window* gained_focus, - mus::Window* lost_focus) override { + void OnWindowFocusChanged(ui::Window* gained_focus, + ui::Window* lost_focus) override { if (gained_focus == mus_window()) platform_window_delegate()->OnActivationChanged(true); else if (lost_focus == mus_window()) platform_window_delegate()->OnActivationChanged(false); } - void OnRequestClose(mus::Window* window) override { + void OnRequestClose(ui::Window* window) override { platform_window_delegate()->OnCloseRequest(); } private: - mus::Window* mus_window() { return native_widget_mus_->window(); } + ui::Window* mus_window() { return native_widget_mus_->window(); } WindowTreeHostMus* window_tree_host() { return native_widget_mus_->window_tree_host(); } @@ -459,7 +486,7 @@ class NativeWidgetMus::MusWindowObserver : public mus::WindowObserver { } NativeWidgetMus* native_widget_mus_; - mus::mojom::ShowState show_state_; + ui::mojom::ShowState show_state_; DISALLOW_COPY_AND_ASSIGN(MusWindowObserver); }; @@ -467,10 +494,12 @@ class NativeWidgetMus::MusWindowObserver : public mus::WindowObserver { class NativeWidgetMus::MusCaptureClient : public aura::client::DefaultCaptureClient { public: - MusCaptureClient(aura::Window* root_window, aura::Window* aura_window, - mus::Window* mus_window) + MusCaptureClient(aura::Window* root_window, + aura::Window* aura_window, + ui::Window* mus_window) : aura::client::DefaultCaptureClient(root_window), - aura_window_(aura_window), mus_window_(mus_window) {} + aura_window_(aura_window), + mus_window_(mus_window) {} ~MusCaptureClient() override {} // aura::client::DefaultCaptureClient: @@ -487,7 +516,7 @@ class NativeWidgetMus::MusCaptureClient private: aura::Window* aura_window_; - mus::Window* mus_window_; + ui::Window* mus_window_; DISALLOW_COPY_AND_ASSIGN(MusCaptureClient); }; @@ -496,52 +525,24 @@ class NativeWidgetMus::MusCaptureClient // NativeWidgetMus, public: NativeWidgetMus::NativeWidgetMus(internal::NativeWidgetDelegate* delegate, - shell::Connector* connector, - mus::Window* window, - mus::mojom::SurfaceType surface_type) + ui::Window* window, + ui::mojom::SurfaceType surface_type) : window_(window), - last_cursor_(mus::mojom::Cursor::CURSOR_NULL), + last_cursor_(ui::mojom::Cursor::CURSOR_NULL), native_widget_delegate_(delegate), surface_type_(surface_type), - show_state_before_fullscreen_(mus::mojom::ShowState::DEFAULT), + show_state_before_fullscreen_(ui::mojom::ShowState::DEFAULT), ownership_(Widget::InitParams::NATIVE_WIDGET_OWNS_WIDGET), content_(new aura::Window(this)), + last_drop_operation_(ui::DragDropTypes::DRAG_NONE), close_widget_factory_(this) { window_->set_input_event_handler(this); - mus_window_observer_.reset(new MusWindowObserver(this)); + mus_window_observer_ = base::MakeUnique<MusWindowObserver>(this); // TODO(fsamuel): Figure out lifetime of |window_|. aura::SetMusWindow(content_, window_); window->SetLocalProperty(kNativeWidgetMusKey, this); - - // WindowTreeHost creates the compositor using the ContextFactory from - // aura::Env. Install |context_factory_| there so that |context_factory_| is - // picked up. - ui::ContextFactory* default_context_factory = - aura::Env::GetInstance()->context_factory(); - // For Chrome, we need the GpuProcessTransportFactory so that renderer and - // browser pixels are composited into a single backing SoftwareOutputDeviceMus - // (which also requires the BitmapUploader). - bool needs_bitmap_uploader = false; - if (!default_context_factory) { - context_factory_.reset( - new SurfaceContextFactory(connector, window_, surface_type_)); - aura::Env::GetInstance()->set_context_factory(context_factory_.get()); - } else { - needs_bitmap_uploader = true; - } - - window_tree_host_.reset(new WindowTreeHostMus(this, window_)); - if (needs_bitmap_uploader) { - bitmap_uploader_.reset(new bitmap_uploader::BitmapUploader(window)); - bitmap_uploader_->Init(connector); - prop_.reset( - new ui::ViewProp(window_tree_host_->GetAcceleratedWidget(), - bitmap_uploader::kBitmapUploaderForAcceleratedWidget, - bitmap_uploader_.get())); - } - - aura::Env::GetInstance()->set_context_factory(default_context_factory); + window_tree_host_ = base::MakeUnique<WindowTreeHostMus>(this, window_); } NativeWidgetMus::~NativeWidgetMus() { @@ -556,8 +557,8 @@ NativeWidgetMus::~NativeWidgetMus() { } // static -void NativeWidgetMus::NotifyFrameChanged(mus::WindowTreeClient* client) { - for (mus::Window* window : client->GetRoots()) { +void NativeWidgetMus::NotifyFrameChanged(ui::WindowTreeClient* client) { + for (ui::Window* window : client->GetRoots()) { NativeWidgetMus* native_widget = window->GetLocalProperty(kNativeWidgetMusKey); if (native_widget && native_widget->GetWidget()->non_client_view()) { @@ -570,11 +571,16 @@ void NativeWidgetMus::NotifyFrameChanged(mus::WindowTreeClient* client) { } // static -Widget* NativeWidgetMus::GetWidgetForWindow(mus::Window* window) { - if (!window) - return nullptr; +NativeWidgetMus* NativeWidgetMus::GetForWindow(ui::Window* window) { + DCHECK(window); NativeWidgetMus* native_widget = window->GetLocalProperty(kNativeWidgetMusKey); + return native_widget; +} + +// static +Widget* NativeWidgetMus::GetWidgetForWindow(ui::Window* window) { + NativeWidgetMus* native_widget = GetForWindow(window); if (!native_widget) return nullptr; return native_widget->GetWidget(); @@ -619,6 +625,9 @@ void NativeWidgetMus::OnActivationChanged(bool active) { } void NativeWidgetMus::UpdateClientArea() { + if (is_parallel_widget_in_window_manager()) + return; + NonClientView* non_client_view = native_widget_delegate_->AsWidget()->non_client_view(); if (!non_client_view || !non_client_view->client_view()) @@ -641,28 +650,32 @@ void NativeWidgetMus::ConfigurePropertiesForNewWindow( properties->insert(init_params.mus_properties.begin(), init_params.mus_properties.end()); if (!init_params.bounds.IsEmpty()) { - (*properties)[mus::mojom::WindowManager::kUserSetBounds_Property] = + (*properties)[ui::mojom::WindowManager::kUserSetBounds_Property] = mojo::ConvertTo<std::vector<uint8_t>>(init_params.bounds); } if (!init_params.name.empty()) { - (*properties)[mus::mojom::WindowManager::kName_Property] = + (*properties)[ui::mojom::WindowManager::kName_Property] = mojo::ConvertTo<std::vector<uint8_t>>(init_params.name); } - (*properties)[mus::mojom::WindowManager::kAlwaysOnTop_Property] = + (*properties)[ui::mojom::WindowManager::kAlwaysOnTop_Property] = mojo::ConvertTo<std::vector<uint8_t>>(init_params.keep_on_top); if (!Widget::RequiresNonClientView(init_params.type)) return; - (*properties)[mus::mojom::WindowManager::kWindowType_Property] = + (*properties)[ui::mojom::WindowManager::kWindowType_Property] = mojo::ConvertTo<std::vector<uint8_t>>(static_cast<int32_t>( - mojo::ConvertTo<mus::mojom::WindowType>(init_params.type))); - (*properties)[mus::mojom::WindowManager::kResizeBehavior_Property] = - mojo::ConvertTo<std::vector<uint8_t>>( - ResizeBehaviorFromDelegate(init_params.delegate)); + mojo::ConvertTo<ui::mojom::WindowType>(init_params.type))); + if (init_params.delegate && + properties->count(ui::mojom::WindowManager::kResizeBehavior_Property) == + 0) { + (*properties)[ui::mojom::WindowManager::kResizeBehavior_Property] = + mojo::ConvertTo<std::vector<uint8_t>>( + ResizeBehaviorFromDelegate(init_params.delegate)); + } SkBitmap app_icon = AppIconFromDelegate(init_params.delegate); if (!app_icon.isNull()) { - (*properties)[mus::mojom::WindowManager::kWindowAppIcon_Property] = + (*properties)[ui::mojom::WindowManager::kWindowAppIcon_Property] = mojo::ConvertTo<std::vector<uint8_t>>(app_icon); } } @@ -681,34 +694,51 @@ void NativeWidgetMus::InitNativeWidget(const Widget::InitParams& params) { ownership_ = params.ownership; window_->SetCanFocus(params.activatable == Widget::InitParams::ACTIVATABLE_YES); + window_->SetCanAcceptEvents(params.accept_events); window_tree_host_->AddObserver(this); window_tree_host_->InitHost(); hosted_window->SetProperty(kMusWindow, window_); - focus_client_.reset( - new FocusControllerMus(new FocusRulesImpl(hosted_window))); + // TODO(moshayedi): crbug.com/641039. Investigate whether there are any cases + // where we need input method but don't have the WindowManagerConnection here. + if (WindowManagerConnection::Exists()) { + window_tree_host_->InitInputMethod( + WindowManagerConnection::Get()->connector()); + } + + focus_client_ = + base::MakeUnique<FocusControllerMus>(new FocusRulesImpl(hosted_window)); aura::client::SetFocusClient(hosted_window, focus_client_.get()); aura::client::SetActivationClient(hosted_window, focus_client_.get()); - screen_position_client_.reset(new ScreenPositionClientMus(window_)); + screen_position_client_ = base::MakeUnique<ScreenPositionClientMus>(window_); aura::client::SetScreenPositionClient(hosted_window, screen_position_client_.get()); - // TODO(erg): Remove this check when ash/mus/frame/move_event_handler.cc's - // direct usage of mus::Window::SetPredefinedCursor() is switched to a + drag_drop_client_ = base::MakeUnique<DragDropClientMus>(window_); + aura::client::SetDragDropClient(hosted_window, drag_drop_client_.get()); + drop_target_ = base::MakeUnique<DropTargetMus>(content_); + window_->SetCanAcceptDrops(drop_target_.get()); + drop_helper_ = base::MakeUnique<DropHelper>(GetWidget()->GetRootView()); + aura::client::SetDragDropDelegate(content_, this); + + // TODO(erg): Remove this check when ash/mus/move_event_handler.cc's + // direct usage of ui::Window::SetPredefinedCursor() is switched to a // private method on WindowManagerClient. - if (surface_type_ == mus::mojom::SurfaceType::DEFAULT) { - cursor_manager_.reset(new wm::CursorManager( - base::WrapUnique(new NativeCursorManagerMus(window_)))); + if (!is_parallel_widget_in_window_manager()) { + cursor_manager_ = base::MakeUnique<wm::CursorManager>( + base::MakeUnique<NativeCursorManagerMus>(window_)); aura::client::SetCursorClient(hosted_window, cursor_manager_.get()); } - window_tree_client_.reset(new NativeWidgetMusWindowTreeClient(hosted_window)); + window_tree_client_ = + base::MakeUnique<NativeWidgetMusWindowTreeClient>(hosted_window); hosted_window->AddPreTargetHandler(focus_client_.get()); hosted_window->SetLayoutManager( new ContentWindowLayoutManager(hosted_window, content_)); - capture_client_.reset(new MusCaptureClient(hosted_window, content_, window_)); + capture_client_ = + base::MakeUnique<MusCaptureClient>(hosted_window, content_, window_); content_->SetType(ui::wm::WINDOW_TYPE_NORMAL); content_->Init(params.layer_type); @@ -716,12 +746,13 @@ void NativeWidgetMus::InitNativeWidget(const Widget::InitParams& params) { content_->Show(); content_->SetTransparent(true); content_->SetFillsBoundsCompletely(false); + content_->set_ignore_events(!params.accept_events); hosted_window->AddChild(content_); // Set-up transiency if appropriate. if (params.parent && !params.child) { aura::Window* parent_root = params.parent->GetRootWindow(); - mus::Window* parent_mus = parent_root->GetProperty(kMusWindow); + ui::Window* parent_mus = parent_root->GetProperty(kMusWindow); if (parent_mus) parent_mus->AddTransientWindow(window_); } @@ -742,23 +773,23 @@ void NativeWidgetMus::OnWidgetInitDone() { // The client area is calculated from the NonClientView. During // InitNativeWidget() the NonClientView has not been created. When this // function is called the NonClientView has been created, so that we can - // correctly calculate the client area and push it to the mus::Window. + // correctly calculate the client area and push it to the ui::Window. UpdateClientArea(); UpdateHitTestMask(); } bool NativeWidgetMus::ShouldUseNativeFrame() const { - // NOTIMPLEMENTED(); + NOTIMPLEMENTED(); return false; } bool NativeWidgetMus::ShouldWindowContentsBeTransparent() const { - // NOTIMPLEMENTED(); + NOTIMPLEMENTED(); return true; } void NativeWidgetMus::FrameTypeChanged() { - // NOTIMPLEMENTED(); + NOTIMPLEMENTED(); } Widget* NativeWidgetMus::GetWidget() { @@ -790,11 +821,11 @@ const ui::Layer* NativeWidgetMus::GetLayer() const { } void NativeWidgetMus::ReorderNativeViews() { - // NOTIMPLEMENTED(); + NOTIMPLEMENTED(); } void NativeWidgetMus::ViewRemoved(View* view) { - // NOTIMPLEMENTED(); + NOTIMPLEMENTED(); } // These methods are wrong in mojo. They're not usually used to associate @@ -804,14 +835,14 @@ void NativeWidgetMus::ViewRemoved(View* view) { // used safely in a world where we separate things with mojo. They should be // removed; not ported. void NativeWidgetMus::SetNativeWindowProperty(const char* name, void* value) { - // TODO(beng): push properties to mus::Window. - // NOTIMPLEMENTED(); + native_window_properties_[name] = value; } void* NativeWidgetMus::GetNativeWindowProperty(const char* name) const { - // TODO(beng): pull properties to mus::Window. - // NOTIMPLEMENTED(); - return nullptr; + auto it = native_window_properties_.find(name); + if (it == native_window_properties_.end()) + return nullptr; + return it->second; } TooltipManager* NativeWidgetMus::GetTooltipManager() const { @@ -838,9 +869,11 @@ ui::InputMethod* NativeWidgetMus::GetInputMethod() { } void NativeWidgetMus::CenterWindow(const gfx::Size& size) { + if (is_parallel_widget_in_window_manager()) + return; // TODO(beng): clear user-placed property and set preferred size property. window_->SetSharedProperty<gfx::Size>( - mus::mojom::WindowManager::kPreferredSize_Property, size); + ui::mojom::WindowManager::kPreferredSize_Property, size); gfx::Rect bounds = display::Screen::GetScreen() ->GetDisplayNearestWindow(content_) @@ -852,14 +885,14 @@ void NativeWidgetMus::CenterWindow(const gfx::Size& size) { void NativeWidgetMus::GetWindowPlacement( gfx::Rect* bounds, ui::WindowShowState* maximized) const { - // NOTIMPLEMENTED(); + NOTIMPLEMENTED(); } bool NativeWidgetMus::SetWindowTitle(const base::string16& title) { - if (!window_) + if (!window_ || is_parallel_widget_in_window_manager()) return false; const char* kWindowTitle_Property = - mus::mojom::WindowManager::kWindowTitle_Property; + ui::mojom::WindowManager::kWindowTitle_Property; const base::string16 current_title = window_->HasSharedProperty(kWindowTitle_Property) ? window_->GetSharedProperty<base::string16>(kWindowTitle_Property) @@ -872,8 +905,11 @@ bool NativeWidgetMus::SetWindowTitle(const base::string16& title) { void NativeWidgetMus::SetWindowIcons(const gfx::ImageSkia& window_icon, const gfx::ImageSkia& app_icon) { + if (is_parallel_widget_in_window_manager()) + return; + const char* const kWindowAppIcon_Property = - mus::mojom::WindowManager::kWindowAppIcon_Property; + ui::mojom::WindowManager::kWindowAppIcon_Property; if (!app_icon.isNull()) { // Send the app icon 1x bitmap to the window manager. @@ -892,7 +928,22 @@ void NativeWidgetMus::InitModalType(ui::ModalType modal_type) { } gfx::Rect NativeWidgetMus::GetWindowBoundsInScreen() const { - return window_ ? window_->GetBoundsInRoot() : gfx::Rect(); + if (!window_) + return gfx::Rect(); + + // Correct for the origin of the display. + const int64_t window_display_id = window_->GetRoot()->display_id(); + for (display::Display display : + display::Screen::GetScreen()->GetAllDisplays()) { + if (display.id() == window_display_id) { + gfx::Point display_origin = display.bounds().origin(); + gfx::Rect bounds_in_screen = window_->GetBoundsInRoot(); + bounds_in_screen.Offset(display_origin.x(), display_origin.y()); + return bounds_in_screen; + } + } + // Unknown display, assume primary display at 0,0. + return window_->GetBoundsInRoot(); } gfx::Rect NativeWidgetMus::GetClientAreaBoundsInScreen() const { @@ -908,7 +959,7 @@ gfx::Rect NativeWidgetMus::GetRestoredBounds() const { // not in either state. if (IsMinimized() || IsMaximized() || IsFullscreen()) { const char* kRestoreBounds_Property = - mus::mojom::WindowManager::kRestoreBounds_Property; + ui::mojom::WindowManager::kRestoreBounds_Property; if (window_->HasSharedProperty(kRestoreBounds_Property)) return window_->GetSharedProperty<gfx::Rect>(kRestoreBounds_Property); } @@ -919,17 +970,26 @@ std::string NativeWidgetMus::GetWorkspace() const { return std::string(); } -void NativeWidgetMus::SetBounds(const gfx::Rect& bounds) { +void NativeWidgetMus::SetBounds(const gfx::Rect& bounds_in_screen) { if (!(window_ && window_tree_host_)) return; - gfx::Size size(bounds.size()); + // TODO(jamescook): Needs something like aura::ScreenPositionClient so higher + // level code can move windows between displays. crbug.com/645291 + gfx::Point origin(bounds_in_screen.origin()); + const gfx::Point display_origin = display::Screen::GetScreen() + ->GetDisplayMatching(bounds_in_screen) + .bounds() + .origin(); + origin.Offset(-display_origin.x(), -display_origin.y()); + + gfx::Size size(bounds_in_screen.size()); const gfx::Size min_size = GetMinimumSize(); const gfx::Size max_size = GetMaximumSize(); if (!max_size.IsEmpty()) size.SetToMin(max_size); size.SetToMax(min_size); - window_->SetBounds(gfx::Rect(bounds.origin(), size)); + window_->SetBounds(gfx::Rect(origin, size)); // Observer on |window_tree_host_| expected to synchronously update bounds. DCHECK(window_->bounds() == window_tree_host_->GetBounds()); } @@ -943,19 +1003,15 @@ void NativeWidgetMus::SetSize(const gfx::Size& size) { } void NativeWidgetMus::StackAbove(gfx::NativeView native_view) { - // NOTIMPLEMENTED(); + NOTIMPLEMENTED(); } void NativeWidgetMus::StackAtTop() { - // NOTIMPLEMENTED(); -} - -void NativeWidgetMus::StackBelow(gfx::NativeView native_view) { - // NOTIMPLEMENTED(); + NOTIMPLEMENTED(); } -void NativeWidgetMus::SetShape(SkRegion* shape) { - // NOTIMPLEMENTED(); +void NativeWidgetMus::SetShape(std::unique_ptr<SkRegion> shape) { + NOTIMPLEMENTED(); } void NativeWidgetMus::Close() { @@ -981,28 +1037,52 @@ void NativeWidgetMus::Hide() { if (!(window_ && window_tree_host_)) return; - window_tree_host_->Hide(); + // NOTE: |window_tree_host_| and |window_| visibility is updated in + // OnMusWindowVisibilityChanged(). window_->SetVisible(false); - GetNativeWindow()->Hide(); } void NativeWidgetMus::ShowMaximizedWithBounds( const gfx::Rect& restored_bounds) { - // NOTIMPLEMENTED(); + if (!window_) + return; + + window_->SetSharedProperty<gfx::Rect>( + ui::mojom::WindowManager::kRestoreBounds_Property, restored_bounds); + ShowWithWindowState(ui::SHOW_STATE_MAXIMIZED); } void NativeWidgetMus::ShowWithWindowState(ui::WindowShowState state) { if (!(window_ && window_tree_host_)) return; - window_tree_host_->Show(); + // Matches NativeWidgetAura. + switch (state) { + case ui::SHOW_STATE_MAXIMIZED: + SetShowState(ui::mojom::ShowState::MAXIMIZED); + break; + case ui::SHOW_STATE_FULLSCREEN: + SetShowState(ui::mojom::ShowState::FULLSCREEN); + break; + case ui::SHOW_STATE_DOCKED: + SetShowState(ui::mojom::ShowState::DOCKED); + break; + default: + break; + } + + // NOTE: |window_tree_host_| and |window_| visibility is updated in + // OnMusWindowVisibilityChanged(). window_->SetVisible(true); - GetNativeWindow()->Show(); if (native_widget_delegate_->CanActivate()) { if (state != ui::SHOW_STATE_INACTIVE) Activate(); GetWidget()->SetInitialFocus(state); } + + // Matches NativeWidgetAura. + if (state == ui::SHOW_STATE_MINIMIZED) + Minimize(); } bool NativeWidgetMus::IsVisible() const { @@ -1024,71 +1104,73 @@ void NativeWidgetMus::Deactivate() { } bool NativeWidgetMus::IsActive() const { - mus::Window* focused = + ui::Window* focused = window_ ? window_->window_tree()->GetFocusedWindow() : nullptr; return focused && window_->Contains(focused); } void NativeWidgetMus::SetAlwaysOnTop(bool always_on_top) { - if (window_) { + if (window_ && !is_parallel_widget_in_window_manager()) { window_->SetSharedProperty<bool>( - mus::mojom::WindowManager::kAlwaysOnTop_Property, always_on_top); + ui::mojom::WindowManager::kAlwaysOnTop_Property, always_on_top); } } bool NativeWidgetMus::IsAlwaysOnTop() const { return window_ && window_->HasSharedProperty( - mus::mojom::WindowManager::kAlwaysOnTop_Property) && + ui::mojom::WindowManager::kAlwaysOnTop_Property) && window_->GetSharedProperty<bool>( - mus::mojom::WindowManager::kAlwaysOnTop_Property); + ui::mojom::WindowManager::kAlwaysOnTop_Property); } void NativeWidgetMus::SetVisibleOnAllWorkspaces(bool always_visible) { // Not needed for chromeos. } +bool NativeWidgetMus::IsVisibleOnAllWorkspaces() const { + return false; +} + void NativeWidgetMus::Maximize() { - SetShowState(mus::mojom::ShowState::MAXIMIZED); + SetShowState(ui::mojom::ShowState::MAXIMIZED); } void NativeWidgetMus::Minimize() { - SetShowState(mus::mojom::ShowState::MINIMIZED); + SetShowState(ui::mojom::ShowState::MINIMIZED); } bool NativeWidgetMus::IsMaximized() const { - return mus_window_observer_ && - mus_window_observer_->show_state() == mus::mojom::ShowState::MAXIMIZED; + return GetShowState(window_) == ui::mojom::ShowState::MAXIMIZED; } bool NativeWidgetMus::IsMinimized() const { - return mus_window_observer_ && - mus_window_observer_->show_state() == mus::mojom::ShowState::MINIMIZED; + return GetShowState(window_) == ui::mojom::ShowState::MINIMIZED; } void NativeWidgetMus::Restore() { - SetShowState(mus::mojom::ShowState::NORMAL); + SetShowState(ui::mojom::ShowState::NORMAL); } void NativeWidgetMus::SetFullscreen(bool fullscreen) { if (IsFullscreen() == fullscreen) return; if (fullscreen) { - show_state_before_fullscreen_ = mus_window_observer_->show_state(); - // TODO(markdittmer): Fullscreen not implemented in mus::Window. + show_state_before_fullscreen_ = GetShowState(window_); + SetShowState(ui::mojom::ShowState::FULLSCREEN); } else { switch (show_state_before_fullscreen_) { - case mus::mojom::ShowState::MAXIMIZED: + case ui::mojom::ShowState::MAXIMIZED: Maximize(); break; - case mus::mojom::ShowState::MINIMIZED: + case ui::mojom::ShowState::MINIMIZED: Minimize(); break; - case mus::mojom::ShowState::DEFAULT: - case mus::mojom::ShowState::NORMAL: - case mus::mojom::ShowState::INACTIVE: - case mus::mojom::ShowState::FULLSCREEN: - case mus::mojom::ShowState::DOCKED: + case ui::mojom::ShowState::DEFAULT: + case ui::mojom::ShowState::NORMAL: + case ui::mojom::ShowState::INACTIVE: + case ui::mojom::ShowState::FULLSCREEN: + case ui::mojom::ShowState::DOCKED: // TODO(sad): This may not be sufficient. Restore(); break; @@ -1097,8 +1179,7 @@ void NativeWidgetMus::SetFullscreen(bool fullscreen) { } bool NativeWidgetMus::IsFullscreen() const { - return mus_window_observer_ && - mus_window_observer_->show_state() == mus::mojom::ShowState::FULLSCREEN; + return GetShowState(window_) == ui::mojom::ShowState::FULLSCREEN; } void NativeWidgetMus::SetOpacity(float opacity) { @@ -1107,16 +1188,16 @@ void NativeWidgetMus::SetOpacity(float opacity) { } void NativeWidgetMus::FlashFrame(bool flash_frame) { - // NOTIMPLEMENTED(); + NOTIMPLEMENTED(); } -void NativeWidgetMus::RunShellDrag( - View* view, - const ui::OSExchangeData& data, - const gfx::Point& location, - int operation, - ui::DragDropTypes::DragEventSource source) { - // NOTIMPLEMENTED(); +void NativeWidgetMus::RunShellDrag(View* view, + const ui::OSExchangeData& data, + const gfx::Point& location, + int drag_operations, + ui::DragDropTypes::DragEventSource source) { + if (window_) + views::RunShellDrag(content_, data, location, drag_operations, source); } void NativeWidgetMus::SchedulePaintInRect(const gfx::Rect& rect) { @@ -1134,20 +1215,20 @@ void NativeWidgetMus::SetCursor(gfx::NativeCursor cursor) { // also send an image, but as the cursor code is currently written, the image // is in a platform native format that's already uploaded to the window // server. - mus::mojom::Cursor new_cursor = mus::mojom::Cursor(cursor.native_type()); + ui::mojom::Cursor new_cursor = ui::mojom::Cursor(cursor.native_type()); if (last_cursor_ != new_cursor) window_->SetPredefinedCursor(new_cursor); } bool NativeWidgetMus::IsMouseEventsEnabled() const { - // NOTIMPLEMENTED(); + NOTIMPLEMENTED(); return true; } void NativeWidgetMus::ClearNativeFocus() { if (!IsActive()) return; - mus::Window* focused = + ui::Window* focused = window_ ? window_->window_tree()->GetFocusedWindow() : nullptr; if (focused && window_->Contains(focused) && focused != window_) window_->SetFocus(); @@ -1158,7 +1239,7 @@ void NativeWidgetMus::ClearNativeFocus() { } gfx::Rect NativeWidgetMus::GetWorkAreaBoundsInScreen() const { - // NOTIMPLEMENTED(); + NOTIMPLEMENTED(); return gfx::Rect(); } @@ -1166,26 +1247,45 @@ Widget::MoveLoopResult NativeWidgetMus::RunMoveLoop( const gfx::Vector2d& drag_offset, Widget::MoveLoopSource source, Widget::MoveLoopEscapeBehavior escape_behavior) { - // NOTIMPLEMENTED(); - return Widget::MOVE_LOOP_CANCELED; + ReleaseCapture(); + + base::MessageLoopForUI* loop = base::MessageLoopForUI::current(); + base::MessageLoop::ScopedNestableTaskAllower allow_nested(loop); + base::RunLoop run_loop; + + ui::mojom::MoveLoopSource mus_source = + source == Widget::MOVE_LOOP_SOURCE_MOUSE + ? ui::mojom::MoveLoopSource::MOUSE + : ui::mojom::MoveLoopSource::TOUCH; + + bool success = false; + gfx::Point cursor_location = + display::Screen::GetScreen()->GetCursorScreenPoint(); + window_->PerformWindowMove( + mus_source, cursor_location, + base::Bind(OnMoveLoopEnd, &success, run_loop.QuitClosure())); + + run_loop.Run(); + + return success ? Widget::MOVE_LOOP_SUCCESSFUL : Widget::MOVE_LOOP_CANCELED; } void NativeWidgetMus::EndMoveLoop() { - // NOTIMPLEMENTED(); + window_->CancelWindowMove(); } void NativeWidgetMus::SetVisibilityChangedAnimationsEnabled(bool value) { - // NOTIMPLEMENTED(); + NOTIMPLEMENTED(); } void NativeWidgetMus::SetVisibilityAnimationDuration( const base::TimeDelta& duration) { - // NOTIMPLEMENTED(); + NOTIMPLEMENTED(); } void NativeWidgetMus::SetVisibilityAnimationTransition( Widget::VisibilityTransition transition) { - // NOTIMPLEMENTED(); + NOTIMPLEMENTED(); } ui::NativeTheme* NativeWidgetMus::GetNativeTheme() const { @@ -1193,25 +1293,25 @@ ui::NativeTheme* NativeWidgetMus::GetNativeTheme() const { } void NativeWidgetMus::OnRootViewLayout() { - // NOTIMPLEMENTED(); + NOTIMPLEMENTED(); } bool NativeWidgetMus::IsTranslucentWindowOpacitySupported() const { - // NOTIMPLEMENTED(); + NOTIMPLEMENTED(); return true; } void NativeWidgetMus::OnSizeConstraintsChanged() { - if (!window_) + if (!window_ || is_parallel_widget_in_window_manager()) return; window_->SetSharedProperty<int32_t>( - mus::mojom::WindowManager::kResizeBehavior_Property, + ui::mojom::WindowManager::kResizeBehavior_Property, ResizeBehaviorFromDelegate(GetWidget()->widget_delegate())); } void NativeWidgetMus::RepostNativeEvent(gfx::NativeEvent native_event) { - // NOTIMPLEMENTED(); + NOTIMPLEMENTED(); } std::string NativeWidgetMus::GetName() const { @@ -1231,18 +1331,8 @@ gfx::Size NativeWidgetMus::GetMaximumSize() const { void NativeWidgetMus::OnBoundsChanged(const gfx::Rect& old_bounds, const gfx::Rect& new_bounds) { - // Assume that if the old bounds was completely empty a move happened. This - // handles the case of a maximize animation acquiring the layer (acquiring a - // layer results in clearing the bounds). - if (old_bounds.origin() != new_bounds.origin() || - (old_bounds == gfx::Rect(0, 0, 0, 0) && !new_bounds.IsEmpty())) { - native_widget_delegate_->OnNativeWidgetMove(); - } - if (old_bounds.size() != new_bounds.size()) { - native_widget_delegate_->OnNativeWidgetSizeChanged(new_bounds.size()); - UpdateClientArea(); - UpdateHitTestMask(); - } + // This is handled in OnHost{Resized,Moved}() like DesktopNativeWidgetAura + // instead of here like in NativeWidgetAura. } gfx::NativeCursor NativeWidgetMus::GetCursor(const gfx::Point& point) { @@ -1294,11 +1384,11 @@ void NativeWidgetMus::GetHitTestMask(gfx::Path* mask) const { native_widget_delegate_->GetHitTestMask(mask); } -void NativeWidgetMus::SetShowState(mus::mojom::ShowState show_state) { +void NativeWidgetMus::SetShowState(ui::mojom::ShowState show_state) { if (!window_) return; window_->SetSharedProperty<int32_t>( - mus::mojom::WindowManager::kShowState_Property, + ui::mojom::WindowManager::kShowState_Property, static_cast<int32_t>(show_state)); } @@ -1345,12 +1435,54 @@ void NativeWidgetMus::OnGestureEvent(ui::GestureEvent* event) { native_widget_delegate_->OnGestureEvent(event); } +void NativeWidgetMus::OnHostResized(const aura::WindowTreeHost* host) { + native_widget_delegate_->OnNativeWidgetSizeChanged( + host->window()->bounds().size()); + UpdateClientArea(); + UpdateHitTestMask(); +} + +void NativeWidgetMus::OnHostMoved(const aura::WindowTreeHost* host, + const gfx::Point& new_origin) { + native_widget_delegate_->OnNativeWidgetMove(); +} + void NativeWidgetMus::OnHostCloseRequested(const aura::WindowTreeHost* host) { GetWidget()->Close(); } +//////////////////////////////////////////////////////////////////////////////// +// NativeWidgetMus, aura::WindowDragDropDelegate implementation: + +void NativeWidgetMus::OnDragEntered(const ui::DropTargetEvent& event) { + DCHECK(drop_helper_); + last_drop_operation_ = drop_helper_->OnDragOver( + event.data(), event.location(), event.source_operations()); +} + +int NativeWidgetMus::OnDragUpdated(const ui::DropTargetEvent& event) { + DCHECK(drop_helper_); + last_drop_operation_ = drop_helper_->OnDragOver( + event.data(), event.location(), event.source_operations()); + return last_drop_operation_; +} + +void NativeWidgetMus::OnDragExited() { + DCHECK(drop_helper_); + drop_helper_->OnDragExit(); +} + +int NativeWidgetMus::OnPerformDrop(const ui::DropTargetEvent& event) { + DCHECK(drop_helper_); + return drop_helper_->OnDrop(event.data(), event.location(), + last_drop_operation_); +} + +//////////////////////////////////////////////////////////////////////////////// +// NativeWidgetMus, ui::InputEventHandler implementation: + void NativeWidgetMus::OnWindowInputEvent( - mus::Window* view, + ui::Window* view, const ui::Event& event_in, std::unique_ptr<base::Callback<void(EventResult)>>* ack_callback) { // Take ownership of the callback, indicating that we will handle it. @@ -1366,31 +1498,31 @@ void NativeWidgetMus::OnWindowInputEvent( // |ack_handler| acks the event on destruction if necessary. } -void NativeWidgetMus::OnMusWindowVisibilityChanging(mus::Window* window) { - if (window == window_) { - native_widget_delegate_->OnNativeWidgetVisibilityChanging( - !window->visible()); - } +void NativeWidgetMus::OnMusWindowVisibilityChanging(ui::Window* window, + bool visible) { + if (window == window_) + native_widget_delegate_->OnNativeWidgetVisibilityChanging(visible); } -void NativeWidgetMus::OnMusWindowVisibilityChanged(mus::Window* window) { +void NativeWidgetMus::OnMusWindowVisibilityChanged(ui::Window* window, + bool visible) { if (window != window_) return; - if (window->visible()) { + if (visible) { window_tree_host_->Show(); GetNativeWindow()->Show(); } else { window_tree_host_->Hide(); GetNativeWindow()->Hide(); } - native_widget_delegate_->OnNativeWidgetVisibilityChanged(window->visible()); + native_widget_delegate_->OnNativeWidgetVisibilityChanged(visible); } void NativeWidgetMus::UpdateHitTestMask() { // The window manager (or other underlay window provider) is not allowed to // set a hit test mask, as that could interfere with a client app mask. - if (surface_type_ == mus::mojom::SurfaceType::UNDERLAY) + if (is_parallel_widget_in_window_manager()) return; if (!native_widget_delegate_->HasHitTestMask()) { diff --git a/chromium/ui/views/mus/native_widget_mus.h b/chromium/ui/views/mus/native_widget_mus.h index 33da5378358..3e7f7412ced 100644 --- a/chromium/ui/views/mus/native_widget_mus.h +++ b/chromium/ui/views/mus/native_widget_mus.h @@ -10,33 +10,32 @@ #include <map> #include <memory> #include <string> +#include <vector> #include "base/callback.h" #include "base/macros.h" #include "base/memory/weak_ptr.h" -#include "components/mus/public/cpp/input_event_handler.h" -#include "components/mus/public/interfaces/window_tree.mojom.h" +#include "services/ui/public/cpp/input_event_handler.h" +#include "services/ui/public/interfaces/window_tree.mojom.h" #include "ui/aura/window_delegate.h" #include "ui/aura/window_tree_host_observer.h" #include "ui/platform_window/platform_window_delegate.h" #include "ui/views/mus/mus_export.h" #include "ui/views/mus/window_tree_host_mus.h" #include "ui/views/widget/native_widget_private.h" +#include "ui/wm/public/drag_drop_delegate.h" namespace aura { namespace client { class DefaultCaptureClient; +class DragDropClient; class ScreenPositionClient; class WindowTreeClient; } class Window; } -namespace bitmap_uploader { -class BitmapUploader; -} - -namespace mus { +namespace ui { class Window; class WindowTreeClient; namespace mojom { @@ -45,13 +44,8 @@ enum class EventResult; } } -namespace shell { -class Connector; -} - namespace ui { class Event; -class ViewProp; } namespace wm { @@ -60,12 +54,13 @@ class FocusController; } namespace views { -class SurfaceContextFactory; +class DropHelper; +class DropTargetMus; class WidgetDelegate; -// An implementation of NativeWidget that binds to a mus::Window. Because Aura +// An implementation of NativeWidget that binds to a ui::Window. Because Aura // is used extensively within Views code, this code uses aura and binds to the -// mus::Window via a Mus-specific aura::WindowTreeHost impl. Because the root +// ui::Window via a Mus-specific aura::WindowTreeHost impl. Because the root // aura::Window in a hierarchy is created without a delegate by the // aura::WindowTreeHost, we must create a child aura::Window in this class // (content_) and attach it to the root. @@ -73,12 +68,12 @@ class VIEWS_MUS_EXPORT NativeWidgetMus : public internal::NativeWidgetPrivate, public aura::WindowDelegate, public aura::WindowTreeHostObserver, - public NON_EXPORTED_BASE(mus::InputEventHandler) { + public aura::client::DragDropDelegate, + public NON_EXPORTED_BASE(ui::InputEventHandler) { public: NativeWidgetMus(internal::NativeWidgetDelegate* delegate, - shell::Connector* connector, - mus::Window* window, - mus::mojom::SurfaceType surface_type); + ui::Window* window, + ui::mojom::SurfaceType surface_type); ~NativeWidgetMus() override; // Configures the set of properties supplied to the window manager when @@ -88,12 +83,16 @@ class VIEWS_MUS_EXPORT NativeWidgetMus std::map<std::string, std::vector<uint8_t>>* properties); // Notifies all widgets the frame constants changed in some way. - static void NotifyFrameChanged(mus::WindowTreeClient* client); + static void NotifyFrameChanged(ui::WindowTreeClient* client); + + // Returns the native widget for a ui::Window, or null if there is none. + static NativeWidgetMus* GetForWindow(ui::Window* window); - // Returns the widget for a mus::Window, or null if there is none. - static Widget* GetWidgetForWindow(mus::Window* window); + // Returns the widget for a ui::Window, or null if there is none. + static Widget* GetWidgetForWindow(ui::Window* window); - mus::Window* window() { return window_; } + ui::mojom::SurfaceType surface_type() const { return surface_type_; } + ui::Window* window() { return window_; } WindowTreeHostMus* window_tree_host() { return window_tree_host_.get(); } aura::Window* GetRootWindow(); @@ -102,7 +101,7 @@ class VIEWS_MUS_EXPORT NativeWidgetMus void OnActivationChanged(bool active); protected: - // Updates the client area in the mus::Window. + // Updates the client area in the ui::Window. virtual void UpdateClientArea(); // internal::NativeWidgetPrivate: @@ -139,12 +138,11 @@ class VIEWS_MUS_EXPORT NativeWidgetMus gfx::Rect GetClientAreaBoundsInScreen() const override; gfx::Rect GetRestoredBounds() const override; std::string GetWorkspace() const override; - void SetBounds(const gfx::Rect& bounds) override; + void SetBounds(const gfx::Rect& bounds_in_screen) override; void SetSize(const gfx::Size& size) override; void StackAbove(gfx::NativeView native_view) override; void StackAtTop() override; - void StackBelow(gfx::NativeView native_view) override; - void SetShape(SkRegion* shape) override; + void SetShape(std::unique_ptr<SkRegion> shape) override; void Close() override; void CloseNow() override; void Show() override; @@ -158,6 +156,7 @@ class VIEWS_MUS_EXPORT NativeWidgetMus void SetAlwaysOnTop(bool always_on_top) override; bool IsAlwaysOnTop() const override; void SetVisibleOnAllWorkspaces(bool always_visible) override; + bool IsVisibleOnAllWorkspaces() const override; void Maximize() override; void Minimize() override; bool IsMaximized() const override; @@ -170,7 +169,7 @@ class VIEWS_MUS_EXPORT NativeWidgetMus void RunShellDrag(View* view, const ui::OSExchangeData& data, const gfx::Point& location, - int operation, + int drag_operations, ui::DragDropTypes::DragEventSource source) override; void SchedulePaintInRect(const gfx::Rect& rect) override; void SetCursor(gfx::NativeCursor cursor) override; @@ -220,16 +219,25 @@ class VIEWS_MUS_EXPORT NativeWidgetMus void OnGestureEvent(ui::GestureEvent* event) override; // Overridden from aura::WindowTreeHostObserver: + void OnHostResized(const aura::WindowTreeHost* host) override; + void OnHostMoved(const aura::WindowTreeHost* host, + const gfx::Point& new_origin) override; void OnHostCloseRequested(const aura::WindowTreeHost* host) override; - // Overridden from mus::InputEventHandler: + // Overridden from aura::client::DragDropDelegate: + void OnDragEntered(const ui::DropTargetEvent& event) override; + int OnDragUpdated(const ui::DropTargetEvent& event) override; + void OnDragExited() override; + int OnPerformDrop(const ui::DropTargetEvent& event) override; + + // Overridden from ui::InputEventHandler: void OnWindowInputEvent( - mus::Window* view, + ui::Window* view, const ui::Event& event, - std::unique_ptr<base::Callback<void(mus::mojom::EventResult)>>* + std::unique_ptr<base::Callback<void(ui::mojom::EventResult)>>* ack_callback) override; -private: + private: friend class NativeWidgetMusTest; class MusWindowObserver; class MusCaptureClient; @@ -238,45 +246,58 @@ private: return window_tree_host(); } - void set_last_cursor(mus::mojom::Cursor cursor) { last_cursor_ = cursor; } - void SetShowState(mus::mojom::ShowState show_state); + // Returns true if this NativeWidgetMus exists on the window manager side + // to provide the frame decorations. + bool is_parallel_widget_in_window_manager() { + return surface_type_ == ui::mojom::SurfaceType::UNDERLAY; + } + + void set_last_cursor(ui::mojom::Cursor cursor) { last_cursor_ = cursor; } + void SetShowState(ui::mojom::ShowState show_state); - void OnMusWindowVisibilityChanging(mus::Window* window); - void OnMusWindowVisibilityChanged(mus::Window* window); + void OnMusWindowVisibilityChanging(ui::Window* window, bool visible); + void OnMusWindowVisibilityChanged(ui::Window* window, bool visible); - // Propagates the widget hit test mask, if any, to the mus::Window. + // Propagates the widget hit test mask, if any, to the ui::Window. // TODO(jamescook): Wire this through views::Widget so widgets can push // updates if needed. void UpdateHitTestMask(); - mus::Window* window_; - mus::mojom::Cursor last_cursor_; + ui::Window* window_; + ui::mojom::Cursor last_cursor_; internal::NativeWidgetDelegate* native_widget_delegate_; - const mus::mojom::SurfaceType surface_type_; - mus::mojom::ShowState show_state_before_fullscreen_; + const ui::mojom::SurfaceType surface_type_; + ui::mojom::ShowState show_state_before_fullscreen_; // See class documentation for Widget in widget.h for a note about ownership. Widget::InitParams::Ownership ownership_; - // Functions with the same name require the mus::WindowObserver to be in + // Functions with the same name require the ui::WindowObserver to be in // a separate class. std::unique_ptr<MusWindowObserver> mus_window_observer_; + // This is misnamed; The native widget interface offers something called + // "native window properties" which are properties which it stores locally, + // and this is used to unsafely pass void* pointers around chrome. + std::map<std::string, void*> native_window_properties_; + + // Receives drop events for |window_|. + std::unique_ptr<DropTargetMus> drop_target_; + // Aura configuration. - std::unique_ptr<SurfaceContextFactory> context_factory_; std::unique_ptr<WindowTreeHostMus> window_tree_host_; aura::Window* content_; std::unique_ptr<wm::FocusController> focus_client_; std::unique_ptr<MusCaptureClient> capture_client_; + std::unique_ptr<aura::client::DragDropClient> drag_drop_client_; std::unique_ptr<aura::client::WindowTreeClient> window_tree_client_; std::unique_ptr<aura::client::ScreenPositionClient> screen_position_client_; std::unique_ptr<wm::CursorManager> cursor_manager_; - // Bitmap management. - std::unique_ptr<bitmap_uploader::BitmapUploader> bitmap_uploader_; - std::unique_ptr<ui::ViewProp> prop_; + std::unique_ptr<DropHelper> drop_helper_; + int last_drop_operation_; base::WeakPtrFactory<NativeWidgetMus> close_widget_factory_; diff --git a/chromium/ui/views/mus/native_widget_mus_unittest.cc b/chromium/ui/views/mus/native_widget_mus_unittest.cc index 6bad3b8f876..559a1f24191 100644 --- a/chromium/ui/views/mus/native_widget_mus_unittest.cc +++ b/chromium/ui/views/mus/native_widget_mus_unittest.cc @@ -6,13 +6,14 @@ #include "base/callback.h" #include "base/macros.h" -#include "components/mus/public/cpp/property_type_converters.h" -#include "components/mus/public/cpp/tests/window_tree_client_private.h" -#include "components/mus/public/cpp/window.h" -#include "components/mus/public/cpp/window_property.h" -#include "components/mus/public/cpp/window_tree_client.h" -#include "components/mus/public/interfaces/window_manager.mojom.h" -#include "components/mus/public/interfaces/window_tree.mojom.h" +#include "services/ui/public/cpp/property_type_converters.h" +#include "services/ui/public/cpp/tests/window_tree_client_private.h" +#include "services/ui/public/cpp/window.h" +#include "services/ui/public/cpp/window_observer.h" +#include "services/ui/public/cpp/window_property.h" +#include "services/ui/public/cpp/window_tree_client.h" +#include "services/ui/public/interfaces/window_manager.mojom.h" +#include "services/ui/public/interfaces/window_tree.mojom.h" #include "testing/gtest/include/gtest/gtest.h" #include "third_party/skia/include/core/SkBitmap.h" #include "third_party/skia/include/core/SkColor.h" @@ -24,6 +25,7 @@ #include "ui/gfx/path.h" #include "ui/gfx/skia_util.h" #include "ui/views/controls/native/native_view_host.h" +#include "ui/views/mus/window_manager_connection.h" #include "ui/views/test/focus_manager_test.h" #include "ui/views/test/views_test_base.h" #include "ui/views/widget/widget.h" @@ -31,7 +33,7 @@ #include "ui/views/widget/widget_observer.h" #include "ui/wm/public/activation_client.h" -using mus::mojom::EventResult; +using ui::mojom::EventResult; namespace views { namespace { @@ -161,26 +163,26 @@ class NativeWidgetMusTest : public ViewsTestBase { int ack_callback_count() { return ack_callback_count_; } - void AckCallback(mus::mojom::EventResult result) { + void AckCallback(ui::mojom::EventResult result) { ack_callback_count_++; - EXPECT_EQ(mus::mojom::EventResult::HANDLED, result); + EXPECT_EQ(ui::mojom::EventResult::HANDLED, result); } // Returns a mouse pressed event inside the widget. Tests that place views // within the widget that respond to the event must be constructed within the // widget coordinate space such that they respond correctly. std::unique_ptr<ui::MouseEvent> CreateMouseEvent() { - return base::WrapUnique(new ui::MouseEvent( + return base::MakeUnique<ui::MouseEvent>( ui::ET_MOUSE_PRESSED, gfx::Point(50, 50), gfx::Point(50, 50), - base::TimeTicks(), ui::EF_LEFT_MOUSE_BUTTON, ui::EF_LEFT_MOUSE_BUTTON)); + base::TimeTicks(), ui::EF_LEFT_MOUSE_BUTTON, ui::EF_LEFT_MOUSE_BUTTON); } // Simulates an input event to the NativeWidget. void OnWindowInputEvent( NativeWidgetMus* native_widget, const ui::Event& event, - std::unique_ptr<base::Callback<void(mus::mojom::EventResult)>>* - ack_callback) { + std::unique_ptr<base::Callback<void(ui::mojom::EventResult)>>* + ack_callback) { native_widget->OnWindowInputEvent(native_widget->window(), event, ack_callback); } @@ -246,39 +248,39 @@ TEST_F(NativeWidgetMusTest, ShowNonActivatableWidget) { EXPECT_EQ(0u, activation_observer.changes().size()); } -// Tests that a window with an icon sets the mus::Window icon property. +// Tests that a window with an icon sets the ui::Window icon property. TEST_F(NativeWidgetMusTest, AppIcon) { // Create a Widget with a bitmap as the icon. SkBitmap source_bitmap = MakeBitmap(SK_ColorRED); std::unique_ptr<Widget> widget( CreateWidget(new TestWidgetDelegate(source_bitmap))); - // The mus::Window has the icon property. - mus::Window* window = + // The ui::Window has the icon property. + ui::Window* window = static_cast<NativeWidgetMus*>(widget->native_widget_private())->window(); EXPECT_TRUE(window->HasSharedProperty( - mus::mojom::WindowManager::kWindowAppIcon_Property)); + ui::mojom::WindowManager::kWindowAppIcon_Property)); // The icon is the expected icon. SkBitmap icon = window->GetSharedProperty<SkBitmap>( - mus::mojom::WindowManager::kWindowAppIcon_Property); + ui::mojom::WindowManager::kWindowAppIcon_Property); EXPECT_TRUE(gfx::BitmapsAreEqual(source_bitmap, icon)); } -// Tests that a window without an icon does not set the mus::Window icon +// Tests that a window without an icon does not set the ui::Window icon // property. TEST_F(NativeWidgetMusTest, NoAppIcon) { // Create a Widget without a special icon. std::unique_ptr<Widget> widget(CreateWidget(nullptr)); - // The mus::Window does not have an icon property. - mus::Window* window = + // The ui::Window does not have an icon property. + ui::Window* window = static_cast<NativeWidgetMus*>(widget->native_widget_private())->window(); EXPECT_FALSE(window->HasSharedProperty( - mus::mojom::WindowManager::kWindowAppIcon_Property)); + ui::mojom::WindowManager::kWindowAppIcon_Property)); } -// Tests that changing the icon on a Widget updates the mus::Window icon +// Tests that changing the icon on a Widget updates the ui::Window icon // property. TEST_F(NativeWidgetMusTest, ChangeAppIcon) { // Create a Widget with an icon. @@ -292,10 +294,10 @@ TEST_F(NativeWidgetMusTest, ChangeAppIcon) { widget->UpdateWindowIcon(); // The window has the updated icon. - mus::Window* window = + ui::Window* window = static_cast<NativeWidgetMus*>(widget->native_widget_private())->window(); SkBitmap icon = window->GetSharedProperty<SkBitmap>( - mus::mojom::WindowManager::kWindowAppIcon_Property); + ui::mojom::WindowManager::kWindowAppIcon_Property); EXPECT_TRUE(gfx::BitmapsAreEqual(bitmap2, icon)); } @@ -308,33 +310,33 @@ TEST_F(NativeWidgetMusTest, ValidLayerTree) { } // Tests that the internal name is propagated from the Widget to the -// mus::Window. +// ui::Window. TEST_F(NativeWidgetMusTest, GetName) { Widget widget; Widget::InitParams params = CreateParams(Widget::InitParams::TYPE_WINDOW); params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; params.name = "MyWidget"; widget.Init(params); - mus::Window* window = + ui::Window* window = static_cast<NativeWidgetMus*>(widget.native_widget_private())->window(); EXPECT_EQ("MyWidget", window->GetName()); } // Tests that a Widget with a hit test mask propagates the mask to the -// mus::Window. +// ui::Window. TEST_F(NativeWidgetMusTest, HitTestMask) { gfx::Rect mask(5, 5, 10, 10); std::unique_ptr<Widget> widget( CreateWidget(new WidgetDelegateWithHitTestMask(mask))); // The window has the mask. - mus::Window* window = + ui::Window* window = static_cast<NativeWidgetMus*>(widget->native_widget_private())->window(); ASSERT_TRUE(window->hit_test_mask()); EXPECT_EQ(mask.ToString(), window->hit_test_mask()->ToString()); } -// Verifies changing the visibility of a child mus::Window doesn't change the +// Verifies changing the visibility of a child ui::Window doesn't change the // visibility of the parent. TEST_F(NativeWidgetMusTest, ChildVisibilityDoesntEffectParent) { Widget widget; @@ -342,13 +344,13 @@ TEST_F(NativeWidgetMusTest, ChildVisibilityDoesntEffectParent) { params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; widget.Init(params); widget.Show(); - mus::Window* window = + ui::Window* window = static_cast<NativeWidgetMus*>(widget.native_widget_private())->window(); ASSERT_TRUE(window->visible()); // Create a child window, make it visible and parent it to the Widget's // window. - mus::Window* child_window = window->window_tree()->NewWindow(); + ui::Window* child_window = window->window_tree()->NewWindow(); child_window->SetVisible(true); window->AddChild(child_window); @@ -416,7 +418,7 @@ TEST_F(NativeWidgetMusTest, WidgetReceivesEvent) { std::unique_ptr<ui::MouseEvent> mouse = CreateMouseEvent(); NativeWidgetMus* native_widget = static_cast<NativeWidgetMus*>(widget->native_widget_private()); - mus::WindowTreeClientPrivate test_api(native_widget->window()); + ui::WindowTreeClientPrivate test_api(native_widget->window()); test_api.CallOnWindowInputEvent(native_widget->window(), std::move(mouse)); EXPECT_EQ(1, handler.num_mouse_events()); } @@ -480,7 +482,7 @@ TEST_F(NativeWidgetMusTest, SetAndReleaseCapture) { widget->GetContentsView()->AddChildView(content); internal::NativeWidgetPrivate* widget_private = widget->native_widget_private(); - mus::Window* mus_window = + ui::Window* mus_window = static_cast<NativeWidgetMus*>(widget_private)->window(); EXPECT_FALSE(widget_private->HasCapture()); EXPECT_FALSE(mus_window->HasCapture()); @@ -494,7 +496,7 @@ TEST_F(NativeWidgetMusTest, SetAndReleaseCapture) { EXPECT_FALSE(mus_window->HasCapture()); } -// Ensure that manually setting NativeWidgetMus's mus::Window bounds also +// Ensure that manually setting NativeWidgetMus's ui::Window bounds also // updates its WindowTreeHost bounds. TEST_F(NativeWidgetMusTest, SetMusWindowBounds) { std::unique_ptr<Widget> widget(CreateWidget(nullptr)); @@ -503,7 +505,7 @@ TEST_F(NativeWidgetMusTest, SetMusWindowBounds) { widget->GetContentsView()->AddChildView(content); NativeWidgetMus* native_widget = static_cast<NativeWidgetMus*>(widget->native_widget_private()); - mus::Window* mus_window = native_widget->window(); + ui::Window* mus_window = native_widget->window(); gfx::Rect start_bounds = initial_bounds(); gfx::Rect end_bounds = gfx::Rect(40, 50, 60, 70); @@ -521,4 +523,90 @@ TEST_F(NativeWidgetMusTest, SetMusWindowBounds) { EXPECT_EQ(end_bounds, native_widget->window_tree_host()->GetBounds()); } +// Verifies visibility of the aura::Window and ui::Window are updated when the +// Widget is shown/hidden. +TEST_F(NativeWidgetMusTest, TargetVisibility) { + std::unique_ptr<Widget> widget(CreateWidget(nullptr)); + NativeWidgetMus* native_widget = + static_cast<NativeWidgetMus*>(widget->native_widget_private()); + ui::Window* mus_window = native_widget->window(); + EXPECT_FALSE(mus_window->visible()); + EXPECT_FALSE(widget->GetNativeView()->TargetVisibility()); + + widget->Show(); + EXPECT_TRUE(mus_window->visible()); + EXPECT_TRUE(widget->GetNativeView()->TargetVisibility()); +} + +// Indirectly verifies Show() isn't invoked twice on the underlying +// aura::Window. +TEST_F(NativeWidgetMusTest, DontShowTwice) { + std::unique_ptr<Widget> widget(CreateWidget(nullptr)); + widget->GetNativeView()->layer()->SetOpacity(0.0f); + // aura::Window::Show() allows the opacity to be 0 as long as the window is + // hidden. So, as long as this only invokes aura::Window::Show() once the + // DCHECK in aura::Window::Show() won't fire. + widget->Show(); +} + +namespace { + +// See description of test for details. +class IsMaximizedObserver : public ui::WindowObserver { + public: + IsMaximizedObserver() {} + ~IsMaximizedObserver() override {} + + void set_widget(Widget* widget) { widget_ = widget; } + + bool got_change() const { return got_change_; } + + // ui::WindowObserver: + void OnWindowSharedPropertyChanged( + ui::Window* window, + const std::string& name, + const std::vector<uint8_t>* old_data, + const std::vector<uint8_t>* new_data) override { + // Expect only one change for the show state. + ASSERT_FALSE(got_change_); + got_change_ = true; + EXPECT_EQ(ui::mojom::WindowManager::kShowState_Property, name); + EXPECT_TRUE(widget_->IsMaximized()); + } + + private: + bool got_change_ = false; + Widget* widget_ = nullptr; + + DISALLOW_COPY_AND_ASSIGN(IsMaximizedObserver); +}; + +} // namespace + +// Verifies that asking for Widget::IsMaximized() from within +// OnWindowSharedPropertyChanged() returns the right thing. +TEST_F(NativeWidgetMusTest, IsMaximized) { + ASSERT_TRUE(WindowManagerConnection::Exists()); + ui::Window* window = WindowManagerConnection::Get()->NewWindow( + std::map<std::string, std::vector<uint8_t>>()); + IsMaximizedObserver observer; + // NOTE: the order here is important, we purposefully add the + // ui::WindowObserver before creating NativeWidgetMus, which also adds its + // own observer. + window->AddObserver(&observer); + + std::unique_ptr<Widget> widget(new Widget()); + observer.set_widget(widget.get()); + Widget::InitParams params = CreateParams(Widget::InitParams::TYPE_WINDOW); + params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; + params.bounds = initial_bounds(); + params.native_widget = new NativeWidgetMus(widget.get(), window, + ui::mojom::SurfaceType::DEFAULT); + widget->Init(params); + window->SetSharedProperty<int32_t>( + ui::mojom::WindowManager::kShowState_Property, + static_cast<uint32_t>(ui::mojom::ShowState::MAXIMIZED)); + EXPECT_TRUE(widget->IsMaximized()); +} + } // namespace views diff --git a/chromium/ui/views/mus/os_exchange_data_provider_mus.cc b/chromium/ui/views/mus/os_exchange_data_provider_mus.cc new file mode 100644 index 00000000000..7d47924848a --- /dev/null +++ b/chromium/ui/views/mus/os_exchange_data_provider_mus.cc @@ -0,0 +1,372 @@ +// Copyright 2016 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/mus/os_exchange_data_provider_mus.h" + +#include <memory> +#include <string> +#include <utility> +#include <vector> + +#include "base/stl_util.h" +#include "base/strings/string_split.h" +#include "base/strings/string_util.h" +#include "base/strings/utf_string_conversions.h" +#include "mojo/common/common_type_converters.h" +#include "net/base/filename_util.h" +#include "services/ui/public/interfaces/clipboard.mojom.h" +#include "ui/base/dragdrop/file_info.h" +#include "url/gurl.h" + +namespace views { + +namespace { + +std::vector<uint8_t> FromString(const std::string& str) { + return std::vector<uint8_t>(str.begin(), str.end()); +} + +std::string ToString(const std::vector<uint8_t>& v) { + return std::string(v.begin(), v.end()); +} + +base::string16 ToString16(const std::vector<uint8_t>& v) { + DCHECK_EQ(0u, v.size() % 2); + return base::string16( + reinterpret_cast<const base::char16*>(v.data()), + v.size() / 2); +} + +std::vector<base::StringPiece> ParseURIList(const std::vector<uint8_t>& data) { + return base::SplitStringPiece( + base::StringPiece( + reinterpret_cast<const char*>(&data.front()), data.size()), + "\n", + base::KEEP_WHITESPACE, base::SPLIT_WANT_NONEMPTY); +} + +void AddString16ToVector(const base::string16& str, + std::vector<uint8_t>* bytes) { + const unsigned char* front = + reinterpret_cast<const uint8_t*>(str.data()); + bytes->insert(bytes->end(), front, front + (str.size() * 2)); +} + +} // namespace + +OSExchangeDataProviderMus::OSExchangeDataProviderMus() {} + +OSExchangeDataProviderMus::OSExchangeDataProviderMus(Data data) + : mime_data_(std::move(data)) {} + +OSExchangeDataProviderMus::~OSExchangeDataProviderMus() {} + +OSExchangeDataProviderMus::Data OSExchangeDataProviderMus::GetData() const { + return mime_data_; +} + +std::unique_ptr<ui::OSExchangeData::Provider> +OSExchangeDataProviderMus::Clone() const { + std::unique_ptr<OSExchangeDataProviderMus> r = + base::MakeUnique<OSExchangeDataProviderMus>(); + r->drag_image_ = drag_image_; + r->drag_image_offset_ = drag_image_offset_; + r->mime_data_ = mime_data_; + return base::WrapUnique<ui::OSExchangeData::Provider>(r.release()); +} + +void OSExchangeDataProviderMus::MarkOriginatedFromRenderer() { + // Currently unimplemented because ChromeOS doesn't need this. + // + // TODO(erg): Implement this when we start porting mus to other platforms. +} + +bool OSExchangeDataProviderMus::DidOriginateFromRenderer() const { + return false; +} + +void OSExchangeDataProviderMus::SetString(const base::string16& data) { + if (HasString()) + return; + + mime_data_[ui::mojom::kMimeTypeText] = FromString(base::UTF16ToUTF8(data)); +} + +void OSExchangeDataProviderMus::SetURL(const GURL& url, + const base::string16& title) { + base::string16 spec = base::UTF8ToUTF16(url.spec()); + std::vector<unsigned char> data; + AddString16ToVector(spec, &data); + AddString16ToVector(base::ASCIIToUTF16("\n"), &data); + AddString16ToVector(title, &data); + mime_data_[ui::mojom::kMimeTypeMozillaURL] = std::move(data); + + if (!base::ContainsKey(mime_data_, ui::mojom::kMimeTypeText)) + mime_data_[ui::mojom::kMimeTypeText] = FromString(url.spec()); +} + +void OSExchangeDataProviderMus::SetFilename(const base::FilePath& path) { + std::vector<ui::FileInfo> data; + data.push_back(ui::FileInfo(path, base::FilePath())); + SetFilenames(data); +} + +void OSExchangeDataProviderMus::SetFilenames( + const std::vector<ui::FileInfo>& file_names) { + std::vector<std::string> paths; + for (std::vector<ui::FileInfo>::const_iterator it = file_names.begin(); + it != file_names.end(); + ++it) { + std::string url_spec = net::FilePathToFileURL(it->path).spec(); + if (!url_spec.empty()) + paths.push_back(url_spec); + } + + std::string joined_data = base::JoinString(paths, "\n"); + mime_data_[ui::mojom::kMimeTypeURIList] = FromString(joined_data); +} + +void OSExchangeDataProviderMus::SetPickledData( + const ui::Clipboard::FormatType& format, + const base::Pickle& pickle) { + const unsigned char* bytes = + reinterpret_cast<const unsigned char*>(pickle.data()); + + mime_data_[format.Serialize()] = mojo::Array<uint8_t>( + std::vector<uint8_t>(bytes, bytes + pickle.size())); +} + +bool OSExchangeDataProviderMus::GetString(base::string16* data) const { + auto it = mime_data_.find(ui::mojom::kMimeTypeText); + if (it != mime_data_.end()) + *data = base::UTF8ToUTF16(ToString(it->second)); + return it != mime_data_.end(); +} + +bool OSExchangeDataProviderMus::GetURLAndTitle( + ui::OSExchangeData::FilenameToURLPolicy policy, + GURL* url, + base::string16* title) const { + auto it = mime_data_.find(ui::mojom::kMimeTypeMozillaURL); + if (it == mime_data_.end()) { + title->clear(); + return GetPlainTextURL(url) || + (policy == ui::OSExchangeData::CONVERT_FILENAMES && GetFileURL(url)); + } + + base::string16 data = ToString16(it->second); + base::string16::size_type newline = data.find('\n'); + if (newline == std::string::npos) + return false; + + GURL unparsed_url(data.substr(0, newline)); + if (!unparsed_url.is_valid()) + return false; + + *url = unparsed_url; + *title = data.substr(newline + 1); + return true; +} + +bool OSExchangeDataProviderMus::GetFilename(base::FilePath* path) const { + std::vector<ui::FileInfo> filenames; + if (GetFilenames(&filenames)) { + *path = filenames.front().path; + return true; + } + + return false; +} + +bool OSExchangeDataProviderMus::GetFilenames( + std::vector<ui::FileInfo>* file_names) const { + auto it = mime_data_.find(ui::mojom::kMimeTypeURIList); + if (it == mime_data_.end()) + return false; + + file_names->clear(); + for (const base::StringPiece& piece : ParseURIList(it->second)) { + GURL url(piece); + base::FilePath file_path; + if (url.SchemeIsFile() && net::FileURLToFilePath(url, &file_path)) + file_names->push_back(ui::FileInfo(file_path, base::FilePath())); + } + + return true; +} + +bool OSExchangeDataProviderMus::GetPickledData( + const ui::Clipboard::FormatType& format, + base::Pickle* data) const { + auto it = mime_data_.find(format.Serialize()); + if (it == mime_data_.end()) + return false; + + // Note that the pickle object on the right hand side of the assignment + // only refers to the bytes in |data|. The assignment copies the data. + *data = base::Pickle(reinterpret_cast<const char*>(it->second.data()), + static_cast<int>(it->second.size())); + return true; +} + +bool OSExchangeDataProviderMus::HasString() const { + return base::ContainsKey(mime_data_, ui::mojom::kMimeTypeText); +} + +bool OSExchangeDataProviderMus::HasURL( + ui::OSExchangeData::FilenameToURLPolicy policy) const { + if (base::ContainsKey(mime_data_, ui::mojom::kMimeTypeMozillaURL)) + return true; + + auto it = mime_data_.find(ui::mojom::kMimeTypeURIList); + if (it == mime_data_.end()) + return false; + + for (const base::StringPiece& piece : ParseURIList(it->second)) { + if (!GURL(piece).SchemeIsFile() || + policy == ui::OSExchangeData::CONVERT_FILENAMES) { + return true; + } + } + + return false; +} + +bool OSExchangeDataProviderMus::HasFile() const { + auto it = mime_data_.find(ui::mojom::kMimeTypeURIList); + if (it == mime_data_.end()) + return false; + + for (const base::StringPiece& piece : ParseURIList(it->second)) { + GURL url(piece); + base::FilePath file_path; + if (url.SchemeIsFile() && net::FileURLToFilePath(url, &file_path)) + return true; + } + + return false; +} + +bool OSExchangeDataProviderMus::HasCustomFormat( + const ui::Clipboard::FormatType& format) const { + return base::ContainsKey(mime_data_, format.Serialize()); +} + +// These methods were added in an ad-hoc way to different operating +// systems. We need to support them until they get cleaned up. +#if (!defined(OS_CHROMEOS) && defined(USE_X11)) || defined(OS_WIN) +void OSExchangeDataProviderMus::SetFileContents( + const base::FilePath& filename, + const std::string& file_contents) { +} +#endif + +#if defined(OS_WIN) +bool OSExchangeDataProviderMus::GetFileContents( + base::FilePath* filename, + std::string* file_contents) const { + return false; +} + +bool OSExchangeDataProviderMus::HasFileContents() const { + return false; +} + +void OSExchangeDataProviderMus::SetDownloadFileInfo( + const ui::OSExchangeData::DownloadFileInfo& download) { +} +#endif + +#if defined(USE_AURA) +void OSExchangeDataProviderMus::SetHtml(const base::string16& html, + const GURL& base_url) { + std::vector<unsigned char> bytes; + // Manually jam a UTF16 BOM into bytes because otherwise, other programs will + // assume UTF-8. + bytes.push_back(0xFF); + bytes.push_back(0xFE); + AddString16ToVector(html, &bytes); + mime_data_[ui::mojom::kMimeTypeHTML] = bytes; +} + +bool OSExchangeDataProviderMus::GetHtml(base::string16* html, + GURL* base_url) const { + auto it = mime_data_.find(ui::mojom::kMimeTypeHTML); + if (it == mime_data_.end()) + return false; + + const unsigned char* data = it->second.data(); + size_t size = it->second.size(); + base::string16 markup; + + // If the data starts with 0xFEFF, i.e., Byte Order Mark, assume it is + // UTF-16, otherwise assume UTF-8. + if (size >= 2 && + reinterpret_cast<const uint16_t*>(data)[0] == 0xFEFF) { + markup.assign(reinterpret_cast<const base::char16*>(data) + 1, + (size / 2) - 1); + } else { + base::UTF8ToUTF16(reinterpret_cast<const char*>(data), size, &markup); + } + + // If there is a terminating NULL, drop it. + if (!markup.empty() && markup.at(markup.length() - 1) == '\0') + markup.resize(markup.length() - 1); + + *html = markup; + *base_url = GURL(); + return true; +} + +bool OSExchangeDataProviderMus::HasHtml() const { + return base::ContainsKey(mime_data_, ui::mojom::kMimeTypeHTML); +} +#endif + +#if defined(USE_AURA) || defined(OS_MACOSX) +void OSExchangeDataProviderMus::SetDragImage( + const gfx::ImageSkia& image, + const gfx::Vector2d& cursor_offset) { + drag_image_ = image; + drag_image_offset_ = cursor_offset; +} + +const gfx::ImageSkia& OSExchangeDataProviderMus::GetDragImage() const { + return drag_image_; +} + +const gfx::Vector2d& OSExchangeDataProviderMus::GetDragImageOffset() const { + return drag_image_offset_; +} +#endif + +bool OSExchangeDataProviderMus::GetFileURL(GURL* url) const { + base::FilePath file_path; + if (!GetFilename(&file_path)) + return false; + + GURL test_url = net::FilePathToFileURL(file_path); + if (!test_url.is_valid()) + return false; + + if (url) + *url = test_url; + return true; +} + +bool OSExchangeDataProviderMus::GetPlainTextURL(GURL* url) const { + base::string16 str; + if (!GetString(&str)) + return false; + + GURL test_url(str); + if (!test_url.is_valid()) + return false; + + if (url) + *url = test_url; + return true; +} + +} // namespace views diff --git a/chromium/ui/views/mus/os_exchange_data_provider_mus.h b/chromium/ui/views/mus/os_exchange_data_provider_mus.h new file mode 100644 index 00000000000..120de932f67 --- /dev/null +++ b/chromium/ui/views/mus/os_exchange_data_provider_mus.h @@ -0,0 +1,118 @@ +// Copyright 2016 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_MUS_OS_EXCHANGE_DATA_PROVIDER_MUS_H_ +#define UI_VIEWS_MUS_OS_EXCHANGE_DATA_PROVIDER_MUS_H_ + +#include "ui/base/dragdrop/os_exchange_data.h" + +#include <map> +#include <memory> +#include <string> +#include <utility> +#include <vector> + +#include "mojo/public/cpp/bindings/map.h" +#include "ui/gfx/geometry/vector2d.h" +#include "ui/gfx/image/image_skia.h" +#include "ui/views/mus/mus_export.h" + +namespace views { + +// Translates chrome's requests for various data types to a platform specific +// type. In the case of mus, this is a mapping of MIME types to byte arrays. +// +// TODO(erg): In the long run, there's a lot of optimizations that we can do +// once everything targets mus. The entire model of OSExchangeDataProvider +// shoves all data across the wire at once whether it is needed or not. +class VIEWS_MUS_EXPORT OSExchangeDataProviderMus + : public ui::OSExchangeData::Provider { + public: + using Data = std::map<std::string, std::vector<uint8_t>>; + + OSExchangeDataProviderMus(); + explicit OSExchangeDataProviderMus(Data data); + ~OSExchangeDataProviderMus() override; + + // Returns the raw MIME type to data mapping. + Data GetData() const; + + // Overridden from OSExchangeData::Provider: + std::unique_ptr<Provider> Clone() const override; + + void MarkOriginatedFromRenderer() override; + bool DidOriginateFromRenderer() const override; + + void SetString(const base::string16& data) override; + void SetURL(const GURL& url, const base::string16& title) override; + void SetFilename(const base::FilePath& path) override; + void SetFilenames(const std::vector<ui::FileInfo>& file_names) override; + void SetPickledData(const ui::Clipboard::FormatType& format, + const base::Pickle& data) override; + + bool GetString(base::string16* data) const override; + bool GetURLAndTitle(ui::OSExchangeData::FilenameToURLPolicy policy, + GURL* url, + base::string16* title) const override; + bool GetFilename(base::FilePath* path) const override; + bool GetFilenames(std::vector<ui::FileInfo>* file_names) const override; + bool GetPickledData(const ui::Clipboard::FormatType& format, + base::Pickle* data) const override; + + bool HasString() const override; + bool HasURL(ui::OSExchangeData::FilenameToURLPolicy policy) const override; + bool HasFile() const override; + bool HasCustomFormat(const ui::Clipboard::FormatType& format) const override; + + // Provider doesn't have a consistent interface between operating systems; + // this wasn't seen as a problem when there was a single Provider subclass + // per operating system. Now we have to have at least two providers per OS, + // leading to the following warts, which will remain until we clean all the + // callsites up. +#if (!defined(OS_CHROMEOS) && defined(USE_X11)) || defined(OS_WIN) + void SetFileContents(const base::FilePath& filename, + const std::string& file_contents) override; +#endif +#if defined(OS_WIN) + bool GetFileContents(base::FilePath* filename, + std::string* file_contents) const override; + bool HasFileContents() const override; + void SetDownloadFileInfo( + const ui::OSExchangeData::DownloadFileInfo& download) override; +#endif + +#if defined(USE_AURA) + void SetHtml(const base::string16& html, const GURL& base_url) override; + bool GetHtml(base::string16* html, GURL* base_url) const override; + bool HasHtml() const override; +#endif + +#if defined(USE_AURA) || defined(OS_MACOSX) + void SetDragImage(const gfx::ImageSkia& image, + const gfx::Vector2d& cursor_offset) override; + const gfx::ImageSkia& GetDragImage() const override; + const gfx::Vector2d& GetDragImageOffset() const override; +#endif + + private: + // Returns true if |formats_| contains a file format and the file name can be + // parsed as a URL. + bool GetFileURL(GURL* url) const; + + // Returns true if |formats_| contains a string format and the string can be + // parsed as a URL. + bool GetPlainTextURL(GURL* url) const; + + // Drag image and offset data. + gfx::ImageSkia drag_image_; + gfx::Vector2d drag_image_offset_; + + Data mime_data_; + + DISALLOW_COPY_AND_ASSIGN(OSExchangeDataProviderMus); +}; + +} // namespace views + +#endif // UI_VIEWS_MUS_OS_EXCHANGE_DATA_PROVIDER_MUS_H_ diff --git a/chromium/ui/views/mus/os_exchange_data_provider_mus_unittest.cc b/chromium/ui/views/mus/os_exchange_data_provider_mus_unittest.cc new file mode 100644 index 00000000000..c2a7644dd96 --- /dev/null +++ b/chromium/ui/views/mus/os_exchange_data_provider_mus_unittest.cc @@ -0,0 +1,211 @@ +// Copyright 2016 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 <memory> + +#include "base/files/file_util.h" +#include "base/memory/ptr_util.h" +#include "base/message_loop/message_loop.h" +#include "base/pickle.h" +#include "base/strings/utf_string_conversions.h" +#include "build/build_config.h" +#include "net/base/filename_util.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "testing/platform_test.h" +#include "ui/base/dragdrop/os_exchange_data.h" +#include "ui/base/dragdrop/os_exchange_data_provider_factory.h" +#include "ui/events/platform/platform_event_source.h" +#include "ui/views/mus/os_exchange_data_provider_mus.h" +#include "url/gurl.h" + +using ui::Clipboard; +using ui::OSExchangeData; + +namespace views { + +// This file is a copy/paste of the unit tests in os_exchange_data_unittest.cc, +// with additional SetUp() to force the mus override. I thought about changeing +// the OSExchangeData test suite to a parameterized test suite, but I've +// previously had problems with that feature of gtest. + +class OSExchangeDataProviderMusTest + : public PlatformTest, + public ui::OSExchangeDataProviderFactory::Factory { + public: + OSExchangeDataProviderMusTest() {} + + // Overridden from PlatformTest: + void SetUp() override { + PlatformTest::SetUp(); + ui::OSExchangeDataProviderFactory::SetFactory(this); + } + + void TearDown() override { + ui::OSExchangeDataProviderFactory::SetFactory(nullptr); + PlatformTest::TearDown(); + } + + // Overridden from ui::OSExchangeDataProviderFactory::Factory: + std::unique_ptr<OSExchangeData::Provider> BuildProvider() override { + return base::MakeUnique<OSExchangeDataProviderMus>(); + } + + private: + base::MessageLoopForUI message_loop_; +}; + +TEST_F(OSExchangeDataProviderMusTest, StringDataGetAndSet) { + OSExchangeData data; + base::string16 input = base::ASCIIToUTF16("I can has cheezburger?"); + EXPECT_FALSE(data.HasString()); + data.SetString(input); + EXPECT_TRUE(data.HasString()); + + OSExchangeData data2( + std::unique_ptr<OSExchangeData::Provider>(data.provider().Clone())); + base::string16 output; + EXPECT_TRUE(data2.HasString()); + EXPECT_TRUE(data2.GetString(&output)); + EXPECT_EQ(input, output); + std::string url_spec = "http://www.goats.com/"; + GURL url(url_spec); + base::string16 title; + EXPECT_FALSE(data2.GetURLAndTitle( + OSExchangeData::DO_NOT_CONVERT_FILENAMES, &url, &title)); + // No URLs in |data|, so url should be untouched. + EXPECT_EQ(url_spec, url.spec()); +} + +TEST_F(OSExchangeDataProviderMusTest, TestURLExchangeFormats) { + OSExchangeData data; + std::string url_spec = "http://www.google.com/"; + GURL url(url_spec); + base::string16 url_title = base::ASCIIToUTF16("www.google.com"); + EXPECT_FALSE(data.HasURL(OSExchangeData::DO_NOT_CONVERT_FILENAMES)); + data.SetURL(url, url_title); + EXPECT_TRUE(data.HasURL(OSExchangeData::DO_NOT_CONVERT_FILENAMES)); + + OSExchangeData data2( + std::unique_ptr<OSExchangeData::Provider>(data.provider().Clone())); + + // URL spec and title should match + GURL output_url; + base::string16 output_title; + EXPECT_TRUE(data2.HasURL(OSExchangeData::DO_NOT_CONVERT_FILENAMES)); + EXPECT_TRUE(data2.GetURLAndTitle( + OSExchangeData::DO_NOT_CONVERT_FILENAMES, &output_url, &output_title)); + EXPECT_EQ(url_spec, output_url.spec()); + EXPECT_EQ(url_title, output_title); + base::string16 output_string; + + // URL should be the raw text response + EXPECT_TRUE(data2.GetString(&output_string)); + EXPECT_EQ(url_spec, base::UTF16ToUTF8(output_string)); +} + +// Test that setting the URL does not overwrite a previously set custom string. +TEST_F(OSExchangeDataProviderMusTest, URLAndString) { + OSExchangeData data; + base::string16 string = base::ASCIIToUTF16("I can has cheezburger?"); + data.SetString(string); + std::string url_spec = "http://www.google.com/"; + GURL url(url_spec); + base::string16 url_title = base::ASCIIToUTF16("www.google.com"); + data.SetURL(url, url_title); + + base::string16 output_string; + EXPECT_TRUE(data.GetString(&output_string)); + EXPECT_EQ(string, output_string); + + GURL output_url; + base::string16 output_title; + EXPECT_TRUE(data.GetURLAndTitle( + OSExchangeData::DO_NOT_CONVERT_FILENAMES, &output_url, &output_title)); + EXPECT_EQ(url_spec, output_url.spec()); + EXPECT_EQ(url_title, output_title); +} + +TEST_F(OSExchangeDataProviderMusTest, TestFileToURLConversion) { + OSExchangeData data; + EXPECT_FALSE(data.HasURL(OSExchangeData::DO_NOT_CONVERT_FILENAMES)); + EXPECT_FALSE(data.HasURL(OSExchangeData::CONVERT_FILENAMES)); + EXPECT_FALSE(data.HasFile()); + + base::FilePath current_directory; + ASSERT_TRUE(base::GetCurrentDirectory(¤t_directory)); + + data.SetFilename(current_directory); + + { + EXPECT_FALSE(data.HasURL(OSExchangeData::DO_NOT_CONVERT_FILENAMES)); + GURL actual_url; + base::string16 actual_title; + EXPECT_FALSE(data.GetURLAndTitle( + OSExchangeData::DO_NOT_CONVERT_FILENAMES, &actual_url, &actual_title)); + EXPECT_EQ(GURL(), actual_url); + EXPECT_EQ(base::string16(), actual_title); + } + + { + EXPECT_TRUE(data.HasURL(OSExchangeData::CONVERT_FILENAMES)); + GURL actual_url; + base::string16 actual_title; + EXPECT_TRUE(data.GetURLAndTitle(OSExchangeData::CONVERT_FILENAMES, + &actual_url, &actual_title)); + // Some Mac OS versions return the URL in file://localhost form instead + // of file:///, so we compare the url's path not its absolute string. + EXPECT_EQ(net::FilePathToFileURL(current_directory).path(), + actual_url.path()); + EXPECT_EQ(base::string16(), actual_title); + } + EXPECT_TRUE(data.HasFile()); + base::FilePath actual_path; + EXPECT_TRUE(data.GetFilename(&actual_path)); + EXPECT_EQ(current_directory, actual_path); +} + +TEST_F(OSExchangeDataProviderMusTest, TestPickledData) { + const Clipboard::FormatType kTestFormat = + Clipboard::GetFormatType("application/vnd.chromium.test"); + + base::Pickle saved_pickle; + saved_pickle.WriteInt(1); + saved_pickle.WriteInt(2); + OSExchangeData data; + data.SetPickledData(kTestFormat, saved_pickle); + + OSExchangeData copy( + std::unique_ptr<OSExchangeData::Provider>(data.provider().Clone())); + EXPECT_TRUE(copy.HasCustomFormat(kTestFormat)); + + base::Pickle restored_pickle; + EXPECT_TRUE(copy.GetPickledData(kTestFormat, &restored_pickle)); + base::PickleIterator iterator(restored_pickle); + int value; + EXPECT_TRUE(iterator.ReadInt(&value)); + EXPECT_EQ(1, value); + EXPECT_TRUE(iterator.ReadInt(&value)); + EXPECT_EQ(2, value); +} + +#if defined(USE_AURA) +TEST_F(OSExchangeDataProviderMusTest, TestHTML) { + OSExchangeData data; + GURL url("http://www.google.com/"); + base::string16 html = base::ASCIIToUTF16( + "<HTML>\n<BODY>\n" + "<b>bold.</b> <i><b>This is bold italic.</b></i>\n" + "</BODY>\n</HTML>"); + data.SetHtml(html, url); + + OSExchangeData copy( + std::unique_ptr<OSExchangeData::Provider>(data.provider().Clone())); + base::string16 read_html; + EXPECT_TRUE(copy.GetHtml(&read_html, &url)); + EXPECT_EQ(html, read_html); +} +#endif + +} // namespace views + diff --git a/chromium/ui/views/mus/pointer_watcher_event_router.cc b/chromium/ui/views/mus/pointer_watcher_event_router.cc new file mode 100644 index 00000000000..998c4cf0d61 --- /dev/null +++ b/chromium/ui/views/mus/pointer_watcher_event_router.cc @@ -0,0 +1,152 @@ +// Copyright 2016 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/mus/pointer_watcher_event_router.h" + +#include "services/ui/public/cpp/window.h" +#include "services/ui/public/cpp/window_tree_client.h" +#include "ui/display/screen.h" +#include "ui/events/base_event_utils.h" +#include "ui/events/event.h" +#include "ui/views/mus/native_widget_mus.h" +#include "ui/views/pointer_watcher.h" + +namespace views { +namespace { + +bool HasPointerWatcher( + base::ObserverList<views::PointerWatcher, true>* observer_list) { + if (!observer_list->might_have_observers()) + return false; + + // might_have_observers() returned true, see if there really are any + // observers. The only way to truly know is to use an Iterator and see if it + // has at least one observer. + base::ObserverList<PointerWatcher>::Iterator iterator(observer_list); + return !!iterator.GetNext(); +} + +} // namespace + +PointerWatcherEventRouter::PointerWatcherEventRouter( + ui::WindowTreeClient* client) + : window_tree_client_(client) { + client->AddObserver(this); +} + +PointerWatcherEventRouter::~PointerWatcherEventRouter() { + if (window_tree_client_) + window_tree_client_->RemoveObserver(this); +} + +void PointerWatcherEventRouter::AddPointerWatcher(PointerWatcher* watcher, + bool wants_moves) { + // Pointer watchers cannot be added multiple times. + DCHECK(!move_watchers_.HasObserver(watcher)); + DCHECK(!non_move_watchers_.HasObserver(watcher)); + if (wants_moves) { + move_watchers_.AddObserver(watcher); + if (event_types_ != EventTypes::MOVE_EVENTS) { + event_types_ = EventTypes::MOVE_EVENTS; + const bool wants_moves = true; + window_tree_client_->StartPointerWatcher(wants_moves); + } + } else { + non_move_watchers_.AddObserver(watcher); + if (event_types_ == EventTypes::NONE) { + event_types_ = EventTypes::NON_MOVE_EVENTS; + const bool wants_moves = false; + window_tree_client_->StartPointerWatcher(wants_moves); + } + } +} + +void PointerWatcherEventRouter::RemovePointerWatcher(PointerWatcher* watcher) { + if (non_move_watchers_.HasObserver(watcher)) { + non_move_watchers_.RemoveObserver(watcher); + } else { + DCHECK(move_watchers_.HasObserver(watcher)); + move_watchers_.RemoveObserver(watcher); + } + const EventTypes types = DetermineEventTypes(); + if (types == event_types_) + return; + + event_types_ = types; + switch (types) { + case EventTypes::NONE: + window_tree_client_->StopPointerWatcher(); + break; + case EventTypes::NON_MOVE_EVENTS: + window_tree_client_->StartPointerWatcher(false); + break; + case EventTypes::MOVE_EVENTS: + // It isn't possible to remove an observer and transition to wanting move + // events. This could only happen if there is a bug in the add logic. + NOTREACHED(); + break; + } +} + +void PointerWatcherEventRouter::OnPointerEventObserved( + const ui::PointerEvent& event, + ui::Window* target) { + Widget* target_widget = nullptr; + if (target) { + ui::Window* window = target; + while (window && !target_widget) { + target_widget = NativeWidgetMus::GetWidgetForWindow(target); + window = window->parent(); + } + } + + // The mojo input events type converter uses the event root_location field + // to store screen coordinates. Screen coordinates really should be returned + // separately. See http://crbug.com/608547 + gfx::Point location_in_screen = event.AsLocatedEvent()->root_location(); + FOR_EACH_OBSERVER( + PointerWatcher, move_watchers_, + OnPointerEventObserved(event, location_in_screen, target_widget)); + if (event.type() != ui::ET_POINTER_MOVED) { + FOR_EACH_OBSERVER( + PointerWatcher, non_move_watchers_, + OnPointerEventObserved(event, location_in_screen, target_widget)); + } +} + +PointerWatcherEventRouter::EventTypes +PointerWatcherEventRouter::DetermineEventTypes() { + if (HasPointerWatcher(&move_watchers_)) + return EventTypes::MOVE_EVENTS; + + if (HasPointerWatcher(&non_move_watchers_)) + return EventTypes::NON_MOVE_EVENTS; + + return EventTypes::NONE; +} + +void PointerWatcherEventRouter::OnWindowTreeCaptureChanged( + ui::Window* gained_capture, + ui::Window* lost_capture) { + const ui::MouseEvent mouse_event(ui::ET_MOUSE_CAPTURE_CHANGED, gfx::Point(), + gfx::Point(), ui::EventTimeForNow(), 0, 0); + const ui::PointerEvent event(mouse_event); + gfx::Point location_in_screen = + display::Screen::GetScreen()->GetCursorScreenPoint(); + FOR_EACH_OBSERVER(PointerWatcher, move_watchers_, + OnPointerEventObserved(event, location_in_screen, nullptr)); + FOR_EACH_OBSERVER(PointerWatcher, non_move_watchers_, + OnPointerEventObserved(event, location_in_screen, nullptr)); +} + +void PointerWatcherEventRouter::OnDidDestroyClient( + ui::WindowTreeClient* client) { + // We expect that all observers have been removed by this time. + DCHECK_EQ(event_types_, EventTypes::NONE); + DCHECK_EQ(client, window_tree_client_); + window_tree_client_->RemoveObserver(this); + window_tree_client_ = nullptr; +} + +} // namespace views diff --git a/chromium/ui/views/mus/pointer_watcher_event_router.h b/chromium/ui/views/mus/pointer_watcher_event_router.h new file mode 100644 index 00000000000..e75e81d441b --- /dev/null +++ b/chromium/ui/views/mus/pointer_watcher_event_router.h @@ -0,0 +1,79 @@ +// Copyright 2016 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_MUS_POINTER_WATCHER_EVENT_ROUTER_H_ +#define UI_VIEWS_MUS_POINTER_WATCHER_EVENT_ROUTER_H_ + +#include "base/compiler_specific.h" +#include "base/macros.h" +#include "base/observer_list.h" +#include "services/ui/public/cpp/window_tree_client_observer.h" +#include "ui/views/mus/mus_export.h" + +namespace ui { +class PointerEvent; +class WindowTreeClient; +} + +namespace views { + +class PointerWatcher; +class PointerWatcherEventRouterTest; + +// PointerWatcherEventRouter is responsible for maintaining the list of +// PointerWatchers and notifying appropriately. It is expected the owner of +// PointerWatcherEventRouter is a WindowTreeClientDelegate and calls +// OnPointerEventObserved(). +class VIEWS_MUS_EXPORT PointerWatcherEventRouter + : public NON_EXPORTED_BASE(ui::WindowTreeClientObserver) { + public: + // Public solely for tests. + enum EventTypes { + // No PointerWatchers have been added. + NONE, + + // Used when the only PointerWatchers added do not want moves. + NON_MOVE_EVENTS, + + // Used when at least one PointerWatcher has been added that wants moves. + MOVE_EVENTS, + }; + + explicit PointerWatcherEventRouter(ui::WindowTreeClient* client); + ~PointerWatcherEventRouter() override; + + // Called by WindowTreeClientDelegate to notify PointerWatchers appropriately. + void OnPointerEventObserved(const ui::PointerEvent& event, + ui::Window* target); + + void AddPointerWatcher(PointerWatcher* watcher, bool wants_moves); + void RemovePointerWatcher(PointerWatcher* watcher); + + private: + friend class PointerWatcherEventRouterTest; + + // Determines EventTypes based on the number and type of PointerWatchers. + EventTypes DetermineEventTypes(); + + // ui::WindowTreeClientObserver: + void OnWindowTreeCaptureChanged(ui::Window* gained_capture, + ui::Window* lost_capture) override; + void OnDidDestroyClient(ui::WindowTreeClient* client) override; + + ui::WindowTreeClient* window_tree_client_; + // The true parameter to ObserverList indicates the list must be empty on + // destruction. Two sets of observers are maintained, one for observers not + // needing moves |non_move_watchers_| and |move_watchers_| for those + // observers wanting moves too. + base::ObserverList<views::PointerWatcher, true> non_move_watchers_; + base::ObserverList<views::PointerWatcher, true> move_watchers_; + + EventTypes event_types_ = EventTypes::NONE; + + DISALLOW_COPY_AND_ASSIGN(PointerWatcherEventRouter); +}; + +} // namespace views + +#endif // UI_VIEWS_MUS_POINTER_WATCHER_EVENT_ROUTER_H_ diff --git a/chromium/ui/views/mus/pointer_watcher_event_router_unittest.cc b/chromium/ui/views/mus/pointer_watcher_event_router_unittest.cc new file mode 100644 index 00000000000..511cc3338e7 --- /dev/null +++ b/chromium/ui/views/mus/pointer_watcher_event_router_unittest.cc @@ -0,0 +1,244 @@ +// Copyright 2016 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/mus/pointer_watcher_event_router.h" + +#include <memory> + +#include "base/message_loop/message_loop.h" +#include "services/ui/public/cpp/tests/window_tree_client_private.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "ui/events/event.h" +#include "ui/views/mus/window_manager_connection.h" +#include "ui/views/pointer_watcher.h" +#include "ui/views/test/scoped_views_test_helper.h" + +namespace views { +namespace { + +class TestPointerWatcher : public PointerWatcher { + public: + TestPointerWatcher() {} + ~TestPointerWatcher() override {} + + ui::PointerEvent* last_event_observed() { return last_event_observed_.get(); } + + void Reset() { last_event_observed_.reset(); } + + // PointerWatcher: + void OnPointerEventObserved(const ui::PointerEvent& event, + const gfx::Point& location_in_screen, + Widget* target) override { + last_event_observed_ = base::MakeUnique<ui::PointerEvent>(event); + } + + private: + std::unique_ptr<ui::PointerEvent> last_event_observed_; + + DISALLOW_COPY_AND_ASSIGN(TestPointerWatcher); +}; + +} // namespace + +class PointerWatcherEventRouterTest : public testing::Test { + public: + PointerWatcherEventRouterTest() {} + ~PointerWatcherEventRouterTest() override {} + + void OnPointerEventObserved(const ui::PointerEvent& event) { + WindowManagerConnection::Get() + ->pointer_watcher_event_router() + ->OnPointerEventObserved(event, nullptr); + } + + PointerWatcherEventRouter::EventTypes event_types() const { + return WindowManagerConnection::Get() + ->pointer_watcher_event_router() + ->event_types_; + } + + private: + DISALLOW_COPY_AND_ASSIGN(PointerWatcherEventRouterTest); +}; + +TEST_F(PointerWatcherEventRouterTest, EventTypes) { + base::MessageLoop message_loop(base::MessageLoop::TYPE_UI); + ScopedViewsTestHelper helper; + TestPointerWatcher pointer_watcher1, pointer_watcher2; + PointerWatcherEventRouter* pointer_watcher_event_router = + WindowManagerConnection::Get()->pointer_watcher_event_router(); + ui::WindowTreeClientPrivate test_api( + WindowManagerConnection::Get()->client()); + EXPECT_FALSE(test_api.HasPointerWatcher()); + + // Start with no moves. + pointer_watcher_event_router->AddPointerWatcher(&pointer_watcher1, false); + EXPECT_EQ(PointerWatcherEventRouter::EventTypes::NON_MOVE_EVENTS, + event_types()); + EXPECT_TRUE(test_api.HasPointerWatcher()); + + // Add moves. + pointer_watcher_event_router->AddPointerWatcher(&pointer_watcher2, true); + EXPECT_EQ(PointerWatcherEventRouter::EventTypes::MOVE_EVENTS, event_types()); + EXPECT_TRUE(test_api.HasPointerWatcher()); + + // Remove no-moves. + pointer_watcher_event_router->RemovePointerWatcher(&pointer_watcher1); + EXPECT_EQ(PointerWatcherEventRouter::EventTypes::MOVE_EVENTS, event_types()); + EXPECT_TRUE(test_api.HasPointerWatcher()); + + // Remove moves. + pointer_watcher_event_router->RemovePointerWatcher(&pointer_watcher2); + EXPECT_EQ(PointerWatcherEventRouter::EventTypes::NONE, event_types()); + EXPECT_FALSE(test_api.HasPointerWatcher()); + + // Add moves. + pointer_watcher_event_router->AddPointerWatcher(&pointer_watcher2, true); + EXPECT_EQ(PointerWatcherEventRouter::EventTypes::MOVE_EVENTS, event_types()); + EXPECT_TRUE(test_api.HasPointerWatcher()); + + // Add no moves. + pointer_watcher_event_router->AddPointerWatcher(&pointer_watcher1, false); + EXPECT_EQ(PointerWatcherEventRouter::EventTypes::MOVE_EVENTS, event_types()); + EXPECT_TRUE(test_api.HasPointerWatcher()); + + // Remove moves. + pointer_watcher_event_router->RemovePointerWatcher(&pointer_watcher2); + EXPECT_EQ(PointerWatcherEventRouter::EventTypes::NON_MOVE_EVENTS, + event_types()); + EXPECT_TRUE(test_api.HasPointerWatcher()); + + // Remove no-moves. + pointer_watcher_event_router->RemovePointerWatcher(&pointer_watcher1); + EXPECT_EQ(PointerWatcherEventRouter::EventTypes::NONE, event_types()); + EXPECT_FALSE(test_api.HasPointerWatcher()); +} + +TEST_F(PointerWatcherEventRouterTest, PointerWatcherNoMove) { + base::MessageLoop message_loop(base::MessageLoop::TYPE_UI); + ScopedViewsTestHelper helper; + ASSERT_TRUE(WindowManagerConnection::Get()); + PointerWatcherEventRouter* pointer_watcher_event_router = + WindowManagerConnection::Get()->pointer_watcher_event_router(); + ASSERT_TRUE(pointer_watcher_event_router); + + ui::PointerEvent pointer_event_down( + ui::ET_POINTER_DOWN, gfx::Point(), gfx::Point(), ui::EF_NONE, 1, 0, + ui::PointerDetails(ui::EventPointerType::POINTER_TYPE_TOUCH), + base::TimeTicks()); + ui::PointerEvent pointer_event_up( + ui::ET_POINTER_UP, gfx::Point(), gfx::Point(), ui::EF_NONE, 1, 0, + ui::PointerDetails(ui::EventPointerType::POINTER_TYPE_MOUSE), + base::TimeTicks()); + ui::PointerEvent pointer_event_wheel( + ui::ET_POINTER_WHEEL_CHANGED, gfx::Point(), gfx::Point(), ui::EF_NONE, 1, + 0, ui::PointerDetails(ui::EventPointerType::POINTER_TYPE_MOUSE), + base::TimeTicks()); + ui::PointerEvent pointer_event_capture( + ui::ET_POINTER_CAPTURE_CHANGED, gfx::Point(), gfx::Point(), ui::EF_NONE, + 1, 0, ui::PointerDetails(ui::EventPointerType::POINTER_TYPE_MOUSE), + base::TimeTicks()); + + // PointerWatchers receive pointer down events. + TestPointerWatcher watcher1; + pointer_watcher_event_router->AddPointerWatcher(&watcher1, false); + OnPointerEventObserved(pointer_event_down); + EXPECT_EQ(ui::ET_POINTER_DOWN, watcher1.last_event_observed()->type()); + watcher1.Reset(); + + // PointerWatchers receive pointer up events. + OnPointerEventObserved(pointer_event_up); + EXPECT_EQ(ui::ET_POINTER_UP, watcher1.last_event_observed()->type()); + watcher1.Reset(); + + // PointerWatchers receive pointer wheel changed events. + OnPointerEventObserved(pointer_event_wheel); + EXPECT_EQ(ui::ET_POINTER_WHEEL_CHANGED, + watcher1.last_event_observed()->type()); + watcher1.Reset(); + + // PointerWatchers receive pointer capture changed events. + OnPointerEventObserved(pointer_event_capture); + EXPECT_EQ(ui::ET_POINTER_CAPTURE_CHANGED, + watcher1.last_event_observed()->type()); + watcher1.Reset(); + + // Two PointerWatchers can both receive a single observed event. + TestPointerWatcher watcher2; + pointer_watcher_event_router->AddPointerWatcher(&watcher2, false); + OnPointerEventObserved(pointer_event_down); + EXPECT_EQ(ui::ET_POINTER_DOWN, watcher1.last_event_observed()->type()); + EXPECT_EQ(ui::ET_POINTER_DOWN, watcher2.last_event_observed()->type()); + watcher1.Reset(); + watcher2.Reset(); + + // Removing the first PointerWatcher stops sending events to it. + pointer_watcher_event_router->RemovePointerWatcher(&watcher1); + OnPointerEventObserved(pointer_event_down); + EXPECT_FALSE(watcher1.last_event_observed()); + EXPECT_EQ(ui::ET_POINTER_DOWN, watcher2.last_event_observed()->type()); + watcher1.Reset(); + watcher2.Reset(); + + // Removing the last PointerWatcher stops sending events to it. + pointer_watcher_event_router->RemovePointerWatcher(&watcher2); + OnPointerEventObserved(pointer_event_down); + EXPECT_FALSE(watcher1.last_event_observed()); + EXPECT_FALSE(watcher2.last_event_observed()); +} + +TEST_F(PointerWatcherEventRouterTest, PointerWatcherMove) { + base::MessageLoop message_loop(base::MessageLoop::TYPE_UI); + ScopedViewsTestHelper helper; + ASSERT_TRUE(WindowManagerConnection::Get()); + PointerWatcherEventRouter* pointer_watcher_event_router = + WindowManagerConnection::Get()->pointer_watcher_event_router(); + ASSERT_TRUE(pointer_watcher_event_router); + + ui::PointerEvent pointer_event_down( + ui::ET_POINTER_DOWN, gfx::Point(), gfx::Point(), ui::EF_NONE, 1, 0, + ui::PointerDetails(ui::EventPointerType::POINTER_TYPE_TOUCH), + base::TimeTicks()); + ui::PointerEvent pointer_event_move( + ui::ET_POINTER_MOVED, gfx::Point(), gfx::Point(), ui::EF_NONE, 1, 0, + ui::PointerDetails(ui::EventPointerType::POINTER_TYPE_TOUCH), + base::TimeTicks()); + + // PointerWatchers receive pointer down events. + TestPointerWatcher watcher1; + pointer_watcher_event_router->AddPointerWatcher(&watcher1, true); + OnPointerEventObserved(pointer_event_down); + EXPECT_EQ(ui::ET_POINTER_DOWN, watcher1.last_event_observed()->type()); + watcher1.Reset(); + + // PointerWatchers receive pointer move events. + OnPointerEventObserved(pointer_event_move); + EXPECT_EQ(ui::ET_POINTER_MOVED, watcher1.last_event_observed()->type()); + watcher1.Reset(); + + // Two PointerWatchers can both receive a single observed event. + TestPointerWatcher watcher2; + pointer_watcher_event_router->AddPointerWatcher(&watcher2, true); + OnPointerEventObserved(pointer_event_move); + EXPECT_EQ(ui::ET_POINTER_MOVED, watcher1.last_event_observed()->type()); + EXPECT_EQ(ui::ET_POINTER_MOVED, watcher2.last_event_observed()->type()); + watcher1.Reset(); + watcher2.Reset(); + + // Removing the first PointerWatcher stops sending events to it. + pointer_watcher_event_router->RemovePointerWatcher(&watcher1); + OnPointerEventObserved(pointer_event_move); + EXPECT_FALSE(watcher1.last_event_observed()); + EXPECT_EQ(ui::ET_POINTER_MOVED, watcher2.last_event_observed()->type()); + watcher1.Reset(); + watcher2.Reset(); + + // Removing the last PointerWatcher stops sending events to it. + pointer_watcher_event_router->RemovePointerWatcher(&watcher2); + OnPointerEventObserved(pointer_event_move); + EXPECT_FALSE(watcher1.last_event_observed()); + EXPECT_FALSE(watcher2.last_event_observed()); +} + +} // namespace views diff --git a/chromium/ui/views/mus/screen_mus.cc b/chromium/ui/views/mus/screen_mus.cc index 533adcb20d2..e25a5e15f5e 100644 --- a/chromium/ui/views/mus/screen_mus.cc +++ b/chromium/ui/views/mus/screen_mus.cc @@ -2,29 +2,25 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +// This has to be before any other includes, else default is picked up. +// See base/logging for details on this. +#define NOTIMPLEMENTED_POLICY 5 + #include "ui/views/mus/screen_mus.h" #include "services/shell/public/cpp/connection.h" #include "services/shell/public/cpp/connector.h" #include "ui/aura/window.h" -#include "ui/display/display_finder.h" -#include "ui/display/display_observer.h" -#include "ui/display/mojo/display_type_converters.h" #include "ui/views/mus/screen_mus_delegate.h" #include "ui/views/mus/window_manager_frame_values.h" -#ifdef NOTIMPLEMENTED -#undef NOTIMPLEMENTED -#define NOTIMPLEMENTED() DVLOG(1) << "notimplemented" -#endif - namespace mojo { template <> struct TypeConverter<views::WindowManagerFrameValues, - mus::mojom::FrameDecorationValuesPtr> { + ui::mojom::FrameDecorationValuesPtr> { static views::WindowManagerFrameValues Convert( - const mus::mojom::FrameDecorationValuesPtr& input) { + const ui::mojom::FrameDecorationValuesPtr& input) { views::WindowManagerFrameValues result; result.normal_insets = input->normal_client_area_insets; result.maximized_insets = input->maximized_client_area_insets; @@ -42,15 +38,10 @@ ScreenMus::ScreenMus(ScreenMusDelegate* delegate) display_manager_observer_binding_(this) { } -ScreenMus::~ScreenMus() { - DCHECK_EQ(this, display::Screen::GetScreen()); - display::Screen::SetScreenInstance(nullptr); -} +ScreenMus::~ScreenMus() {} void ScreenMus::Init(shell::Connector* connector) { - display::Screen::SetScreenInstance(this); - - connector->ConnectToInterface("mojo:mus", &display_manager_); + connector->ConnectToInterface("service:ui", &display_manager_); display_manager_->AddObserver( display_manager_observer_binding_.CreateInterfacePtrAndBind()); @@ -64,28 +55,14 @@ void ScreenMus::Init(shell::Connector* connector) { // The WaitForIncomingMethodCall() should have supplied the set of Displays, // unless mus is going down, in which case encountered_error() is true, or the // call to WaitForIncomingMethodCall() failed. - if (display_list_.displays().empty()) { + if (display_list()->displays().empty()) { DCHECK(display_manager_.encountered_error() || !success); // In this case we install a default display and assume the process is // going to exit shortly so that the real value doesn't matter. - display_list_.AddDisplay( + display_list()->AddDisplay( display::Display(0xFFFFFFFF, gfx::Rect(0, 0, 801, 802)), - DisplayList::Type::PRIMARY); - } -} - -void ScreenMus::ProcessDisplayChanged(const display::Display& changed_display, - bool is_primary) { - if (display_list_.FindDisplayById(changed_display.id()) == - display_list_.displays().end()) { - display_list_.AddDisplay(changed_display, - is_primary ? DisplayList::Type::PRIMARY - : DisplayList::Type::NOT_PRIMARY); - return; + display::DisplayList::Type::PRIMARY); } - display_list_.UpdateDisplay( - changed_display, - is_primary ? DisplayList::Type::PRIMARY : DisplayList::Type::NOT_PRIMARY); } gfx::Point ScreenMus::GetCursorScreenPoint() { @@ -100,88 +77,39 @@ gfx::Point ScreenMus::GetCursorScreenPoint() { } bool ScreenMus::IsWindowUnderCursor(gfx::NativeWindow window) { - if (!window) - return false; - - return window->IsVisible() && - window->GetBoundsInScreen().Contains(GetCursorScreenPoint()); -} - -gfx::NativeWindow ScreenMus::GetWindowAtScreenPoint(const gfx::Point& point) { - NOTIMPLEMENTED(); - return nullptr; -} - -display::Display ScreenMus::GetPrimaryDisplay() const { - return *display_list_.GetPrimaryDisplayIterator(); -} - -display::Display ScreenMus::GetDisplayNearestWindow( - gfx::NativeView view) const { - NOTIMPLEMENTED(); - return *display_list_.GetPrimaryDisplayIterator(); -} - -display::Display ScreenMus::GetDisplayNearestPoint( - const gfx::Point& point) const { - return *display::FindDisplayNearestPoint(display_list_.displays(), point); -} - -int ScreenMus::GetNumDisplays() const { - return static_cast<int>(display_list_.displays().size()); -} - -std::vector<display::Display> ScreenMus::GetAllDisplays() const { - return display_list_.displays(); -} - -display::Display ScreenMus::GetDisplayMatching( - const gfx::Rect& match_rect) const { - const display::Display* match = display::FindDisplayWithBiggestIntersection( - display_list_.displays(), match_rect); - return match ? *match : GetPrimaryDisplay(); -} - -void ScreenMus::AddObserver(display::DisplayObserver* observer) { - display_list_.AddObserver(observer); -} - -void ScreenMus::RemoveObserver(display::DisplayObserver* observer) { - display_list_.RemoveObserver(observer); + return window && window->IsVisible() && + window->GetBoundsInScreen().Contains(GetCursorScreenPoint()); } -void ScreenMus::OnDisplays( - mojo::Array<mus::mojom::DisplayPtr> transport_displays) { +void ScreenMus::OnDisplays(mojo::Array<ui::mojom::WsDisplayPtr> ws_displays) { // This should only be called once from Init() before any observers have been // added. - DCHECK(display_list_.displays().empty()); - std::vector<display::Display> displays = - transport_displays.To<std::vector<display::Display>>(); - for (size_t i = 0; i < displays.size(); ++i) { - const bool is_primary = transport_displays[i]->is_primary; - display_list_.AddDisplay(displays[i], is_primary - ? DisplayList::Type::PRIMARY - : DisplayList::Type::NOT_PRIMARY); + DCHECK(display_list()->displays().empty()); + for (size_t i = 0; i < ws_displays.size(); ++i) { + const bool is_primary = ws_displays[i]->is_primary; + display_list()->AddDisplay(ws_displays[i]->display, + is_primary + ? display::DisplayList::Type::PRIMARY + : display::DisplayList::Type::NOT_PRIMARY); if (is_primary) { // TODO(sky): Make WindowManagerFrameValues per display. WindowManagerFrameValues frame_values = - transport_displays[i] + ws_displays[i] ->frame_decoration_values.To<WindowManagerFrameValues>(); WindowManagerFrameValues::SetInstance(frame_values); } } - DCHECK(!display_list_.displays().empty()); + DCHECK(!display_list()->displays().empty()); } void ScreenMus::OnDisplaysChanged( - mojo::Array<mus::mojom::DisplayPtr> transport_displays) { - for (size_t i = 0; i < transport_displays.size(); ++i) { - const bool is_primary = transport_displays[i]->is_primary; - ProcessDisplayChanged(transport_displays[i].To<display::Display>(), - is_primary); + mojo::Array<ui::mojom::WsDisplayPtr> ws_displays) { + for (size_t i = 0; i < ws_displays.size(); ++i) { + const bool is_primary = ws_displays[i]->is_primary; + ProcessDisplayChanged(ws_displays[i]->display, is_primary); if (is_primary) { WindowManagerFrameValues frame_values = - transport_displays[i] + ws_displays[i] ->frame_decoration_values.To<WindowManagerFrameValues>(); WindowManagerFrameValues::SetInstance(frame_values); if (delegate_) @@ -191,7 +119,7 @@ void ScreenMus::OnDisplaysChanged( } void ScreenMus::OnDisplayRemoved(int64_t id) { - display_list_.RemoveDisplay(id); + display_list()->RemoveDisplay(id); } } // namespace views diff --git a/chromium/ui/views/mus/screen_mus.h b/chromium/ui/views/mus/screen_mus.h index ddd9c14077d..b86d5d4b16b 100644 --- a/chromium/ui/views/mus/screen_mus.h +++ b/chromium/ui/views/mus/screen_mus.h @@ -5,15 +5,9 @@ #ifndef UI_VIEWS_MUS_SCREEN_MUS_H_ #define UI_VIEWS_MUS_SCREEN_MUS_H_ -#include <vector> - -#include "base/observer_list.h" -#include "base/run_loop.h" -#include "components/mus/public/interfaces/display.mojom.h" #include "mojo/public/cpp/bindings/binding.h" -#include "ui/display/display.h" -#include "ui/display/screen.h" -#include "ui/views/mus/display_list.h" +#include "services/ui/public/interfaces/display.mojom.h" +#include "ui/display/screen_base.h" #include "ui/views/mus/mus_export.h" namespace shell { @@ -24,10 +18,10 @@ namespace views { class ScreenMusDelegate; -// Screen implementation backed by mus::mojom::DisplayManager. +// Screen implementation backed by ui::mojom::DisplayManager. class VIEWS_MUS_EXPORT ScreenMus - : public display::Screen, - public NON_EXPORTED_BASE(mus::mojom::DisplayManagerObserver) { + : public display::ScreenBase, + public NON_EXPORTED_BASE(ui::mojom::DisplayManagerObserver) { public: // |delegate| can be nullptr. explicit ScreenMus(ScreenMusDelegate* delegate); @@ -36,38 +30,20 @@ class VIEWS_MUS_EXPORT ScreenMus void Init(shell::Connector* connector); private: - // Invoked when a display changed in some weay, including being added. - // If |is_primary| is true, |changed_display| is the primary display. - void ProcessDisplayChanged(const display::Display& changed_display, - bool is_primary); - // display::Screen: gfx::Point GetCursorScreenPoint() override; bool IsWindowUnderCursor(gfx::NativeWindow window) override; - gfx::NativeWindow GetWindowAtScreenPoint(const gfx::Point& point) override; - display::Display GetPrimaryDisplay() const override; - display::Display GetDisplayNearestWindow(gfx::NativeView view) const override; - display::Display GetDisplayNearestPoint( - const gfx::Point& point) const override; - int GetNumDisplays() const override; - std::vector<display::Display> GetAllDisplays() const override; - display::Display GetDisplayMatching( - const gfx::Rect& match_rect) const override; - void AddObserver(display::DisplayObserver* observer) override; - void RemoveObserver(display::DisplayObserver* observer) override; - // mus::mojom::DisplayManager: - void OnDisplays( - mojo::Array<mus::mojom::DisplayPtr> transport_displays) override; + // ui::mojom::DisplayManager: + void OnDisplays(mojo::Array<ui::mojom::WsDisplayPtr> ws_displays) override; void OnDisplaysChanged( - mojo::Array<mus::mojom::DisplayPtr> transport_displays) override; + mojo::Array<ui::mojom::WsDisplayPtr> ws_displays) override; void OnDisplayRemoved(int64_t id) override; ScreenMusDelegate* delegate_; // Can be nullptr. - mus::mojom::DisplayManagerPtr display_manager_; - mojo::Binding<mus::mojom::DisplayManagerObserver> + ui::mojom::DisplayManagerPtr display_manager_; + mojo::Binding<ui::mojom::DisplayManagerObserver> display_manager_observer_binding_; - DisplayList display_list_; DISALLOW_COPY_AND_ASSIGN(ScreenMus); }; diff --git a/chromium/ui/views/mus/screen_mus_delegate.h b/chromium/ui/views/mus/screen_mus_delegate.h index 1db1b67faaa..2ae48373b52 100644 --- a/chromium/ui/views/mus/screen_mus_delegate.h +++ b/chromium/ui/views/mus/screen_mus_delegate.h @@ -13,7 +13,7 @@ class Point; namespace views { -// Screen implementation backed by mus::mojom::DisplayManager. +// Screen implementation backed by ui::mojom::DisplayManager. class VIEWS_MUS_EXPORT ScreenMusDelegate { public: virtual void OnWindowManagerFrameValuesChanged() = 0; diff --git a/chromium/ui/views/mus/surface_binding.cc b/chromium/ui/views/mus/surface_binding.cc deleted file mode 100644 index fb0e3818bb5..00000000000 --- a/chromium/ui/views/mus/surface_binding.cc +++ /dev/null @@ -1,128 +0,0 @@ -// Copyright 2015 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/mus/surface_binding.h" - -#include <stdint.h> - -#include <map> -#include <utility> - -#include "base/bind.h" -#include "base/lazy_instance.h" -#include "base/macros.h" -#include "base/memory/ptr_util.h" -#include "base/threading/thread_local.h" -#include "cc/output/compositor_frame.h" -#include "cc/output/output_surface.h" -#include "cc/output/output_surface_client.h" -#include "cc/output/software_output_device.h" -#include "cc/resources/shared_bitmap_manager.h" -#include "components/mus/public/cpp/context_provider.h" -#include "components/mus/public/cpp/output_surface.h" -#include "components/mus/public/cpp/window.h" -#include "components/mus/public/cpp/window_tree_client.h" -#include "mojo/public/cpp/bindings/binding.h" -#include "ui/views/mus/window_tree_host_mus.h" - -namespace views { - -// PerClientState -------------------------------------------------------------- - -// State needed per WindowTreeClient. Provides the real implementation of -// CreateOutputSurface. SurfaceBinding obtains a pointer to the -// PerClientState appropriate for the WindowTreeClient. PerClientState is -// stored in a thread local map. When no more refereces to a PerClientState -// remain the PerClientState is deleted and the underlying map cleaned up. -class SurfaceBinding::PerClientState : public base::RefCounted<PerClientState> { - public: - static PerClientState* Get(shell::Connector* connector, - mus::WindowTreeClient* client); - - std::unique_ptr<cc::OutputSurface> CreateOutputSurface( - mus::Window* window, - mus::mojom::SurfaceType type); - - private: - typedef std::map<mus::WindowTreeClient*, PerClientState*> ClientToStateMap; - - friend class base::RefCounted<PerClientState>; - - PerClientState(shell::Connector* connector, - mus::WindowTreeClient* client); - ~PerClientState(); - - static base::LazyInstance< - base::ThreadLocalPointer<ClientToStateMap>>::Leaky window_states; - - shell::Connector* connector_; - mus::WindowTreeClient* client_; - - DISALLOW_COPY_AND_ASSIGN(PerClientState); -}; - -// static -base::LazyInstance<base::ThreadLocalPointer< - SurfaceBinding::PerClientState::ClientToStateMap>>::Leaky - SurfaceBinding::PerClientState::window_states; - -// static -SurfaceBinding::PerClientState* SurfaceBinding::PerClientState::Get( - shell::Connector* connector, - mus::WindowTreeClient* client) { - // |connector| can be null in some unit-tests. - if (!connector) - return nullptr; - ClientToStateMap* window_map = window_states.Pointer()->Get(); - if (!window_map) { - window_map = new ClientToStateMap; - window_states.Pointer()->Set(window_map); - } - if (!(*window_map)[client]) - (*window_map)[client] = new PerClientState(connector, client); - return (*window_map)[client]; -} - -std::unique_ptr<cc::OutputSurface> -SurfaceBinding::PerClientState::CreateOutputSurface( - mus::Window* window, - mus::mojom::SurfaceType surface_type) { - scoped_refptr<cc::ContextProvider> context_provider( - new mus::ContextProvider(connector_)); - return base::WrapUnique(new mus::OutputSurface( - context_provider, window->RequestSurface(surface_type))); -} - -SurfaceBinding::PerClientState::PerClientState( - shell::Connector* connector, - mus::WindowTreeClient* client) - : connector_(connector), client_(client) {} - -SurfaceBinding::PerClientState::~PerClientState() { - ClientToStateMap* window_map = window_states.Pointer()->Get(); - DCHECK(window_map); - DCHECK_EQ(this, (*window_map)[client_]); - window_map->erase(client_); - if (window_map->empty()) { - delete window_map; - window_states.Pointer()->Set(nullptr); - } -} - -// SurfaceBinding -------------------------------------------------------------- - -SurfaceBinding::SurfaceBinding(shell::Connector* connector, - mus::Window* window, - mus::mojom::SurfaceType surface_type) - : window_(window), - surface_type_(surface_type), - state_(PerClientState::Get(connector, window->window_tree())) {} - -SurfaceBinding::~SurfaceBinding() {} - -std::unique_ptr<cc::OutputSurface> SurfaceBinding::CreateOutputSurface() { - return state_ ? state_->CreateOutputSurface(window_, surface_type_) : nullptr; -} - -} // namespace views diff --git a/chromium/ui/views/mus/surface_binding.h b/chromium/ui/views/mus/surface_binding.h deleted file mode 100644 index 2ff6c76b6fa..00000000000 --- a/chromium/ui/views/mus/surface_binding.h +++ /dev/null @@ -1,57 +0,0 @@ -// Copyright 2015 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_MUS_SURFACE_BINDING_H_ -#define UI_VIEWS_MUS_SURFACE_BINDING_H_ - -#include <memory> - -#include "base/macros.h" -#include "base/memory/ref_counted.h" -#include "components/mus/public/interfaces/window_tree.mojom.h" -#include "ui/views/mus/mus_export.h" - -namespace cc { -class OutputSurface; -} - -namespace mus { -class Window; -} - -namespace shell { -class Connector; -} - -namespace views { - -// SurfaceBinding is responsible for managing the connections necessary to -// bind a Window to the surfaces service. -// Internally SurfaceBinding manages one connection (and related structures) per -// WindowTree. That is, all Windows from a particular WindowTree share the same -// connection. -class VIEWS_MUS_EXPORT SurfaceBinding { - public: - SurfaceBinding(shell::Connector* connector, - mus::Window* window, - mus::mojom::SurfaceType surface_type); - ~SurfaceBinding(); - - // Creates an OutputSurface that renders to the Window supplied to the - // constructor. - std::unique_ptr<cc::OutputSurface> CreateOutputSurface(); - - private: - class PerClientState; - - mus::Window* window_; - const mus::mojom::SurfaceType surface_type_; - scoped_refptr<PerClientState> state_; - - DISALLOW_COPY_AND_ASSIGN(SurfaceBinding); -}; - -} // namespace views - -#endif // UI_VIEWS_MUS_SURFACE_BINDING_H_ diff --git a/chromium/ui/views/mus/surface_context_factory.cc b/chromium/ui/views/mus/surface_context_factory.cc index 316d14e3c27..d5f9966bb77 100644 --- a/chromium/ui/views/mus/surface_context_factory.cc +++ b/chromium/ui/views/mus/surface_context_factory.cc @@ -5,13 +5,14 @@ #include "ui/views/mus/surface_context_factory.h" #include "base/memory/ptr_util.h" -#include "cc/output/output_surface.h" #include "cc/resources/shared_bitmap_manager.h" #include "cc/surfaces/surface_id_allocator.h" -#include "components/mus/public/cpp/window.h" -#include "services/shell/public/interfaces/connector.mojom.h" +#include "services/ui/public/cpp/compositor_frame_sink.h" +#include "services/ui/public/cpp/gpu_service.h" +#include "services/ui/public/cpp/window.h" #include "ui/compositor/reflector.h" #include "ui/gl/gl_bindings.h" +#include "ui/views/mus/native_widget_mus.h" namespace views { namespace { @@ -27,22 +28,20 @@ class FakeReflector : public ui::Reflector { } // namespace -SurfaceContextFactory::SurfaceContextFactory( - shell::Connector* connector, - mus::Window* window, - mus::mojom::SurfaceType surface_type) - : surface_binding_(connector, window, surface_type), - next_surface_id_namespace_(1u) {} +SurfaceContextFactory::SurfaceContextFactory(ui::GpuService* gpu_service) + : next_sink_id_(1u), gpu_service_(gpu_service) {} SurfaceContextFactory::~SurfaceContextFactory() {} -void SurfaceContextFactory::CreateOutputSurface( +void SurfaceContextFactory::CreateCompositorFrameSink( base::WeakPtr<ui::Compositor> compositor) { - // NOTIMPLEMENTED(); - std::unique_ptr<cc::OutputSurface> surface = - surface_binding_.CreateOutputSurface(); - if (surface) - compositor->SetOutputSurface(std::move(surface)); + ui::Window* window = compositor->window(); + NativeWidgetMus* native_widget = NativeWidgetMus::GetForWindow(window); + ui::mojom::SurfaceType surface_type = native_widget->surface_type(); + auto compositor_frame_sink = base::MakeUnique<ui::CompositorFrameSink>( + gpu_service_->EstablishGpuChannelSync(), + window->RequestSurface(surface_type)); + compositor->SetCompositorFrameSink(std::move(compositor_frame_sink)); } std::unique_ptr<ui::Reflector> SurfaceContextFactory::CreateReflector( @@ -83,27 +82,29 @@ cc::SharedBitmapManager* SurfaceContextFactory::GetSharedBitmapManager() { gpu::GpuMemoryBufferManager* SurfaceContextFactory::GetGpuMemoryBufferManager() { - return &gpu_memory_buffer_manager_; + return gpu_service_->gpu_memory_buffer_manager(); } cc::TaskGraphRunner* SurfaceContextFactory::GetTaskGraphRunner() { return raster_thread_helper_.task_graph_runner(); } -std::unique_ptr<cc::SurfaceIdAllocator> -SurfaceContextFactory::CreateSurfaceIdAllocator() { - return base::WrapUnique( - new cc::SurfaceIdAllocator(next_surface_id_namespace_++)); +cc::FrameSinkId SurfaceContextFactory::AllocateFrameSinkId() { + return cc::FrameSinkId(0, next_sink_id_++); } cc::SurfaceManager* SurfaceContextFactory::GetSurfaceManager() { - // NOTIMPLEMENTED(); - return nullptr; + return &surface_manager_; +} + +void SurfaceContextFactory::SetDisplayVisible(ui::Compositor* compositor, + bool visible) { + // TODO(fsamuel): display[compositor]->SetVisible(visible); } void SurfaceContextFactory::ResizeDisplay(ui::Compositor* compositor, const gfx::Size& size) { - // NOTIMPLEMENTED(); + // TODO(fsamuel): display[compositor]->Resize(size); } } // namespace views diff --git a/chromium/ui/views/mus/surface_context_factory.h b/chromium/ui/views/mus/surface_context_factory.h index 740d856cf19..d7bfe42b5e8 100644 --- a/chromium/ui/views/mus/surface_context_factory.h +++ b/chromium/ui/views/mus/surface_context_factory.h @@ -8,33 +8,27 @@ #include <stdint.h> #include "base/macros.h" -#include "components/mus/common/mojo_gpu_memory_buffer_manager.h" -#include "components/mus/gles2/raster_thread_helper.h" -#include "components/mus/public/interfaces/window_tree.mojom.h" +#include "cc/surfaces/surface_manager.h" +#include "services/ui/public/cpp/raster_thread_helper.h" +#include "services/ui/public/interfaces/window_tree.mojom.h" #include "ui/compositor/compositor.h" #include "ui/views/mus/mus_export.h" -#include "ui/views/mus/surface_binding.h" -namespace mojo { -class Connector; -} - -namespace mus { -class Window; +namespace ui { +class GpuService; } namespace views { class VIEWS_MUS_EXPORT SurfaceContextFactory : public ui::ContextFactory { public: - SurfaceContextFactory(shell::Connector* connector, - mus::Window* window, - mus::mojom::SurfaceType surface_type); + explicit SurfaceContextFactory(ui::GpuService* gpu_service); ~SurfaceContextFactory() override; private: // ContextFactory: - void CreateOutputSurface(base::WeakPtr<ui::Compositor> compositor) override; + void CreateCompositorFrameSink( + base::WeakPtr<ui::Compositor> compositor) override; std::unique_ptr<ui::Reflector> CreateReflector( ui::Compositor* mirrored_compositor, ui::Layer* mirroring_layer) override; @@ -47,22 +41,26 @@ class VIEWS_MUS_EXPORT SurfaceContextFactory : public ui::ContextFactory { cc::SharedBitmapManager* GetSharedBitmapManager() override; gpu::GpuMemoryBufferManager* GetGpuMemoryBufferManager() override; cc::TaskGraphRunner* GetTaskGraphRunner() override; - std::unique_ptr<cc::SurfaceIdAllocator> CreateSurfaceIdAllocator() override; + cc::FrameSinkId AllocateFrameSinkId() override; cc::SurfaceManager* GetSurfaceManager() override; + void SetDisplayVisible(ui::Compositor* compositor, bool visible) override; void ResizeDisplay(ui::Compositor* compositor, const gfx::Size& size) override; void SetDisplayColorSpace(ui::Compositor* compositor, const gfx::ColorSpace& color_space) override {} void SetAuthoritativeVSyncInterval(ui::Compositor* compositor, base::TimeDelta interval) override {} + void SetDisplayVSyncParameters(ui::Compositor* compositor, + base::TimeTicks timebase, + base::TimeDelta interval) override {} void SetOutputIsSecure(ui::Compositor* compositor, bool secure) override {} void AddObserver(ui::ContextFactoryObserver* observer) override {} void RemoveObserver(ui::ContextFactoryObserver* observer) override {} - SurfaceBinding surface_binding_; - uint32_t next_surface_id_namespace_; - gles2::RasterThreadHelper raster_thread_helper_; - mus::MojoGpuMemoryBufferManager gpu_memory_buffer_manager_; + cc::SurfaceManager surface_manager_; + uint32_t next_sink_id_; + ui::RasterThreadHelper raster_thread_helper_; + ui::GpuService* gpu_service_; DISALLOW_COPY_AND_ASSIGN(SurfaceContextFactory); }; diff --git a/chromium/ui/views/mus/text_input_client_impl.cc b/chromium/ui/views/mus/text_input_client_impl.cc new file mode 100644 index 00000000000..59b4b995c66 --- /dev/null +++ b/chromium/ui/views/mus/text_input_client_impl.cc @@ -0,0 +1,55 @@ +// Copyright 2016 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/mus/text_input_client_impl.h" + +#include "ui/base/ime/text_input_client.h" +#include "ui/views/mus/input_method_mus.h" + +namespace views { + +TextInputClientImpl::TextInputClientImpl(ui::TextInputClient* text_input_client, + InputMethodMus* input_method) + : text_input_client_(text_input_client), + input_method_(input_method), + binding_(this) {} + +TextInputClientImpl::~TextInputClientImpl() {} + +ui::mojom::TextInputClientPtr TextInputClientImpl::CreateInterfacePtrAndBind() { + return binding_.CreateInterfacePtrAndBind(); +} + +void TextInputClientImpl::OnCompositionEvent( + ui::mojom::CompositionEventPtr event) { + switch (event->type) { + case ui::mojom::CompositionEventType::INSERT_CHAR: { + DCHECK((*event->key_event)->IsKeyEvent()); + ui::KeyEvent* key_event = (*event->key_event)->AsKeyEvent(); + DCHECK(key_event->is_char()); + text_input_client_->InsertChar(*key_event); + break; + } + case ui::mojom::CompositionEventType::CONFIRM: + text_input_client_->ConfirmCompositionText(); + break; + case ui::mojom::CompositionEventType::CLEAR: + text_input_client_->ClearCompositionText(); + break; + case ui::mojom::CompositionEventType::UPDATE: + case ui::mojom::CompositionEventType::INSERT_TEXT: + // TODO(moshayedi): crbug.com/631524. Implement these types of composition + // events once we have the necessary fields in ui.mojom.CompositionEvent. + NOTIMPLEMENTED(); + break; + } +} + +void TextInputClientImpl::OnUnhandledEvent( + std::unique_ptr<ui::Event> key_event) { + DCHECK(key_event && key_event->IsKeyEvent()); + input_method_->DispatchKeyEventPostIME(key_event->AsKeyEvent()); +} + +} // namespace views diff --git a/chromium/ui/views/mus/text_input_client_impl.h b/chromium/ui/views/mus/text_input_client_impl.h new file mode 100644 index 00000000000..cb51a6cd913 --- /dev/null +++ b/chromium/ui/views/mus/text_input_client_impl.h @@ -0,0 +1,43 @@ +// Copyright 2016 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_MUS_TEXT_INPUT_CLIENT_IMPL_H_ +#define UI_VIEWS_MUS_TEXT_INPUT_CLIENT_IMPL_H_ + +#include "mojo/public/cpp/bindings/binding.h" +#include "services/ui/public/interfaces/ime.mojom.h" + +namespace ui { +class TextInputClient; +} + +namespace views { + +class InputMethodMus; + +// TextInputClientImpl receieves updates from IME drivers over Mojo IPC, and +// notifies the underlying ui::TextInputClient accordingly. +class TextInputClientImpl : public ui::mojom::TextInputClient { + public: + TextInputClientImpl(ui::TextInputClient* text_input_client, + InputMethodMus* input_method); + ~TextInputClientImpl() override; + + ui::mojom::TextInputClientPtr CreateInterfacePtrAndBind(); + + private: + // ui::mojom::TextInputClient: + void OnCompositionEvent(ui::mojom::CompositionEventPtr event) override; + void OnUnhandledEvent(std::unique_ptr<ui::Event> key_event) override; + + ui::TextInputClient* text_input_client_; + InputMethodMus* input_method_; + mojo::Binding<ui::mojom::TextInputClient> binding_; + + DISALLOW_COPY_AND_ASSIGN(TextInputClientImpl); +}; + +} // namespace views + +#endif // UI_VIEWS_MUS_TEXT_INPUT_CLIENT_IMPL_H_ diff --git a/chromium/ui/views/mus/views_mus_test_suite.cc b/chromium/ui/views/mus/views_mus_test_suite.cc index 2257b8e8640..8b10feb0cbf 100644 --- a/chromium/ui/views/mus/views_mus_test_suite.cc +++ b/chromium/ui/views/mus/views_mus_test_suite.cc @@ -5,6 +5,7 @@ #include "ui/views/mus/views_mus_test_suite.h" #include <memory> +#include <string> #include "base/command_line.h" #include "base/files/file_path.h" @@ -12,12 +13,11 @@ #include "base/synchronization/waitable_event.h" #include "base/threading/simple_thread.h" #include "base/threading/thread.h" -#include "components/mus/common/gpu_service.h" -#include "components/mus/common/switches.h" #include "services/shell/background/background_shell.h" #include "services/shell/public/cpp/connector.h" -#include "services/shell/public/cpp/shell_client.h" -#include "services/shell/public/cpp/shell_connection.h" +#include "services/shell/public/cpp/service.h" +#include "services/shell/public/cpp/service_context.h" +#include "services/ui/common/switches.h" #include "testing/gtest/include/gtest/gtest.h" #include "ui/views/mus/window_manager_connection.h" #include "ui/views/test/platform_test_helper.h" @@ -32,27 +32,24 @@ void EnsureCommandLineSwitch(const std::string& name) { cmd_line->AppendSwitch(name); } -class DefaultShellClient : public shell::ShellClient { +class DefaultService : public shell::Service { public: - DefaultShellClient() {} - ~DefaultShellClient() override {} + DefaultService() {} + ~DefaultService() override {} private: - DISALLOW_COPY_AND_ASSIGN(DefaultShellClient); + DISALLOW_COPY_AND_ASSIGN(DefaultService); }; class PlatformTestHelperMus : public PlatformTestHelper { public: PlatformTestHelperMus(shell::Connector* connector, const shell::Identity& identity) { - mus::GpuService::Initialize(connector); // It is necessary to recreate the WindowManagerConnection for each test, // since a new MessageLoop is created for each test. connection_ = WindowManagerConnection::Create(connector, identity); } - ~PlatformTestHelperMus() override { - mus::GpuService::Terminate(); - } + ~PlatformTestHelperMus() override {} private: std::unique_ptr<WindowManagerConnection> connection_; @@ -63,7 +60,7 @@ class PlatformTestHelperMus : public PlatformTestHelper { std::unique_ptr<PlatformTestHelper> CreatePlatformTestHelper( const shell::Identity& identity, const base::Callback<shell::Connector*(void)>& callback) { - return base::WrapUnique(new PlatformTestHelperMus(callback.Run(), identity)); + return base::MakeUnique<PlatformTestHelperMus>(callback.Run(), identity); } } // namespace @@ -118,16 +115,15 @@ class ShellConnection { } void SetUpConnections(base::WaitableEvent* wait) { - background_shell_.reset(new shell::BackgroundShell); + background_shell_ = base::MakeUnique<shell::BackgroundShell>(); background_shell_->Init(nullptr); - shell_client_.reset(new DefaultShellClient); - shell_connection_.reset(new shell::ShellConnection( - shell_client_.get(), - background_shell_->CreateShellClientRequest(GetTestName()))); + service_ = base::MakeUnique<DefaultService>(); + shell_connection_ = base::MakeUnique<shell::ServiceContext>( + service_.get(), background_shell_->CreateServiceRequest(GetTestName())); // ui/views/mus requires a WindowManager running, so launch test_wm. shell::Connector* connector = shell_connection_->connector(); - connector->Connect("mojo:test_wm"); + connector->Connect("service:test_wm"); shell_connector_ = connector->Clone(); shell_identity_ = shell_connection_->identity(); wait->Signal(); @@ -149,8 +145,8 @@ class ShellConnection { base::Thread thread_; std::unique_ptr<shell::BackgroundShell> background_shell_; - std::unique_ptr<shell::ShellConnection> shell_connection_; - std::unique_ptr<DefaultShellClient> shell_client_; + std::unique_ptr<shell::ServiceContext> shell_connection_; + std::unique_ptr<DefaultService> service_; std::unique_ptr<shell::Connector> shell_connector_; shell::Identity shell_identity_; @@ -167,10 +163,10 @@ void ViewsMusTestSuite::Initialize() { // Let other mojo apps know that we're running in tests. Do this with a // command line flag to avoid making blocking calls to other processes for // setup for tests (e.g. to unlock the screen in the window manager). - EnsureCommandLineSwitch(mus::switches::kUseTestConfig); + EnsureCommandLineSwitch(ui::switches::kUseTestConfig); ViewsTestSuite::Initialize(); - shell_connections_.reset(new ShellConnection); + shell_connections_ = base::MakeUnique<ShellConnection>(); } void ViewsMusTestSuite::Shutdown() { diff --git a/chromium/ui/views/mus/window_manager_connection.cc b/chromium/ui/views/mus/window_manager_connection.cc index 4d1fe3bc11b..8c4cf85d690 100644 --- a/chromium/ui/views/mus/window_manager_connection.cc +++ b/chromium/ui/views/mus/window_manager_connection.cc @@ -4,21 +4,28 @@ #include "ui/views/mus/window_manager_connection.h" +#include <set> #include <utility> #include "base/lazy_instance.h" +#include "base/memory/ptr_util.h" #include "base/threading/thread_local.h" -#include "components/mus/public/cpp/property_type_converters.h" -#include "components/mus/public/cpp/window.h" -#include "components/mus/public/cpp/window_property.h" -#include "components/mus/public/cpp/window_tree_client.h" -#include "components/mus/public/interfaces/event_matcher.mojom.h" -#include "components/mus/public/interfaces/window_tree.mojom.h" #include "services/shell/public/cpp/connection.h" #include "services/shell/public/cpp/connector.h" +#include "services/ui/public/cpp/gpu_service.h" +#include "services/ui/public/cpp/property_type_converters.h" +#include "services/ui/public/cpp/window.h" +#include "services/ui/public/cpp/window_property.h" +#include "services/ui/public/cpp/window_tree_client.h" +#include "services/ui/public/interfaces/event_matcher.mojom.h" +#include "services/ui/public/interfaces/window_tree.mojom.h" +#include "ui/aura/env.h" #include "ui/views/mus/clipboard_mus.h" #include "ui/views/mus/native_widget_mus.h" +#include "ui/views/mus/os_exchange_data_provider_mus.h" +#include "ui/views/mus/pointer_watcher_event_router.h" #include "ui/views/mus/screen_mus.h" +#include "ui/views/mus/surface_context_factory.h" #include "ui/views/pointer_watcher.h" #include "ui/views/views_delegate.h" @@ -34,13 +41,29 @@ base::LazyInstance<WindowManagerConnectionPtr>::Leaky lazy_tls_ptr = } // namespace +WindowManagerConnection::~WindowManagerConnection() { + // ~WindowTreeClient calls back to us (we're its delegate), destroy it while + // we are still valid. + client_.reset(); + ui::OSExchangeDataProviderFactory::SetFactory(nullptr); + ui::Clipboard::DestroyClipboardForCurrentThread(); + gpu_service_.reset(); + lazy_tls_ptr.Pointer()->Set(nullptr); + + if (ViewsDelegate::GetInstance()) { + ViewsDelegate::GetInstance()->set_native_widget_factory( + ViewsDelegate::NativeWidgetFactory()); + } +} + // static std::unique_ptr<WindowManagerConnection> WindowManagerConnection::Create( shell::Connector* connector, - const shell::Identity& identity) { + const shell::Identity& identity, + scoped_refptr<base::SingleThreadTaskRunner> task_runner) { DCHECK(!lazy_tls_ptr.Pointer()->Get()); WindowManagerConnection* connection = - new WindowManagerConnection(connector, identity); + new WindowManagerConnection(connector, identity, std::move(task_runner)); DCHECK(lazy_tls_ptr.Pointer()->Get()); return base::WrapUnique(connection); } @@ -57,7 +80,7 @@ bool WindowManagerConnection::Exists() { return !!lazy_tls_ptr.Pointer()->Get(); } -mus::Window* WindowManagerConnection::NewWindow( +ui::Window* WindowManagerConnection::NewWindow( const std::map<std::string, std::vector<uint8_t>>& properties) { return client_->NewTopLevelWindow(&properties); } @@ -73,109 +96,66 @@ NativeWidget* WindowManagerConnection::CreateNativeWidgetMus( return nullptr; std::map<std::string, std::vector<uint8_t>> properties = props; NativeWidgetMus::ConfigurePropertiesForNewWindow(init_params, &properties); - properties[mus::mojom::WindowManager::kAppID_Property] = + properties[ui::mojom::WindowManager::kAppID_Property] = mojo::ConvertTo<std::vector<uint8_t>>(identity_.name()); - return new NativeWidgetMus(delegate, connector_, NewWindow(properties), - mus::mojom::SurfaceType::DEFAULT); -} - -void WindowManagerConnection::AddPointerWatcher(PointerWatcher* watcher) { - bool had_watcher = HasPointerWatcher(); - pointer_watchers_.AddObserver(watcher); - if (!had_watcher) { - // Start a watcher for pointer down. - // TODO(jamescook): Extend event observers to handle multiple event types. - mus::mojom::EventMatcherPtr matcher = mus::mojom::EventMatcher::New(); - matcher->type_matcher = mus::mojom::EventTypeMatcher::New(); - matcher->type_matcher->type = ui::mojom::EventType::POINTER_DOWN; - client_->SetEventObserver(std::move(matcher)); - } + return new NativeWidgetMus(delegate, NewWindow(properties), + ui::mojom::SurfaceType::DEFAULT); } -void WindowManagerConnection::RemovePointerWatcher(PointerWatcher* watcher) { - pointer_watchers_.RemoveObserver(watcher); - if (!HasPointerWatcher()) { - // Last PointerWatcher removed, stop the event observer. - client_->SetEventObserver(nullptr); - } +const std::set<ui::Window*>& WindowManagerConnection::GetRoots() const { + return client_->GetRoots(); } WindowManagerConnection::WindowManagerConnection( shell::Connector* connector, - const shell::Identity& identity) + const shell::Identity& identity, + scoped_refptr<base::SingleThreadTaskRunner> task_runner) : connector_(connector), identity_(identity) { lazy_tls_ptr.Pointer()->Set(this); - client_.reset(new mus::WindowTreeClient(this, nullptr, nullptr)); + + gpu_service_ = ui::GpuService::Create(connector, std::move(task_runner)); + compositor_context_factory_ = + base::MakeUnique<views::SurfaceContextFactory>(gpu_service_.get()); + aura::Env::GetInstance()->set_context_factory( + compositor_context_factory_.get()); + client_ = base::MakeUnique<ui::WindowTreeClient>(this, nullptr, nullptr); client_->ConnectViaWindowTreeFactory(connector_); - screen_.reset(new ScreenMus(this)); + pointer_watcher_event_router_ = + base::MakeUnique<PointerWatcherEventRouter>(client_.get()); + + screen_ = base::MakeUnique<ScreenMus>(this); screen_->Init(connector); - std::unique_ptr<ClipboardMus> clipboard(new ClipboardMus); + std::unique_ptr<ClipboardMus> clipboard = base::MakeUnique<ClipboardMus>(); clipboard->Init(connector); ui::Clipboard::SetClipboardForCurrentThread(std::move(clipboard)); + ui::OSExchangeDataProviderFactory::SetFactory(this); + ViewsDelegate::GetInstance()->set_native_widget_factory(base::Bind( &WindowManagerConnection::CreateNativeWidgetMus, base::Unretained(this), std::map<std::string, std::vector<uint8_t>>())); } -WindowManagerConnection::~WindowManagerConnection() { - // ~WindowTreeClient calls back to us (we're its delegate), destroy it while - // we are still valid. - client_.reset(); - ui::Clipboard::DestroyClipboardForCurrentThread(); - lazy_tls_ptr.Pointer()->Set(nullptr); - - if (ViewsDelegate::GetInstance()) { - ViewsDelegate::GetInstance()->set_native_widget_factory( - ViewsDelegate::NativeWidgetFactory()); - } -} +void WindowManagerConnection::OnEmbed(ui::Window* root) {} -bool WindowManagerConnection::HasPointerWatcher() { - // Check to see if we really have any observers left. This doesn't use - // base::ObserverList<>::might_have_observers() because that returns true - // during iteration over the list even when the last observer is removed. - base::ObserverList<PointerWatcher>::Iterator iterator(&pointer_watchers_); - return !!iterator.GetNext(); +void WindowManagerConnection::OnLostConnection(ui::WindowTreeClient* client) { + DCHECK_EQ(client, client_.get()); + client_.reset(); } -void WindowManagerConnection::OnEmbed(mus::Window* root) {} - -void WindowManagerConnection::OnWindowTreeClientDestroyed( - mus::WindowTreeClient* client) { - if (client_.get() == client) { - client_.release(); - } else { - DCHECK(!client_); - } +void WindowManagerConnection::OnEmbedRootDestroyed(ui::Window* root) { + // Not called for WindowManagerConnection as WindowTreeClient isn't created by + // way of an Embed(). + NOTREACHED(); } -void WindowManagerConnection::OnEventObserved(const ui::Event& event, - mus::Window* target) { - if (!event.IsLocatedEvent()) - return; - Widget* target_widget = nullptr; - if (target) { - mus::Window* root = target->GetRoot(); - target_widget = NativeWidgetMus::GetWidgetForWindow(root); - } - - // The mojo input events type converter uses the event root_location field - // to store screen coordinates. Screen coordinates really should be returned - // separately. See http://crbug.com/608547 - gfx::Point location_in_screen = event.AsLocatedEvent()->root_location(); - if (event.type() == ui::ET_MOUSE_PRESSED) { - FOR_EACH_OBSERVER(PointerWatcher, pointer_watchers_, - OnMousePressed(*event.AsMouseEvent(), location_in_screen, - target_widget)); - } else if (event.type() == ui::ET_TOUCH_PRESSED) { - FOR_EACH_OBSERVER(PointerWatcher, pointer_watchers_, - OnTouchPressed(*event.AsTouchEvent(), location_in_screen, - target_widget)); - } +void WindowManagerConnection::OnPointerEventObserved( + const ui::PointerEvent& event, + ui::Window* target) { + pointer_watcher_event_router_->OnPointerEventObserved(event, target); } void WindowManagerConnection::OnWindowManagerFrameValuesChanged() { @@ -187,4 +167,9 @@ gfx::Point WindowManagerConnection::GetCursorScreenPoint() { return client_->GetCursorScreenPoint(); } +std::unique_ptr<OSExchangeData::Provider> +WindowManagerConnection::BuildProvider() { + return base::MakeUnique<OSExchangeDataProviderMus>(); +} + } // namespace views diff --git a/chromium/ui/views/mus/window_manager_connection.h b/chromium/ui/views/mus/window_manager_connection.h index 8af483995bc..cad161ac061 100644 --- a/chromium/ui/views/mus/window_manager_connection.h +++ b/chromium/ui/views/mus/window_manager_connection.h @@ -13,9 +13,9 @@ #include <vector> #include "base/macros.h" -#include "base/observer_list.h" -#include "components/mus/public/cpp/window_tree_client_delegate.h" #include "services/shell/public/cpp/identity.h" +#include "services/ui/public/cpp/window_tree_client_delegate.h" +#include "ui/base/dragdrop/os_exchange_data_provider_factory.h" #include "ui/views/mus/mus_export.h" #include "ui/views/mus/screen_mus_delegate.h" #include "ui/views/widget/widget.h" @@ -24,11 +24,17 @@ namespace shell { class Connector; } +namespace ui { +class GpuService; +} + namespace views { class ClipboardMus; class NativeWidget; class PointerWatcher; +class PointerWatcherEventRouter; class ScreenMus; +class SurfaceContextFactory; namespace internal { class NativeWidgetDelegate; } @@ -43,54 +49,65 @@ class NativeWidgetDelegate; // // TODO(sky): this name is now totally confusing. Come up with a better one. class VIEWS_MUS_EXPORT WindowManagerConnection - : public NON_EXPORTED_BASE(mus::WindowTreeClientDelegate), - public ScreenMusDelegate { + : public NON_EXPORTED_BASE(ui::WindowTreeClientDelegate), + public ScreenMusDelegate, + public ui::OSExchangeDataProviderFactory::Factory { public: + ~WindowManagerConnection() override; + + // |io_task_runner| is used by the gpu service. If no task runner is provided, + // then a new thread is created and used by ui::GpuService. static std::unique_ptr<WindowManagerConnection> Create( shell::Connector* connector, - const shell::Identity& identity); + const shell::Identity& identity, + scoped_refptr<base::SingleThreadTaskRunner> io_task_runner = nullptr); static WindowManagerConnection* Get(); static bool Exists(); - ~WindowManagerConnection() override; - + PointerWatcherEventRouter* pointer_watcher_event_router() { + return pointer_watcher_event_router_.get(); + } shell::Connector* connector() { return connector_; } + ui::GpuService* gpu_service() { return gpu_service_.get(); } + ui::WindowTreeClient* client() { return client_.get(); } - mus::Window* NewWindow(const std::map<std::string, - std::vector<uint8_t>>& properties); + ui::Window* NewWindow( + const std::map<std::string, std::vector<uint8_t>>& properties); NativeWidget* CreateNativeWidgetMus( const std::map<std::string, std::vector<uint8_t>>& properties, const Widget::InitParams& init_params, internal::NativeWidgetDelegate* delegate); - void AddPointerWatcher(PointerWatcher* watcher); - void RemovePointerWatcher(PointerWatcher* watcher); + const std::set<ui::Window*>& GetRoots() const; private: - friend class WindowManagerConnectionTest; - - WindowManagerConnection(shell::Connector* connector, - const shell::Identity& identity); - - // Returns true if there is one or more pointer watchers for this client. - bool HasPointerWatcher(); + WindowManagerConnection( + shell::Connector* connector, + const shell::Identity& identity, + scoped_refptr<base::SingleThreadTaskRunner> task_runner); - // mus::WindowTreeClientDelegate: - void OnEmbed(mus::Window* root) override; - void OnWindowTreeClientDestroyed(mus::WindowTreeClient* client) override; - void OnEventObserved(const ui::Event& event, mus::Window* target) override; + // ui::WindowTreeClientDelegate: + void OnEmbed(ui::Window* root) override; + void OnLostConnection(ui::WindowTreeClient* client) override; + void OnEmbedRootDestroyed(ui::Window* root) override; + void OnPointerEventObserved(const ui::PointerEvent& event, + ui::Window* target) override; // ScreenMusDelegate: void OnWindowManagerFrameValuesChanged() override; gfx::Point GetCursorScreenPoint() override; + // ui:OSExchangeDataProviderFactory::Factory: + std::unique_ptr<OSExchangeData::Provider> BuildProvider() override; + shell::Connector* connector_; shell::Identity identity_; std::unique_ptr<ScreenMus> screen_; - std::unique_ptr<mus::WindowTreeClient> client_; - // Must be empty on destruction. - base::ObserverList<PointerWatcher, true> pointer_watchers_; + std::unique_ptr<ui::WindowTreeClient> client_; + std::unique_ptr<ui::GpuService> gpu_service_; + std::unique_ptr<PointerWatcherEventRouter> pointer_watcher_event_router_; + std::unique_ptr<SurfaceContextFactory> compositor_context_factory_; DISALLOW_COPY_AND_ASSIGN(WindowManagerConnection); }; diff --git a/chromium/ui/views/mus/window_manager_connection_unittest.cc b/chromium/ui/views/mus/window_manager_connection_unittest.cc deleted file mode 100644 index 967b5ad83d0..00000000000 --- a/chromium/ui/views/mus/window_manager_connection_unittest.cc +++ /dev/null @@ -1,118 +0,0 @@ -// Copyright 2016 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/mus/window_manager_connection.h" - -#include <memory> - -#include "base/message_loop/message_loop.h" -#include "testing/gtest/include/gtest/gtest.h" -#include "ui/events/event.h" -#include "ui/views/pointer_watcher.h" -#include "ui/views/test/scoped_views_test_helper.h" - -namespace views { -namespace { - -class TestPointerWatcher : public PointerWatcher { - public: - TestPointerWatcher() {} - ~TestPointerWatcher() override {} - - bool mouse_pressed() const { return mouse_pressed_; } - bool touch_pressed() const { return touch_pressed_; } - - void Reset() { - mouse_pressed_ = false; - touch_pressed_ = false; - } - - // PointerWatcher: - void OnMousePressed(const ui::MouseEvent& event, - const gfx::Point& location_in_screen, - Widget* target) override { - mouse_pressed_ = true; - } - void OnTouchPressed(const ui::TouchEvent& event, - const gfx::Point& location_in_screen, - Widget* target) override { - touch_pressed_ = true; - } - - private: - bool mouse_pressed_ = false; - bool touch_pressed_ = false; - - DISALLOW_COPY_AND_ASSIGN(TestPointerWatcher); -}; - -} // namespace - -class WindowManagerConnectionTest : public testing::Test { - public: - WindowManagerConnectionTest() {} - ~WindowManagerConnectionTest() override {} - - void OnEventObserved(const ui::Event& event) { - WindowManagerConnection::Get()->OnEventObserved(event, nullptr); - } - - private: - DISALLOW_COPY_AND_ASSIGN(WindowManagerConnectionTest); -}; - -TEST_F(WindowManagerConnectionTest, PointerWatcher) { - base::MessageLoop message_loop(base::MessageLoop::TYPE_UI); - ScopedViewsTestHelper helper; - WindowManagerConnection* connection = WindowManagerConnection::Get(); - ASSERT_TRUE(connection); - ui::MouseEvent mouse_pressed(ui::ET_MOUSE_PRESSED, gfx::Point(), gfx::Point(), - base::TimeTicks(), ui::EF_NONE, 0); - ui::TouchEvent touch_pressed(ui::ET_TOUCH_PRESSED, gfx::Point(), 1, - base::TimeTicks()); - ui::KeyEvent key_pressed(ui::ET_KEY_PRESSED, ui::VKEY_A, 0); - - // PointerWatchers receive mouse events. - TestPointerWatcher watcher1; - connection->AddPointerWatcher(&watcher1); - OnEventObserved(mouse_pressed); - EXPECT_TRUE(watcher1.mouse_pressed()); - watcher1.Reset(); - - // PointerWatchers receive touch events. - OnEventObserved(touch_pressed); - EXPECT_TRUE(watcher1.touch_pressed()); - watcher1.Reset(); - - // PointerWatchers do not trigger for key events. - OnEventObserved(key_pressed); - EXPECT_FALSE(watcher1.mouse_pressed()); - EXPECT_FALSE(watcher1.touch_pressed()); - watcher1.Reset(); - - // Two PointerWatchers can both receive a single observed event. - TestPointerWatcher watcher2; - connection->AddPointerWatcher(&watcher2); - OnEventObserved(mouse_pressed); - EXPECT_TRUE(watcher1.mouse_pressed()); - EXPECT_TRUE(watcher2.mouse_pressed()); - watcher1.Reset(); - watcher2.Reset(); - - // Removing the first PointerWatcher stops sending events to it. - connection->RemovePointerWatcher(&watcher1); - OnEventObserved(mouse_pressed); - EXPECT_FALSE(watcher1.mouse_pressed()); - EXPECT_TRUE(watcher2.mouse_pressed()); - watcher1.Reset(); - watcher2.Reset(); - - // Removing the last PointerWatcher stops sending events to it. - connection->RemovePointerWatcher(&watcher2); - OnEventObserved(mouse_pressed); - EXPECT_FALSE(watcher1.mouse_pressed()); - EXPECT_FALSE(watcher1.touch_pressed()); -} - -} // namespace views diff --git a/chromium/ui/views/mus/window_manager_constants_converters.cc b/chromium/ui/views/mus/window_manager_constants_converters.cc index 889737698f1..90ee4793112 100644 --- a/chromium/ui/views/mus/window_manager_constants_converters.cc +++ b/chromium/ui/views/mus/window_manager_constants_converters.cc @@ -7,30 +7,30 @@ namespace mojo { // static -mus::mojom::WindowType -TypeConverter<mus::mojom::WindowType, views::Widget::InitParams::Type>::Convert( +ui::mojom::WindowType +TypeConverter<ui::mojom::WindowType, views::Widget::InitParams::Type>::Convert( views::Widget::InitParams::Type type) { switch (type) { case views::Widget::InitParams::TYPE_WINDOW: - return mus::mojom::WindowType::WINDOW; + return ui::mojom::WindowType::WINDOW; case views::Widget::InitParams::TYPE_PANEL: - return mus::mojom::WindowType::PANEL; + return ui::mojom::WindowType::PANEL; case views::Widget::InitParams::TYPE_WINDOW_FRAMELESS: - return mus::mojom::WindowType::WINDOW_FRAMELESS; + return ui::mojom::WindowType::WINDOW_FRAMELESS; case views::Widget::InitParams::TYPE_CONTROL: - return mus::mojom::WindowType::CONTROL; + return ui::mojom::WindowType::CONTROL; case views::Widget::InitParams::TYPE_POPUP: - return mus::mojom::WindowType::POPUP; + return ui::mojom::WindowType::POPUP; case views::Widget::InitParams::TYPE_MENU: - return mus::mojom::WindowType::MENU; + return ui::mojom::WindowType::MENU; case views::Widget::InitParams::TYPE_TOOLTIP: - return mus::mojom::WindowType::TOOLTIP; + return ui::mojom::WindowType::TOOLTIP; case views::Widget::InitParams::TYPE_BUBBLE: - return mus::mojom::WindowType::BUBBLE; + return ui::mojom::WindowType::BUBBLE; case views::Widget::InitParams::TYPE_DRAG: - return mus::mojom::WindowType::DRAG; + return ui::mojom::WindowType::DRAG; } - return mus::mojom::WindowType::POPUP; + return ui::mojom::WindowType::POPUP; } } // namespace mojo diff --git a/chromium/ui/views/mus/window_manager_constants_converters.h b/chromium/ui/views/mus/window_manager_constants_converters.h index 0c73d9d8f58..1a93a030f9a 100644 --- a/chromium/ui/views/mus/window_manager_constants_converters.h +++ b/chromium/ui/views/mus/window_manager_constants_converters.h @@ -5,11 +5,11 @@ #ifndef UI_VIEWS_MUS_WINDOW_MANAGER_CONSTANTS_CONVERTERS_H_ #define UI_VIEWS_MUS_WINDOW_MANAGER_CONSTANTS_CONVERTERS_H_ -#include "components/mus/public/interfaces/window_manager_constants.mojom.h" +#include "services/ui/public/interfaces/window_manager_constants.mojom.h" #include "ui/views/mus/mus_export.h" #include "ui/views/widget/widget.h" -namespace mus { +namespace ui { class Window; } @@ -17,8 +17,8 @@ namespace mojo { template <> struct VIEWS_MUS_EXPORT - TypeConverter<mus::mojom::WindowType, views::Widget::InitParams::Type> { - static mus::mojom::WindowType Convert(views::Widget::InitParams::Type type); + TypeConverter<ui::mojom::WindowType, views::Widget::InitParams::Type> { + static ui::mojom::WindowType Convert(views::Widget::InitParams::Type type); }; } // namespace mojo diff --git a/chromium/ui/views/mus/window_tree_host_mus.cc b/chromium/ui/views/mus/window_tree_host_mus.cc index 4aed38c33de..caed894310e 100644 --- a/chromium/ui/views/mus/window_tree_host_mus.cc +++ b/chromium/ui/views/mus/window_tree_host_mus.cc @@ -5,7 +5,7 @@ #include "ui/views/mus/window_tree_host_mus.h" #include "base/memory/ptr_util.h" -#include "components/mus/public/cpp/window.h" +#include "services/ui/public/cpp/window.h" #include "ui/aura/window.h" #include "ui/aura/window_event_dispatcher.h" #include "ui/events/event.h" @@ -23,7 +23,7 @@ static uint32_t accelerated_widget_count = 1; // WindowTreeHostMus, public: WindowTreeHostMus::WindowTreeHostMus(NativeWidgetMus* native_widget, - mus::Window* window) + ui::Window* window) : native_widget_(native_widget) { // We need accelerated widget numbers to be different for each // window and fit in the smallest sizeof(AcceleratedWidget) uint32_t @@ -38,11 +38,13 @@ WindowTreeHostMus::WindowTreeHostMus(NativeWidgetMus* native_widget, // TODO(markdittmer): Use correct device-scale-factor from |window|. OnAcceleratedWidgetAvailable(accelerated_widget, 1.f); - SetPlatformWindow(base::WrapUnique(new ui::StubWindow( + SetPlatformWindow(base::MakeUnique<ui::StubWindow>( this, - false))); // Do not advertise accelerated widget; already set manually. + false)); // Do not advertise accelerated widget; already set manually. - // Initialize the stub platform window bounds to those of the mus::Window. + compositor()->SetWindow(window); + + // Initialize the stub platform window bounds to those of the ui::Window. platform_window()->SetBounds(window->bounds()); // The location of events is already transformed, and there is no way to @@ -52,7 +54,7 @@ WindowTreeHostMus::WindowTreeHostMus(NativeWidgetMus* native_widget, dispatcher()->set_transform_events(false); compositor()->SetHostHasTransparentBackground(true); - input_method_.reset(new InputMethodMUS(this, window)); + input_method_ = base::MakeUnique<InputMethodMus>(this, window); SetSharedInputMethod(input_method_.get()); } @@ -61,10 +63,13 @@ WindowTreeHostMus::~WindowTreeHostMus() { DestroyDispatcher(); } +void WindowTreeHostMus::InitInputMethod(shell::Connector* connector) { + input_method_->Init(connector); +} + void WindowTreeHostMus::DispatchEvent(ui::Event* event) { if (event->IsKeyEvent() && GetInputMethod()) { GetInputMethod()->DispatchKeyEvent(event->AsKeyEvent()); - event->StopPropagation(); return; } WindowTreeHostPlatform::DispatchEvent(event); @@ -89,4 +94,9 @@ void WindowTreeHostMus::OnCloseRequest() { OnHostCloseRequested(); } +gfx::ICCProfile WindowTreeHostMus::GetICCProfileForCurrentDisplay() { + // TODO: This should read the profile from mus. crbug.com/647510 + return gfx::ICCProfile(); +} + } // namespace views diff --git a/chromium/ui/views/mus/window_tree_host_mus.h b/chromium/ui/views/mus/window_tree_host_mus.h index 683e65faa1e..535aeb2fb07 100644 --- a/chromium/ui/views/mus/window_tree_host_mus.h +++ b/chromium/ui/views/mus/window_tree_host_mus.h @@ -6,12 +6,13 @@ #define UI_VIEWS_MUS_WINDOW_TREE_HOST_MUS_H_ #include "base/macros.h" +#include "services/shell/public/cpp/connector.h" #include "ui/aura/window_tree_host_platform.h" #include "ui/views/mus/mus_export.h" class SkBitmap; -namespace mus { +namespace ui { class Window; } @@ -21,14 +22,17 @@ class Connector; namespace views { -class InputMethodMUS; +class InputMethodMus; class NativeWidgetMus; class PlatformWindowMus; class VIEWS_MUS_EXPORT WindowTreeHostMus : public aura::WindowTreeHostPlatform { public: - WindowTreeHostMus(NativeWidgetMus* native_widget, mus::Window* window); + WindowTreeHostMus(NativeWidgetMus* native_widget, ui::Window* window); ~WindowTreeHostMus() override; + NativeWidgetMus* native_widget() { return native_widget_; } + + void InitInputMethod(shell::Connector* connector); private: // aura::WindowTreeHostPlatform: @@ -36,9 +40,10 @@ class VIEWS_MUS_EXPORT WindowTreeHostMus : public aura::WindowTreeHostPlatform { void OnClosed() override; void OnActivationChanged(bool active) override; void OnCloseRequest() override; + gfx::ICCProfile GetICCProfileForCurrentDisplay() override; NativeWidgetMus* native_widget_; - std::unique_ptr<InputMethodMUS> input_method_; + std::unique_ptr<InputMethodMus> input_method_; DISALLOW_COPY_AND_ASSIGN(WindowTreeHostMus); }; diff --git a/chromium/ui/views/painter.cc b/chromium/ui/views/painter.cc index c9cc6a64328..8d4fc45aa28 100644 --- a/chromium/ui/views/painter.cc +++ b/chromium/ui/views/painter.cc @@ -65,8 +65,7 @@ void SolidRoundRectPainter::Paint(gfx::Canvas* canvas, const gfx::Size& size) { gfx::ScopedCanvas scoped_canvas(canvas); const float scale = canvas->UndoDeviceScaleFactor(); - gfx::RectF border_rect_f((gfx::SizeF(size))); - border_rect_f.Scale(scale); + gfx::RectF border_rect_f(gfx::ScaleToEnclosingRect(gfx::Rect(size), scale)); const SkScalar scaled_corner_radius = SkFloatToScalar(radius_ * scale); SkPaint paint; @@ -344,20 +343,20 @@ Painter* Painter::CreateImageGridPainter(const int image_ids[]) { // static std::unique_ptr<Painter> Painter::CreateDashedFocusPainter() { - return base::WrapUnique(new DashedFocusPainter(gfx::Insets())); + return base::MakeUnique<DashedFocusPainter>(gfx::Insets()); } // static std::unique_ptr<Painter> Painter::CreateDashedFocusPainterWithInsets( const gfx::Insets& insets) { - return base::WrapUnique(new DashedFocusPainter(insets)); + return base::MakeUnique<DashedFocusPainter>(insets); } // static std::unique_ptr<Painter> Painter::CreateSolidFocusPainter( SkColor color, const gfx::Insets& insets) { - return base::WrapUnique(new SolidFocusPainter(color, insets)); + return base::MakeUnique<SolidFocusPainter>(color, insets); } // HorizontalPainter ---------------------------------------------------------- diff --git a/chromium/ui/views/pointer_watcher.h b/chromium/ui/views/pointer_watcher.h index 2abc4cb42d2..7a68216db7d 100644 --- a/chromium/ui/views/pointer_watcher.h +++ b/chromium/ui/views/pointer_watcher.h @@ -5,6 +5,7 @@ #ifndef UI_VIEWS_POINTER_WATCHER_H_ #define UI_VIEWS_POINTER_WATCHER_H_ +#include "base/macros.h" #include "ui/views/views_export.h" namespace gfx { @@ -12,29 +13,49 @@ class Point; } namespace ui { -class MouseEvent; -class TouchEvent; +class PointerEvent; } namespace views { class Widget; +// When a PointerWatcher is added the types of events desired is specified by +// way of PointerWatcherEventTypes. +enum class PointerWatcherEventTypes { + // The PointerWatcher is interested in press, release, capture and mouse + // wheel. + BASIC, + // The PointerWatcher is interested in BASIC events, as well as move + // events. + MOVES, + // The PointerWatcher is interested in MOVE events, as well as drag + // events. + DRAGS +}; + // An interface for read-only observation of pointer events (in particular, the // events cannot be marked as handled). Only certain event types are supported. // The |target| is the top-level widget that will receive the event, if any. +// To reduce IPC traffic from the window server, move events are not provided +// unless the app specifically requests them. // NOTE: On mus this allows observation of events outside of windows owned // by the current process, in which case the |target| will be null. On mus // event.target() is always null. +// NOTE: Mouse capture change events are sent through OnPointerEventObserved and +// its |target| is always null. class VIEWS_EXPORT PointerWatcher { public: + PointerWatcher() {} + + virtual void OnPointerEventObserved(const ui::PointerEvent& event, + const gfx::Point& location_in_screen, + Widget* target) = 0; + + protected: virtual ~PointerWatcher() {} - virtual void OnMousePressed(const ui::MouseEvent& event, - const gfx::Point& location_in_screen, - Widget* target) = 0; - virtual void OnTouchPressed(const ui::TouchEvent& event, - const gfx::Point& location_in_screen, - Widget* target) = 0; + private: + DISALLOW_COPY_AND_ASSIGN(PointerWatcher); }; } // namespace views diff --git a/chromium/ui/views/repeat_controller.h b/chromium/ui/views/repeat_controller.h index 5eef95961ed..aff5a35fbf3 100644 --- a/chromium/ui/views/repeat_controller.h +++ b/chromium/ui/views/repeat_controller.h @@ -33,6 +33,8 @@ class RepeatController { // Stop repeating. void Stop(); + const base::OneShotTimer& timer_for_testing() const { return timer_; } + private: // Called when the timer expires. void Run(); diff --git a/chromium/ui/views/resources/default_100_percent/slider_right_active.png b/chromium/ui/views/resources/default_100_percent/slider_right_active.png Binary files differdeleted file mode 100644 index 3fa882dd86c..00000000000 --- a/chromium/ui/views/resources/default_100_percent/slider_right_active.png +++ /dev/null diff --git a/chromium/ui/views/resources/default_200_percent/slider_right_active.png b/chromium/ui/views/resources/default_200_percent/slider_right_active.png Binary files differdeleted file mode 100644 index 3da2bb7558b..00000000000 --- a/chromium/ui/views/resources/default_200_percent/slider_right_active.png +++ /dev/null diff --git a/chromium/ui/views/resources/views_resources.grd b/chromium/ui/views/resources/views_resources.grd index 7d6eb05ee83..dbcd9c47cca 100644 --- a/chromium/ui/views/resources/views_resources.grd +++ b/chromium/ui/views/resources/views_resources.grd @@ -160,7 +160,6 @@ <structure type="chrome_scaled_image" name="IDR_MENU_CHECK" file="cros/menu_check.png" /> </if> <structure type="chrome_scaled_image" name="IDR_SLIDER_ACTIVE_LEFT" file="slider_left_active.png" /> - <structure type="chrome_scaled_image" name="IDR_SLIDER_ACTIVE_RIGHT" file="slider_right_active.png" /> <structure type="chrome_scaled_image" name="IDR_SLIDER_ACTIVE_CENTER" file="slider_center_active.png" /> <structure type="chrome_scaled_image" name="IDR_SLIDER_DISABLED_LEFT" file="slider_left_disabled.png" /> <structure type="chrome_scaled_image" name="IDR_SLIDER_DISABLED_RIGHT" file="slider_right_disabled.png" /> diff --git a/chromium/ui/views/resources/views_resources.gyp b/chromium/ui/views/resources/views_resources.gyp deleted file mode 100644 index 1596f3621f6..00000000000 --- a/chromium/ui/views/resources/views_resources.gyp +++ /dev/null @@ -1,27 +0,0 @@ -# Copyright 2015 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. - -{ - 'variables': { - 'chromium_code': 1, - }, - 'targets': [ - { - # GN version: //ui/views/resources - 'target_name': 'views_resources', - 'type': 'none', - 'variables': { - 'grit_out_dir': '<(SHARED_INTERMEDIATE_DIR)/ui/views/resources', - }, - 'actions': [{ - 'action_name': 'views_resources', - 'variables': { - 'grit_grd_file': 'views_resources.grd', - }, - 'includes': [ '../../../build/grit_action.gypi' ], - }], - 'includes': [ '../../../build/grit_target.gypi' ], - }, - ], -} diff --git a/chromium/ui/views/round_rect_painter.cc b/chromium/ui/views/round_rect_painter.cc index 1b96a125c43..7673b72a380 100644 --- a/chromium/ui/views/round_rect_painter.cc +++ b/chromium/ui/views/round_rect_painter.cc @@ -26,14 +26,14 @@ void RoundRectPainter::Paint(gfx::Canvas* canvas, const gfx::Size& size) { SkPaint paint; paint.setColor(border_color_); paint.setStyle(SkPaint::kStroke_Style); - paint.setStrokeWidth(1); + paint.setStrokeWidth(kBorderWidth); paint.setFlags(SkPaint::kAntiAlias_Flag); gfx::Rect rect(size); - rect.Inset(0, 0, 1, 1); + rect.Inset(0, 0, kBorderWidth, kBorderWidth); SkRect skia_rect = gfx::RectToSkRect(rect); - skia_rect.offset(.5, .5); + skia_rect.offset(kBorderWidth / 2.f, kBorderWidth / 2.f); canvas->sk_canvas()->drawRoundRect(skia_rect, SkIntToScalar(corner_radius_), - SkIntToScalar(corner_radius_), paint); + SkIntToScalar(corner_radius_), paint); } } // namespace views diff --git a/chromium/ui/views/round_rect_painter.h b/chromium/ui/views/round_rect_painter.h index f52624a425a..17c51d0fc60 100644 --- a/chromium/ui/views/round_rect_painter.h +++ b/chromium/ui/views/round_rect_painter.h @@ -21,6 +21,8 @@ namespace views { // Painter to draw a border with rounded corners. class VIEWS_EXPORT RoundRectPainter : public Painter { public: + enum { kBorderWidth = 1 }; + RoundRectPainter(SkColor border_color, int corner_radius); ~RoundRectPainter() override; diff --git a/chromium/ui/views/style/mac/combobox_background_mac.cc b/chromium/ui/views/style/mac/combobox_background_mac.cc index d15be0838a9..e9d73b19cf9 100644 --- a/chromium/ui/views/style/mac/combobox_background_mac.cc +++ b/chromium/ui/views/style/mac/combobox_background_mac.cc @@ -22,7 +22,7 @@ ComboboxBackgroundMac::~ComboboxBackgroundMac() {} void ComboboxBackgroundMac::Paint(gfx::Canvas* canvas, View* view) const { gfx::RectF bounds(view->GetLocalBounds()); - gfx::ScopedRTLFlipCanvas scoped_canvas(canvas, view->bounds()); + gfx::ScopedRTLFlipCanvas scoped_canvas(canvas, view->width()); // Inset the left side far enough to draw only the arrow button, and inset the // other three sides by half a pixel so the edge of the background doesn't diff --git a/chromium/ui/views/style/mac/dialog_button_border_mac.cc b/chromium/ui/views/style/mac/dialog_button_border_mac.cc deleted file mode 100644 index 2781b1ccb64..00000000000 --- a/chromium/ui/views/style/mac/dialog_button_border_mac.cc +++ /dev/null @@ -1,72 +0,0 @@ -// Copyright 2015 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/style/mac/dialog_button_border_mac.h" - -#include "base/logging.h" -#include "third_party/skia/include/core/SkCanvas.h" -#include "third_party/skia/include/core/SkDrawLooper.h" -#include "third_party/skia/include/core/SkPaint.h" -#include "third_party/skia/include/core/SkPath.h" -#include "third_party/skia/include/effects/SkGradientShader.h" -#include "ui/gfx/canvas.h" -#include "ui/native_theme/native_theme_mac.h" -#include "ui/views/border.h" -#include "ui/views/controls/button/custom_button.h" -#include "ui/views/controls/button/label_button.h" - -using ui::NativeThemeMac; - -namespace views { -namespace { - -// Default border insets, to provide text padding. -const int kPaddingX = 19; -const int kPaddingY = 7; - -NativeThemeMac::ButtonBackgroundType PaintTypeFromButton( - const LabelButton& button) { - if (!button.enabled() || button.state() == Button::STATE_DISABLED) - return NativeThemeMac::ButtonBackgroundType::DISABLED; - if (button.state() == Button::STATE_PRESSED) - return NativeThemeMac::ButtonBackgroundType::PRESSED; - if (DialogButtonBorderMac::ShouldRenderDefault(button)) - return NativeThemeMac::ButtonBackgroundType::HIGHLIGHTED; - return NativeThemeMac::ButtonBackgroundType::NORMAL; -} - -} // namespace - -DialogButtonBorderMac::DialogButtonBorderMac() { - set_insets(gfx::Insets(kPaddingY, kPaddingX, kPaddingY, kPaddingX)); -} - -DialogButtonBorderMac::~DialogButtonBorderMac() {} - -// static -bool DialogButtonBorderMac::ShouldRenderDefault(const LabelButton& button) { - // TODO(tapted): Check whether the Widget is active, and only return true here - // if it is. Plumbing this requires default buttons to also observe Widget - // activations to ensure text and background colors are properly invalidated. - return button.is_default(); -} - -void DialogButtonBorderMac::Paint(const View& view, gfx::Canvas* canvas) { - // Actually, |view| should be a LabelButton as well, but don't rely too much - // on RTTI. - DCHECK(CustomButton::AsCustomButton(&view)); - const LabelButton& button = static_cast<const LabelButton&>(view); - - ui::NativeThemeMac::PaintStyledGradientButton( - canvas->sk_canvas(), view.GetLocalBounds(), PaintTypeFromButton(button), - true, true, button.HasFocus()); -} - -gfx::Size DialogButtonBorderMac::GetMinimumSize() const { - // Overridden by PlatformStyle. Here, just ensure the minimum size is - // consistent with the padding. - return gfx::Size(2 * kPaddingX, 2 * kPaddingY); -} - -} // namespace views diff --git a/chromium/ui/views/style/mac/dialog_button_border_mac.h b/chromium/ui/views/style/mac/dialog_button_border_mac.h deleted file mode 100644 index b10b09a9b7e..00000000000 --- a/chromium/ui/views/style/mac/dialog_button_border_mac.h +++ /dev/null @@ -1,37 +0,0 @@ -// Copyright 2015 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_STYLE_MAC_DIALOG_BUTTON_BORDER_MAC_H_ -#define UI_VIEWS_STYLE_MAC_DIALOG_BUTTON_BORDER_MAC_H_ - -#include "base/macros.h" -#include "ui/views/controls/button/label_button_border.h" -#include "ui/views/views_export.h" - -namespace views { - -class LabelButton; - -// Skia port of the default button style used for dialogs on Chrome Mac. -// Originally provided by ConstrainedWindowButton, which used Quartz-backed -// Cocoa drawing routines. -class VIEWS_EXPORT DialogButtonBorderMac : public LabelButtonBorder { - public: - DialogButtonBorderMac(); - ~DialogButtonBorderMac() override; - - // Whether the given |button| should get a highlighted background. - static bool ShouldRenderDefault(const LabelButton& button); - - // views::Border: - void Paint(const View& view, gfx::Canvas* canvas) override; - gfx::Size GetMinimumSize() const override; - - private: - DISALLOW_COPY_AND_ASSIGN(DialogButtonBorderMac); -}; - -} // namespace views - -#endif // UI_VIEWS_STYLE_MAC_DIALOG_BUTTON_BORDER_MAC_H_ diff --git a/chromium/ui/views/style/mac/dialog_button_border_mac_unittest.cc b/chromium/ui/views/style/mac/dialog_button_border_mac_unittest.cc deleted file mode 100644 index 950697481e1..00000000000 --- a/chromium/ui/views/style/mac/dialog_button_border_mac_unittest.cc +++ /dev/null @@ -1,200 +0,0 @@ -// Copyright 2015 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/style/mac/dialog_button_border_mac.h" - -#include "base/macros.h" -#include "base/memory/ptr_util.h" -#include "base/strings/utf_string_conversions.h" -#include "third_party/skia/include/core/SkCanvas.h" -#include "ui/compositor/canvas_painter.h" -#include "ui/gfx/canvas.h" -#include "ui/views/border.h" -#include "ui/views/controls/button/label_button.h" -#include "ui/views/test/views_test_base.h" - -namespace views { -namespace { - -// LabelButton that can optionally provide a custom border. -class TestLabelButton : public LabelButton { - public: - explicit TestLabelButton(const char* text) - : LabelButton(nullptr, base::ASCIIToUTF16(text)) {} - - void SimulateAddToWidget() { OnNativeThemeChanged(nullptr); } - void set_provide_custom_border(bool value) { provide_custom_border_ = value; } - - // LabelButton: - std::unique_ptr<LabelButtonBorder> CreateDefaultBorder() const override { - if (!provide_custom_border_) - return LabelButton::CreateDefaultBorder(); - - return base::WrapUnique(new LabelButtonAssetBorder(style())); - } - - private: - bool provide_custom_border_ = false; - - DISALLOW_COPY_AND_ASSIGN(TestLabelButton); -}; - -gfx::Size DialogButtonBorderMacSize() { - const DialogButtonBorderMac template_border; - return template_border.GetMinimumSize(); -} - -// A heuristic that tries to determine whether the border on |view| is a -// DialogButtonBorderMac by checking its minimum size. -bool BorderIsDialogButton(const View& view) { - const Border* border = view.border(); - return border && DialogButtonBorderMacSize() == border->GetMinimumSize(); -} - -SkColor TestPaint(View* view) { - EXPECT_TRUE(view->visible()); - EXPECT_FALSE(view->bounds().IsEmpty()); - const gfx::Point center = view->bounds().CenterPoint(); - gfx::Canvas canvas(view->bounds().size(), 1.0, false /* is_opaque */); - SkCanvas* sk_canvas = canvas.sk_canvas(); - - // Read a pixel - it should be blank. - SkColor initial_pixel; - SkBitmap bitmap; - bitmap.allocN32Pixels(1, 1); - EXPECT_TRUE(sk_canvas->readPixels(&bitmap, center.x(), center.y())); - initial_pixel = bitmap.getColor(0, 0); - EXPECT_EQ(static_cast<SkColor>(SK_ColorTRANSPARENT), initial_pixel); - - view->Paint(ui::CanvasPainter(&canvas, 1.f).context()); - - // Ensure save()/restore() calls are balanced. - EXPECT_EQ(1, sk_canvas->getSaveCount()); - - // Ensure "something" happened. This assumes the border is a - // DialogButtonBorderMac, which always modifies the center pixel. - EXPECT_TRUE(sk_canvas->readPixels(&bitmap, center.x(), center.y())); - return bitmap.getColor(0, 0); -} - -void TestPaintAllStates(CustomButton* button, bool verify) { - for (int i = 0; i < Button::STATE_COUNT; ++i) { - Button::ButtonState state = static_cast<Button::ButtonState>(i); - SCOPED_TRACE(testing::Message() << "Button::ButtonState: " << state); - button->SetState(state); - SkColor color = TestPaint(button); - if (verify) - EXPECT_NE(static_cast<SkColor>(SK_ColorTRANSPARENT), color); - } -} - -} // namespace - -using DialogButtonBorderMacTest = ViewsTestBase; - -// Verify that the DialogButtonBorderMac insets are consistent with the -// minimum size, and they're correctly carried across to the View's preferred -// size. -TEST_F(DialogButtonBorderMacTest, DrawMinimumSize) { - TestLabelButton button(""); - button.SetStyle(Button::STYLE_BUTTON); - button.SimulateAddToWidget(); - - EXPECT_TRUE(BorderIsDialogButton(button)); - - // The border minimum size should be at least the size of the insets. - const gfx::Size border_min_size = DialogButtonBorderMacSize(); - const gfx::Insets insets = button.GetInsets(); - EXPECT_LE(insets.width(), border_min_size.width()); - EXPECT_LE(insets.height(), border_min_size.height()); - - // The view preferred size should be at least as big as the border minimum. - gfx::Size view_preferred_size = button.GetPreferredSize(); - EXPECT_LE(border_min_size.width(), view_preferred_size.width()); - EXPECT_LE(border_min_size.height(), view_preferred_size.height()); - - // Note that Mac's PlatformStyle specifies a minimum button size, but it - // shouldn't be larger than the size of the button's label plus border insets. - // If it was, a Button::SetMinSize() call would be needed here to override it. - - button.SizeToPreferredSize(); - EXPECT_EQ(view_preferred_size.width(), button.width()); - EXPECT_EQ(view_preferred_size.height(), button.height()); - - { - SCOPED_TRACE("Preferred Size"); - TestPaintAllStates(&button, true); - } - - // The View can ignore the border minimum size. To account for shadows, the - // border will paint something as small as 4x4. - { - SCOPED_TRACE("Minimum Paint Size"); - button.SetSize(gfx::Size(4, 4)); - TestPaintAllStates(&button, true); - } - - // Smaller than that, nothing gets painted, but the paint code should be sane. - { - SCOPED_TRACE("Size 1x1"); - button.SetSize(gfx::Size(1, 1)); - TestPaintAllStates(&button, false); - } -} - -// Test drawing with some text. The usual case. -TEST_F(DialogButtonBorderMacTest, DrawWithLabel) { - TestLabelButton button(""); - button.SetStyle(Button::STYLE_BUTTON); - button.SimulateAddToWidget(); - - EXPECT_TRUE(BorderIsDialogButton(button)); - - button.SizeToPreferredSize(); - const gfx::Size no_label_size = button.size(); - - button.SetText( - base::ASCIIToUTF16("Label Text That Exceeds the Minimum Button Size")); - button.SizeToPreferredSize(); - - // Long label, so the button width should be greater than the empty button. - EXPECT_LT(no_label_size.width(), button.width()); - - // The height shouldn't change. - EXPECT_EQ(no_label_size.height(), button.height()); - - TestPaintAllStates(&button, true); -} - -// Test that the themed style is not used for STYLE_TEXTBUTTON (the default), or -// when a custom Border is set, or when a LabelButton subclass provides its own -// default border. -TEST_F(DialogButtonBorderMacTest, ChecksButtonStyle) { - TestLabelButton button(""); - button.SimulateAddToWidget(); - - // Default style is STYLE_TEXTBUTTON, which doesn't use the themed border. - EXPECT_FALSE(BorderIsDialogButton(button)); - - button.SetStyle(Button::STYLE_BUTTON); - button.SimulateAddToWidget(); - EXPECT_TRUE(BorderIsDialogButton(button)); - - button.set_provide_custom_border(true); - button.SimulateAddToWidget(); - EXPECT_FALSE(BorderIsDialogButton(button)); - - button.set_provide_custom_border(false); - button.SimulateAddToWidget(); - EXPECT_TRUE(BorderIsDialogButton(button)); - - // Any call to SetBorder() will immediately prevent themed buttons and adding - // to a Widget (to pick up a NativeTheme) shouldn't restore them. - button.SetBorder(Border::NullBorder()); - EXPECT_FALSE(BorderIsDialogButton(button)); - button.SimulateAddToWidget(); - EXPECT_FALSE(BorderIsDialogButton(button)); -} - -} // namespace views diff --git a/chromium/ui/views/style/platform_style.cc b/chromium/ui/views/style/platform_style.cc index a4cad6c345c..a9fd9829c9d 100644 --- a/chromium/ui/views/style/platform_style.cc +++ b/chromium/ui/views/style/platform_style.cc @@ -38,7 +38,12 @@ const int PlatformStyle::kComboboxNormalArrowPadding = 7; const int PlatformStyle::kMinLabelButtonWidth = 70; const int PlatformStyle::kMinLabelButtonHeight = 33; const bool PlatformStyle::kDefaultLabelButtonHasBoldFont = true; +const bool PlatformStyle::kDialogDefaultButtonCanBeCancel = true; const bool PlatformStyle::kTextfieldDragVerticallyDragsToEnd = false; +const CustomButton::NotifyAction PlatformStyle::kMenuNotifyActivationAction = + CustomButton::NOTIFY_ON_RELEASE; +const bool PlatformStyle::kTreeViewSelectionPaintsEntireRow = false; +const bool PlatformStyle::kUseRipples = true; // static gfx::ImageSkia PlatformStyle::CreateComboboxArrow(bool is_enabled, @@ -49,7 +54,7 @@ gfx::ImageSkia PlatformStyle::CreateComboboxArrow(bool is_enabled, // static std::unique_ptr<FocusableBorder> PlatformStyle::CreateComboboxBorder() { - return base::WrapUnique(new FocusableBorder()); + return base::MakeUnique<FocusableBorder>(); } // static @@ -59,22 +64,8 @@ std::unique_ptr<Background> PlatformStyle::CreateComboboxBackground( } // static -std::unique_ptr<LabelButtonBorder> PlatformStyle::CreateLabelButtonBorder( - Button::ButtonStyle style) { - if (!ui::MaterialDesignController::IsModeMaterial() || - style != Button::STYLE_TEXTBUTTON) { - return base::WrapUnique(new LabelButtonAssetBorder(style)); - } - - std::unique_ptr<LabelButtonBorder> border(new views::LabelButtonBorder()); - border->set_insets(views::LabelButtonAssetBorder::GetDefaultInsetsForStyle( - Button::STYLE_TEXTBUTTON)); - return border; -} - -// static std::unique_ptr<ScrollBar> PlatformStyle::CreateScrollBar(bool is_horizontal) { - return base::WrapUnique(new NativeScrollBar(is_horizontal)); + return base::MakeUnique<NativeScrollBar>(is_horizontal); } // static @@ -84,6 +75,9 @@ SkColor PlatformStyle::TextColorForButton( return color_by_state[button.state()]; } +// static +void PlatformStyle::OnTextfieldKeypressUnhandled() {} + #endif // OS_MACOSX #if !defined(DESKTOP_LINUX) && !defined(OS_MACOSX) @@ -96,10 +90,6 @@ void PlatformStyle::ApplyLabelButtonTextStyle( colors[Button::STATE_HOVERED] = kStyleButtonTextColor; colors[Button::STATE_PRESSED] = kStyleButtonTextColor; - const ui::NativeTheme* theme = label->GetNativeTheme(); - label->SetBackgroundColor( - theme->GetSystemColor(ui::NativeTheme::kColorId_ButtonBackgroundColor)); - label->SetAutoColorReadabilityEnabled(false); label->SetShadows(gfx::ShadowValues( 1, gfx::ShadowValue(gfx::Vector2d(0, 1), 0, kStyleButtonShadowColor))); } diff --git a/chromium/ui/views/style/platform_style.h b/chromium/ui/views/style/platform_style.h index 8534aa27635..8405cb2009a 100644 --- a/chromium/ui/views/style/platform_style.h +++ b/chromium/ui/views/style/platform_style.h @@ -9,6 +9,7 @@ #include "base/macros.h" #include "ui/views/controls/button/button.h" +#include "ui/views/controls/button/custom_button.h" #include "ui/views/controls/combobox/combobox.h" #include "ui/views/views_export.h" @@ -18,7 +19,6 @@ class Border; class FocusableBorder; class Label; class LabelButton; -class LabelButtonBorder; class ScrollBar; // Cross-platform API for providing platform-specific styling for toolkit-views. @@ -38,10 +38,23 @@ class VIEWS_EXPORT PlatformStyle { // Whether dialog-default buttons are given a bold font style. static const bool kDefaultLabelButtonHasBoldFont; + // Whether the default button for a dialog can be the Cancel button. + static const bool kDialogDefaultButtonCanBeCancel; + // Whether dragging vertically above or below a textfield's bounds selects to // the left or right end of the text from the cursor, respectively. static const bool kTextfieldDragVerticallyDragsToEnd; + // The menu button's action to show the menu. + static const CustomButton::NotifyAction kMenuNotifyActivationAction; + + // Whether selecting a row in a TreeView selects the entire row or only the + // label for that row. + static const bool kTreeViewSelectionPaintsEntireRow; + + // Whether ripples should be used for visual feedback on control activation. + static const bool kUseRipples; + // Creates an ImageSkia containing the image to use for the combobox arrow. // The |is_enabled| argument is true if the control the arrow is for is // enabled, and false if the control is disabled. The |style| argument is the @@ -56,11 +69,6 @@ class VIEWS_EXPORT PlatformStyle { static std::unique_ptr<Background> CreateComboboxBackground( int shoulder_width); - // Creates the default label button border for the given |style|. Used when a - // custom default border is not provided for a particular LabelButton class. - static std::unique_ptr<LabelButtonBorder> CreateLabelButtonBorder( - Button::ButtonStyle style); - // Creates the default scrollbar for the given orientation. static std::unique_ptr<ScrollBar> CreateScrollBar(bool is_horizontal); @@ -78,6 +86,10 @@ class VIEWS_EXPORT PlatformStyle { static std::unique_ptr<Border> CreateThemedLabelButtonBorder( LabelButton* button); + // Called whenever a textfield keypress is unhandled for any reason. Gives + // visual/audio feedback about the unhandled key if platform-appropriate. + static void OnTextfieldKeypressUnhandled(); + private: DISALLOW_IMPLICIT_CONSTRUCTORS(PlatformStyle); }; diff --git a/chromium/ui/views/style/platform_style_mac.mm b/chromium/ui/views/style/platform_style_mac.mm index 5a8d2bd0b63..866bba64424 100644 --- a/chromium/ui/views/style/platform_style_mac.mm +++ b/chromium/ui/views/style/platform_style_mac.mm @@ -6,15 +6,16 @@ #include "base/memory/ptr_util.h" #include "ui/base/resource/resource_bundle.h" +#include "ui/gfx/color_utils.h" #include "ui/gfx/paint_vector_icon.h" -#include "ui/gfx/vector_icons.h" +#include "ui/gfx/vector_icons_public.h" #include "ui/resources/grit/ui_resources.h" #include "ui/views/controls/button/label_button.h" -#include "ui/views/controls/button/label_button_border.h" #include "ui/views/controls/focusable_rounded_border_mac.h" #import "ui/views/controls/scrollbar/cocoa_scroll_bar.h" #include "ui/views/style/mac/combobox_background_mac.h" -#include "ui/views/style/mac/dialog_button_border_mac.h" + +#import <Cocoa/Cocoa.h> namespace views { @@ -22,7 +23,13 @@ const int PlatformStyle::kComboboxNormalArrowPadding = 0; const int PlatformStyle::kMinLabelButtonWidth = 32; const int PlatformStyle::kMinLabelButtonHeight = 30; const bool PlatformStyle::kDefaultLabelButtonHasBoldFont = false; +const bool PlatformStyle::kDialogDefaultButtonCanBeCancel = false; const bool PlatformStyle::kTextfieldDragVerticallyDragsToEnd = true; +const bool PlatformStyle::kTreeViewSelectionPaintsEntireRow = true; +const bool PlatformStyle::kUseRipples = false; + +const CustomButton::NotifyAction PlatformStyle::kMenuNotifyActivationAction = + CustomButton::NOTIFY_ON_PRESS; // static gfx::ImageSkia PlatformStyle::CreateComboboxArrow(bool is_enabled, @@ -48,21 +55,12 @@ std::unique_ptr<FocusableBorder> PlatformStyle::CreateComboboxBorder() { // static std::unique_ptr<Background> PlatformStyle::CreateComboboxBackground( int shoulder_width) { - return base::WrapUnique(new ComboboxBackgroundMac(shoulder_width)); -} - -// static -std::unique_ptr<LabelButtonBorder> PlatformStyle::CreateLabelButtonBorder( - Button::ButtonStyle style) { - if (style == Button::STYLE_BUTTON) - return base::WrapUnique(new DialogButtonBorderMac()); - - return base::WrapUnique(new LabelButtonAssetBorder(style)); + return base::MakeUnique<ComboboxBackgroundMac>(shoulder_width); } // static std::unique_ptr<ScrollBar> PlatformStyle::CreateScrollBar(bool is_horizontal) { - return base::WrapUnique(new CocoaScrollBar(is_horizontal)); + return base::MakeUnique<CocoaScrollBar>(is_horizontal); } // static @@ -70,8 +68,7 @@ SkColor PlatformStyle::TextColorForButton( const ButtonColorByState& color_by_state, const LabelButton& button) { Button::ButtonState state = button.state(); - if (button.style() == Button::STYLE_BUTTON && - DialogButtonBorderMac::ShouldRenderDefault(button)) { + if (button.style() == Button::STYLE_BUTTON && button.is_default()) { // For convenience, we currently assume Mac wants the color corresponding to // the pressed state for default buttons. state = Button::STATE_PRESSED; @@ -83,10 +80,13 @@ SkColor PlatformStyle::TextColorForButton( void PlatformStyle::ApplyLabelButtonTextStyle( views::Label* label, ButtonColorByState* color_by_state) { - const ui::NativeTheme* theme = label->GetNativeTheme(); ButtonColorByState& colors = *color_by_state; - colors[Button::STATE_PRESSED] = - theme->GetSystemColor(ui::NativeTheme::kColorId_ButtonHighlightColor); + colors[Button::STATE_PRESSED] = SK_ColorWHITE; +} + +// static +void PlatformStyle::OnTextfieldKeypressUnhandled() { + NSBeep(); } } // namespace views diff --git a/chromium/ui/views/touchui/touch_selection_controller_impl.cc b/chromium/ui/views/touchui/touch_selection_controller_impl.cc index 1e803135964..201e6d04c85 100644 --- a/chromium/ui/views/touchui/touch_selection_controller_impl.cc +++ b/chromium/ui/views/touchui/touch_selection_controller_impl.cc @@ -237,7 +237,6 @@ class TouchSelectionControllerImpl::EditingHandleView draw_invisible_(false), weak_ptr_factory_(this) { widget_.reset(CreateTouchSelectionPopupWidget(context, this)); - widget_->SetContentsView(this); aura::Window* window = widget_->GetNativeWindow(); window->SetEventTargeter(std::unique_ptr<ui::EventTargeter>( @@ -321,6 +320,10 @@ class TouchSelectionControllerImpl::EditingHandleView } gfx::Size GetPreferredSize() const override { + // This function will be called during widget initialization, i.e. before + // SetBoundInScreen has been called. No-op in that case. + if (selection_bound_.type() == gfx::SelectionBound::EMPTY) + return gfx::Size(); return GetSelectionWidgetBounds(selection_bound_).size(); } diff --git a/chromium/ui/views/touchui/touch_selection_controller_impl_unittest.cc b/chromium/ui/views/touchui/touch_selection_controller_impl_unittest.cc index b1a5e36e3dc..e3983dc0459 100644 --- a/chromium/ui/views/touchui/touch_selection_controller_impl_unittest.cc +++ b/chromium/ui/views/touchui/touch_selection_controller_impl_unittest.cc @@ -97,7 +97,7 @@ class TouchSelectionControllerImplTest : public ViewsTestBase { textfield_widget_->SetContentsView(container); container->AddChildView(textfield_); - textfield_->SetBoundsRect(gfx::Rect(0, 0, 200, 20)); + textfield_->SetBoundsRect(gfx::Rect(0, 0, 200, 21)); textfield_->set_id(1); textfield_widget_->Show(); @@ -687,11 +687,6 @@ class TestTouchEditable : public ui::TouchEditable { NOTREACHED(); return false; } - bool GetAcceleratorForCommandId(int command_id, - ui::Accelerator* accelerator) override { - NOTREACHED(); - return false; - } void ExecuteCommand(int command_id, int event_flags) override { NOTREACHED(); } diff --git a/chromium/ui/views/touchui/touch_selection_menu_runner_views.cc b/chromium/ui/views/touchui/touch_selection_menu_runner_views.cc index b506f787de5..b208d5ffb35 100644 --- a/chromium/ui/views/touchui/touch_selection_menu_runner_views.cc +++ b/chromium/ui/views/touchui/touch_selection_menu_runner_views.cc @@ -161,10 +161,7 @@ Button* TouchSelectionMenuRunnerViews::Menu::CreateButton( LabelButton* button = new LabelButton(this, label); button->SetMinSize(gfx::Size(kMenuButtonMinWidth, kMenuButtonMinHeight)); button->SetFocusForPlatform(); - const gfx::FontList& font_list = - ui::ResourceBundle::GetSharedInstance().GetFontList( - ui::ResourceBundle::SmallFont); - button->SetFontList(font_list); + button->AdjustFontSize(ui::ResourceBundle::kSmallFontDelta); button->SetHorizontalAlignment(gfx::ALIGN_CENTER); button->set_tag(tag); return button; diff --git a/chromium/ui/views/view.cc b/chromium/ui/views/view.cc index be3bbf5b7ed..a57d82f97db 100644 --- a/chromium/ui/views/view.cc +++ b/chromium/ui/views/view.cc @@ -447,7 +447,13 @@ void View::OnEnabledChanged() { // Transformations ------------------------------------------------------------- gfx::Transform View::GetTransform() const { - return layer() ? layer()->transform() : gfx::Transform(); + if (!layer()) + return gfx::Transform(); + + gfx::Transform transform = layer()->transform(); + gfx::ScrollOffset scroll_offset = layer()->CurrentScrollOffset(); + transform.Translate(-scroll_offset.x(), -scroll_offset.y()); + return transform; } void View::SetTransform(const gfx::Transform& transform) { @@ -826,7 +832,7 @@ void View::Paint(const ui::PaintContext& parent_context) { transform_from_parent.Translate(offset_from_parent.x(), offset_from_parent.y()); transform_from_parent.PreconcatTransform(GetTransform()); - transform_recorder.Transform(transform_from_parent, size()); + transform_recorder.Transform(transform_from_parent); } // Note that the cache is not aware of the offset of the view @@ -835,16 +841,8 @@ void View::Paint(const ui::PaintContext& parent_context) { if (is_invalidated || !paint_cache_.UseCache(context, size())) { ui::PaintRecorder recorder(context, size(), &paint_cache_); gfx::Canvas* canvas = recorder.canvas(); - - // 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. - if (FlipCanvasOnPaintForRTLUI()) { - canvas->Translate(gfx::Vector2d(width(), 0)); - canvas->Scale(-1, 1); - } + gfx::ScopedRTLFlipCanvas scoped_canvas(canvas, width(), + flip_canvas_on_paint_for_rtl_ui_); // Delegate painting the contents of the View to the virtual OnPaint method. OnPaint(canvas); @@ -1082,6 +1080,10 @@ ViewTargeter* View::GetEffectiveViewTargeter() const { return view_targeter; } +WordLookupClient* View::GetWordLookupClient() { + return nullptr; +} + bool View::CanAcceptEvent(const ui::Event& event) { return IsDrawn(); } @@ -1091,7 +1093,7 @@ ui::EventTarget* View::GetParentTarget() { } std::unique_ptr<ui::EventTargetIterator> View::GetChildIterator() const { - return base::WrapUnique(new ui::EventTargetIteratorImpl<View>(children_)); + return base::MakeUnique<ui::EventTargetIteratorImpl<View>>(children_); } ui::EventTargeter* View::GetEventTargeter() { @@ -1109,7 +1111,7 @@ void View::AddAccelerator(const ui::Accelerator& accelerator) { if (!accelerators_.get()) accelerators_.reset(new std::vector<ui::Accelerator>()); - if (!ContainsValue(*accelerators_.get(), accelerator)) + if (!base::ContainsValue(*accelerators_.get(), accelerator)) accelerators_->push_back(accelerator); RegisterPendingAccelerators(); @@ -2081,7 +2083,7 @@ void View::CreateLayer() { for (int i = 0, count = child_count(); i < count; ++i) child_at(i)->UpdateChildLayerVisibility(true); - SetLayer(new ui::Layer()); + SetLayer(base::MakeUnique<ui::Layer>()); layer()->set_delegate(this); layer()->set_name(GetClassName()); diff --git a/chromium/ui/views/view.h b/chromium/ui/views/view.h index 6b45c6313ba..28171db1da3 100644 --- a/chromium/ui/views/view.h +++ b/chromium/ui/views/view.h @@ -76,6 +76,7 @@ class LayoutManager; class NativeViewAccessibility; class ScrollView; class Widget; +class WordLookupClient; namespace internal { class PreEventDispatchHandler; @@ -538,13 +539,8 @@ class VIEWS_EXPORT View : public ui::LayerDelegate, // 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; + bool flip_canvas_on_paint_for_rtl_ui() const { + return flip_canvas_on_paint_for_rtl_ui_; } // Enables or disables flipping of the gfx::Canvas during View::Paint(). @@ -722,6 +718,9 @@ class VIEWS_EXPORT View : public ui::LayerDelegate, ViewTargeter* targeter() const { return targeter_.get(); } + // Returns the WordLookupClient associated with this view. + virtual WordLookupClient* GetWordLookupClient(); + // Overridden from ui::EventTarget: bool CanAcceptEvent(const ui::Event& event) override; ui::EventTarget* GetParentTarget() override; diff --git a/chromium/ui/views/view_unittest.cc b/chromium/ui/views/view_unittest.cc index 38f55bd11bd..e9af12ad5a9 100644 --- a/chromium/ui/views/view_unittest.cc +++ b/chromium/ui/views/view_unittest.cc @@ -526,7 +526,7 @@ TEST_F(ViewTest, PaintEmptyView) { // Paint "everything". gfx::Rect first_paint(1, 1); scoped_refptr<cc::DisplayItemList> list = - cc::DisplayItemList::Create(first_paint, cc::DisplayItemListSettings()); + cc::DisplayItemList::Create(cc::DisplayItemListSettings()); root_view->Paint(ui::PaintContext(list.get(), 1.f, first_paint)); // The empty view has nothing to paint so it doesn't try build a cache, nor do @@ -548,7 +548,7 @@ TEST_F(ViewTest, PaintWithMovedViewUsesCache) { gfx::Rect pixel_rect = gfx::Rect(1, 1); float device_scale_factor = 1.f; scoped_refptr<cc::DisplayItemList> list = - cc::DisplayItemList::Create(pixel_rect, cc::DisplayItemListSettings()); + cc::DisplayItemList::Create(cc::DisplayItemListSettings()); root_view->Paint( ui::PaintContext(list.get(), device_scale_factor, pixel_rect)); EXPECT_TRUE(v1->did_paint_); @@ -564,14 +564,14 @@ TEST_F(ViewTest, PaintWithMovedViewUsesCache) { list->VisualRectForTesting(item_index)); // If invalidation doesn't intersect v1, we paint with the cache. - list = cc::DisplayItemList::Create(pixel_rect, cc::DisplayItemListSettings()); + list = cc::DisplayItemList::Create(cc::DisplayItemListSettings()); root_view->Paint( ui::PaintContext(list.get(), device_scale_factor, pixel_rect)); EXPECT_FALSE(v1->did_paint_); v1->Reset(); // If invalidation does intersect v1, we don't paint with the cache. - list = cc::DisplayItemList::Create(pixel_rect, cc::DisplayItemListSettings()); + list = cc::DisplayItemList::Create(cc::DisplayItemListSettings()); root_view->Paint( ui::PaintContext(list.get(), device_scale_factor, v1->bounds())); EXPECT_TRUE(v1->did_paint_); @@ -579,7 +579,7 @@ TEST_F(ViewTest, PaintWithMovedViewUsesCache) { // Moving the view should still use the cache when the invalidation doesn't // intersect v1. - list = cc::DisplayItemList::Create(pixel_rect, cc::DisplayItemListSettings()); + list = cc::DisplayItemList::Create(cc::DisplayItemListSettings()); v1->SetX(9); root_view->Paint( ui::PaintContext(list.get(), device_scale_factor, pixel_rect)); @@ -596,7 +596,7 @@ TEST_F(ViewTest, PaintWithMovedViewUsesCache) { // Moving the view should not use the cache when painting without // invalidation. - list = cc::DisplayItemList::Create(pixel_rect, cc::DisplayItemListSettings()); + list = cc::DisplayItemList::Create(cc::DisplayItemListSettings()); v1->SetX(8); root_view->Paint(ui::PaintContext( ui::PaintContext(list.get(), device_scale_factor, pixel_rect), @@ -626,7 +626,7 @@ TEST_F(ViewTest, PaintWithMovedViewUsesCacheInRTL) { gfx::Rect pixel_rect = gfx::Rect(1, 1); float device_scale_factor = 1.f; scoped_refptr<cc::DisplayItemList> list = - cc::DisplayItemList::Create(pixel_rect, cc::DisplayItemListSettings()); + cc::DisplayItemList::Create(cc::DisplayItemListSettings()); root_view->Paint( ui::PaintContext(list.get(), device_scale_factor, pixel_rect)); EXPECT_TRUE(v1->did_paint_); @@ -643,14 +643,14 @@ TEST_F(ViewTest, PaintWithMovedViewUsesCacheInRTL) { list->VisualRectForTesting(item_index)); // If invalidation doesn't intersect v1, we paint with the cache. - list = cc::DisplayItemList::Create(pixel_rect, cc::DisplayItemListSettings()); + list = cc::DisplayItemList::Create(cc::DisplayItemListSettings()); root_view->Paint( ui::PaintContext(list.get(), device_scale_factor, pixel_rect)); EXPECT_FALSE(v1->did_paint_); v1->Reset(); // If invalidation does intersect v1, we don't paint with the cache. - list = cc::DisplayItemList::Create(pixel_rect, cc::DisplayItemListSettings()); + list = cc::DisplayItemList::Create(cc::DisplayItemListSettings()); root_view->Paint( ui::PaintContext(list.get(), device_scale_factor, v1->bounds())); EXPECT_TRUE(v1->did_paint_); @@ -658,7 +658,7 @@ TEST_F(ViewTest, PaintWithMovedViewUsesCacheInRTL) { // Moving the view should still use the cache when the invalidation doesn't // intersect v1. - list = cc::DisplayItemList::Create(pixel_rect, cc::DisplayItemListSettings()); + list = cc::DisplayItemList::Create(cc::DisplayItemListSettings()); v1->SetX(9); root_view->Paint( ui::PaintContext(list.get(), device_scale_factor, pixel_rect)); @@ -676,7 +676,7 @@ TEST_F(ViewTest, PaintWithMovedViewUsesCacheInRTL) { // Moving the view should not use the cache when painting without // invalidation. - list = cc::DisplayItemList::Create(pixel_rect, cc::DisplayItemListSettings()); + list = cc::DisplayItemList::Create(cc::DisplayItemListSettings()); v1->SetX(8); root_view->Paint(ui::PaintContext( ui::PaintContext(list.get(), device_scale_factor, pixel_rect), @@ -710,14 +710,14 @@ TEST_F(ViewTest, PaintWithUnknownInvalidation) { // invalidation. gfx::Rect first_paint(1, 1); scoped_refptr<cc::DisplayItemList> list = - cc::DisplayItemList::Create(first_paint, cc::DisplayItemListSettings()); + cc::DisplayItemList::Create(cc::DisplayItemListSettings()); root_view->Paint(ui::PaintContext(list.get(), 1.f, first_paint)); v1->Reset(); v2->Reset(); gfx::Rect paint_area(1, 1); gfx::Rect root_area(root_view->size()); - list = cc::DisplayItemList::Create(root_area, cc::DisplayItemListSettings()); + list = cc::DisplayItemList::Create(cc::DisplayItemListSettings()); // With a known invalidation, v1 and v2 are not painted. EXPECT_FALSE(v1->did_paint_); @@ -750,14 +750,14 @@ TEST_F(ViewTest, PaintContainsChildren) { // invalidation. gfx::Rect first_paint(1, 1); scoped_refptr<cc::DisplayItemList> list = - cc::DisplayItemList::Create(first_paint, cc::DisplayItemListSettings()); + cc::DisplayItemList::Create(cc::DisplayItemListSettings()); root_view->Paint(ui::PaintContext(list.get(), 1.f, first_paint)); v1->Reset(); v2->Reset(); gfx::Rect paint_area(25, 26); gfx::Rect root_area(root_view->size()); - list = cc::DisplayItemList::Create(root_area, cc::DisplayItemListSettings()); + list = cc::DisplayItemList::Create(cc::DisplayItemListSettings()); EXPECT_FALSE(v1->did_paint_); EXPECT_FALSE(v2->did_paint_); @@ -794,14 +794,14 @@ TEST_F(ViewTest, PaintContainsChildrenInRTL) { // invalidation. gfx::Rect first_paint(1, 1); scoped_refptr<cc::DisplayItemList> list = - cc::DisplayItemList::Create(first_paint, cc::DisplayItemListSettings()); + cc::DisplayItemList::Create(cc::DisplayItemListSettings()); root_view->Paint(ui::PaintContext(list.get(), 1.f, first_paint)); v1->Reset(); v2->Reset(); gfx::Rect paint_area(25, 26); gfx::Rect root_area(root_view->size()); - list = cc::DisplayItemList::Create(root_area, cc::DisplayItemListSettings()); + list = cc::DisplayItemList::Create(cc::DisplayItemListSettings()); EXPECT_FALSE(v1->did_paint_); EXPECT_FALSE(v2->did_paint_); @@ -826,14 +826,14 @@ TEST_F(ViewTest, PaintIntersectsChildren) { // invalidation. gfx::Rect first_paint(1, 1); scoped_refptr<cc::DisplayItemList> list = - cc::DisplayItemList::Create(first_paint, cc::DisplayItemListSettings()); + cc::DisplayItemList::Create(cc::DisplayItemListSettings()); root_view->Paint(ui::PaintContext(list.get(), 1.f, first_paint)); v1->Reset(); v2->Reset(); gfx::Rect paint_area(9, 10, 5, 6); gfx::Rect root_area(root_view->size()); - list = cc::DisplayItemList::Create(root_area, cc::DisplayItemListSettings()); + list = cc::DisplayItemList::Create(cc::DisplayItemListSettings()); EXPECT_FALSE(v1->did_paint_); EXPECT_FALSE(v2->did_paint_); @@ -870,14 +870,14 @@ TEST_F(ViewTest, PaintIntersectsChildrenInRTL) { // invalidation. gfx::Rect first_paint(1, 1); scoped_refptr<cc::DisplayItemList> list = - cc::DisplayItemList::Create(first_paint, cc::DisplayItemListSettings()); + cc::DisplayItemList::Create(cc::DisplayItemListSettings()); root_view->Paint(ui::PaintContext(list.get(), 1.f, first_paint)); v1->Reset(); v2->Reset(); gfx::Rect paint_area(2, 10, 5, 6); gfx::Rect root_area(root_view->size()); - list = cc::DisplayItemList::Create(root_area, cc::DisplayItemListSettings()); + list = cc::DisplayItemList::Create(cc::DisplayItemListSettings()); EXPECT_FALSE(v1->did_paint_); EXPECT_FALSE(v2->did_paint_); @@ -902,14 +902,14 @@ TEST_F(ViewTest, PaintIntersectsChildButNotGrandChild) { // invalidation. gfx::Rect first_paint(1, 1); scoped_refptr<cc::DisplayItemList> list = - cc::DisplayItemList::Create(first_paint, cc::DisplayItemListSettings()); + cc::DisplayItemList::Create(cc::DisplayItemListSettings()); root_view->Paint(ui::PaintContext(list.get(), 1.f, first_paint)); v1->Reset(); v2->Reset(); gfx::Rect paint_area(9, 10, 2, 3); gfx::Rect root_area(root_view->size()); - list = cc::DisplayItemList::Create(root_area, cc::DisplayItemListSettings()); + list = cc::DisplayItemList::Create(cc::DisplayItemListSettings()); EXPECT_FALSE(v1->did_paint_); EXPECT_FALSE(v2->did_paint_); @@ -946,14 +946,14 @@ TEST_F(ViewTest, PaintIntersectsChildButNotGrandChildInRTL) { // invalidation. gfx::Rect first_paint(1, 1); scoped_refptr<cc::DisplayItemList> list = - cc::DisplayItemList::Create(first_paint, cc::DisplayItemListSettings()); + cc::DisplayItemList::Create(cc::DisplayItemListSettings()); root_view->Paint(ui::PaintContext(list.get(), 1.f, first_paint)); v1->Reset(); v2->Reset(); gfx::Rect paint_area(2, 10, 2, 3); gfx::Rect root_area(root_view->size()); - list = cc::DisplayItemList::Create(root_area, cc::DisplayItemListSettings()); + list = cc::DisplayItemList::Create(cc::DisplayItemListSettings()); EXPECT_FALSE(v1->did_paint_); EXPECT_FALSE(v2->did_paint_); @@ -978,14 +978,14 @@ TEST_F(ViewTest, PaintIntersectsNoChildren) { // invalidation. gfx::Rect first_paint(1, 1); scoped_refptr<cc::DisplayItemList> list = - cc::DisplayItemList::Create(first_paint, cc::DisplayItemListSettings()); + cc::DisplayItemList::Create(cc::DisplayItemListSettings()); root_view->Paint(ui::PaintContext(list.get(), 1.f, first_paint)); v1->Reset(); v2->Reset(); gfx::Rect paint_area(9, 10, 2, 1); gfx::Rect root_area(root_view->size()); - list = cc::DisplayItemList::Create(root_area, cc::DisplayItemListSettings()); + list = cc::DisplayItemList::Create(cc::DisplayItemListSettings()); EXPECT_FALSE(v1->did_paint_); EXPECT_FALSE(v2->did_paint_); @@ -1022,14 +1022,14 @@ TEST_F(ViewTest, PaintIntersectsNoChildrenInRTL) { // invalidation. gfx::Rect first_paint(1, 1); scoped_refptr<cc::DisplayItemList> list = - cc::DisplayItemList::Create(first_paint, cc::DisplayItemListSettings()); + cc::DisplayItemList::Create(cc::DisplayItemListSettings()); root_view->Paint(ui::PaintContext(list.get(), 1.f, first_paint)); v1->Reset(); v2->Reset(); gfx::Rect paint_area(2, 10, 2, 1); gfx::Rect root_area(root_view->size()); - list = cc::DisplayItemList::Create(root_area, cc::DisplayItemListSettings()); + list = cc::DisplayItemList::Create(cc::DisplayItemListSettings()); EXPECT_FALSE(v1->did_paint_); EXPECT_FALSE(v2->did_paint_); @@ -1054,7 +1054,7 @@ TEST_F(ViewTest, PaintIntersectsOneChild) { // invalidation. gfx::Rect first_paint(1, 1); scoped_refptr<cc::DisplayItemList> list = - cc::DisplayItemList::Create(first_paint, cc::DisplayItemListSettings()); + cc::DisplayItemList::Create(cc::DisplayItemListSettings()); root_view->Paint(ui::PaintContext(list.get(), 1.f, first_paint)); v1->Reset(); v2->Reset(); @@ -1062,7 +1062,7 @@ TEST_F(ViewTest, PaintIntersectsOneChild) { // Intersects with the second child only. gfx::Rect paint_area(3, 3, 1, 2); gfx::Rect root_area(root_view->size()); - list = cc::DisplayItemList::Create(root_area, cc::DisplayItemListSettings()); + list = cc::DisplayItemList::Create(cc::DisplayItemListSettings()); EXPECT_FALSE(v1->did_paint_); EXPECT_FALSE(v2->did_paint_); @@ -1110,7 +1110,7 @@ TEST_F(ViewTest, PaintIntersectsOneChildInRTL) { // invalidation. gfx::Rect first_paint(1, 1); scoped_refptr<cc::DisplayItemList> list = - cc::DisplayItemList::Create(first_paint, cc::DisplayItemListSettings()); + cc::DisplayItemList::Create(cc::DisplayItemListSettings()); root_view->Paint(ui::PaintContext(list.get(), 1.f, first_paint)); v1->Reset(); v2->Reset(); @@ -1118,7 +1118,7 @@ TEST_F(ViewTest, PaintIntersectsOneChildInRTL) { // Intersects with the first child only. gfx::Rect paint_area(3, 10, 1, 2); gfx::Rect root_area(root_view->size()); - list = cc::DisplayItemList::Create(root_area, cc::DisplayItemListSettings()); + list = cc::DisplayItemList::Create(cc::DisplayItemListSettings()); EXPECT_FALSE(v1->did_paint_); EXPECT_FALSE(v2->did_paint_); @@ -1155,7 +1155,7 @@ TEST_F(ViewTest, PaintInPromotedToLayer) { // invalidation. gfx::Rect first_paint(1, 1); scoped_refptr<cc::DisplayItemList> list = - cc::DisplayItemList::Create(first_paint, cc::DisplayItemListSettings()); + cc::DisplayItemList::Create(cc::DisplayItemListSettings()); v1->Paint(ui::PaintContext(list.get(), 1.f, first_paint)); v1->Reset(); v2->Reset(); @@ -1164,7 +1164,7 @@ TEST_F(ViewTest, PaintInPromotedToLayer) { gfx::Rect paint_area(25, 26); gfx::Rect view_area(root_view->size()); scoped_refptr<cc::DisplayItemList> list = - cc::DisplayItemList::Create(view_area, cc::DisplayItemListSettings()); + cc::DisplayItemList::Create(cc::DisplayItemListSettings()); // The promoted views are not painted as they are separate paint roots. root_view->Paint(ui::PaintContext(list.get(), 1.f, paint_area)); @@ -1176,7 +1176,7 @@ TEST_F(ViewTest, PaintInPromotedToLayer) { gfx::Rect paint_area(1, 1); gfx::Rect view_area(v1->size()); scoped_refptr<cc::DisplayItemList> list = - cc::DisplayItemList::Create(view_area, cc::DisplayItemListSettings()); + cc::DisplayItemList::Create(cc::DisplayItemListSettings()); // The |v1| view is painted. If it used its offset incorrect, it would think // its at (10,11) instead of at (0,0) since it is the paint root. @@ -1191,7 +1191,7 @@ TEST_F(ViewTest, PaintInPromotedToLayer) { gfx::Rect paint_area(3, 3, 1, 2); gfx::Rect view_area(v1->size()); scoped_refptr<cc::DisplayItemList> list = - cc::DisplayItemList::Create(view_area, cc::DisplayItemListSettings()); + cc::DisplayItemList::Create(cc::DisplayItemListSettings()); // The |v2| view is painted also. If it used its offset incorrect, it would // think its at (13,15) instead of at (3,4) since |v1| is the paint root. @@ -1238,7 +1238,7 @@ TEST_F(ViewTest, PaintLocalBounds) { EXPECT_EQ(gfx::Rect(0, 1000, 100, 100), v1->GetVisibleBounds()); scoped_refptr<cc::DisplayItemList> list = - cc::DisplayItemList::Create(gfx::Rect(), cc::DisplayItemListSettings()); + cc::DisplayItemList::Create(cc::DisplayItemListSettings()); ui::PaintContext context(list.get(), 1.f, gfx::Rect()); v1->Paint(context); diff --git a/chromium/ui/views/view_unittest_aura.cc b/chromium/ui/views/view_unittest_aura.cc index b1e7b2fee60..531302ae75b 100644 --- a/chromium/ui/views/view_unittest_aura.cc +++ b/chromium/ui/views/view_unittest_aura.cc @@ -122,7 +122,7 @@ TEST_F(ViewAuraTest, RecreateLayersWithWindows) { { std::unique_ptr<ui::LayerTreeOwner> cloned_owner( - wm::RecreateLayers(w1->GetNativeView(), nullptr)); + wm::RecreateLayers(w1->GetNativeView())); EXPECT_EQ(w1_layer, cloned_owner->root()); EXPECT_NE(w1_layer, w1->GetNativeView()->layer()); diff --git a/chromium/ui/views/views.gyp b/chromium/ui/views/views.gyp deleted file mode 100644 index 29964e6f5eb..00000000000 --- a/chromium/ui/views/views.gyp +++ /dev/null @@ -1,1056 +0,0 @@ -# Copyright (c) 2012 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. -{ - 'variables': { - 'chromium_code': 1, - # Sources lists shared with GN build. - 'views_sources': [ - 'accessibility/native_view_accessibility.cc', - 'accessibility/native_view_accessibility.h', - 'accessibility/native_view_accessibility_win.cc', - 'accessibility/native_view_accessibility_win.h', - 'accessible_pane_view.cc', - 'accessible_pane_view.h', - 'animation/bounds_animator.cc', - 'animation/bounds_animator.h', - 'animation/flood_fill_ink_drop_ripple.cc', - 'animation/flood_fill_ink_drop_ripple.h', - 'animation/ink_drop.h', - 'animation/ink_drop_animation_ended_reason.cc', - 'animation/ink_drop_animation_ended_reason.h', - 'animation/ink_drop_highlight.cc', - 'animation/ink_drop_highlight.h', - 'animation/ink_drop_highlight_observer.h', - 'animation/ink_drop_host.h', - 'animation/ink_drop_host_view.cc', - 'animation/ink_drop_host_view.h', - 'animation/ink_drop_impl.cc', - 'animation/ink_drop_impl.h', - 'animation/ink_drop_painted_layer_delegates.cc', - 'animation/ink_drop_painted_layer_delegates.h', - 'animation/ink_drop_ripple.cc', - 'animation/ink_drop_ripple.h', - 'animation/ink_drop_ripple_observer.h', - 'animation/ink_drop_state.cc', - 'animation/ink_drop_state.h', - 'animation/ink_drop_stub.h', - 'animation/ink_drop_stub.cc', - 'animation/scroll_animator.cc', - 'animation/scroll_animator.h', - 'animation/square_ink_drop_ripple.cc', - 'animation/square_ink_drop_ripple.h', - 'background.cc', - 'background.h', - 'border.cc', - 'border.h', - 'bubble/bubble_border.cc', - 'bubble/bubble_border.h', - 'bubble/bubble_dialog_delegate.cc', - 'bubble/bubble_dialog_delegate.h', - 'bubble/bubble_frame_view.cc', - 'bubble/bubble_frame_view.h', - 'button_drag_utils.cc', - 'button_drag_utils.h', - 'cocoa/bridged_content_view.h', - 'cocoa/bridged_content_view.mm', - 'cocoa/bridged_native_widget.h', - 'cocoa/bridged_native_widget.mm', - 'cocoa/bridged_native_widget_owner.h', - 'cocoa/drag_drop_client_mac.h', - 'cocoa/drag_drop_client_mac.mm', - 'cocoa/cocoa_mouse_capture.h', - 'cocoa/cocoa_mouse_capture.mm', - 'cocoa/cocoa_mouse_capture_delegate.h', - 'cocoa/cocoa_window_move_loop.h', - 'cocoa/cocoa_window_move_loop.mm', - 'cocoa/native_widget_mac_nswindow.h', - 'cocoa/native_widget_mac_nswindow.mm', - 'cocoa/tooltip_manager_mac.h', - 'cocoa/tooltip_manager_mac.mm', - 'cocoa/views_nswindow_delegate.h', - 'cocoa/views_nswindow_delegate.mm', - 'cocoa/views_scrollbar_bridge.h', - 'cocoa/views_scrollbar_bridge.mm', - 'cocoa/widget_owner_nswindow_adapter.h', - 'cocoa/widget_owner_nswindow_adapter.mm', - 'color_chooser/color_chooser_listener.h', - 'color_chooser/color_chooser_view.cc', - 'color_chooser/color_chooser_view.h', - 'context_menu_controller.h', - 'controls/button/blue_button.cc', - 'controls/button/blue_button.h', - 'controls/button/button.cc', - 'controls/button/button.h', - 'controls/button/checkbox.cc', - 'controls/button/checkbox.h', - 'controls/button/custom_button.cc', - 'controls/button/custom_button.h', - 'controls/button/image_button.cc', - 'controls/button/image_button.h', - 'controls/button/label_button.cc', - 'controls/button/label_button.h', - 'controls/button/label_button_border.cc', - 'controls/button/label_button_border.h', - 'controls/button/md_text_button.cc', - 'controls/button/md_text_button.h', - 'controls/button/menu_button.cc', - 'controls/button/menu_button.h', - 'controls/button/menu_button_listener.h', - 'controls/button/radio_button.cc', - 'controls/button/radio_button.h', - 'controls/combobox/combobox.cc', - 'controls/combobox/combobox.h', - 'controls/combobox/combobox_listener.h', - 'controls/focusable_border.cc', - 'controls/focusable_border.h', - 'controls/focusable_rounded_border_mac.cc', - 'controls/focusable_rounded_border_mac.h', - 'controls/glow_hover_controller.cc', - 'controls/glow_hover_controller.h', - 'controls/image_view.cc', - 'controls/image_view.h', - 'controls/label.cc', - 'controls/label.h', - 'controls/link.cc', - 'controls/link.h', - 'controls/link_listener.h', - 'controls/menu/display_change_listener_mac.cc', - 'controls/menu/menu_config.cc', - 'controls/menu/menu_config.h', - 'controls/menu/menu_config_chromeos.cc', - 'controls/menu/menu_config_linux.cc', - 'controls/menu/menu_config_mac.mm', - 'controls/menu/menu_config_win.cc', - 'controls/menu/menu_controller.cc', - 'controls/menu/menu_controller.h', - 'controls/menu/menu_controller_delegate.h', - 'controls/menu/menu_delegate.cc', - 'controls/menu/menu_delegate.h', - 'controls/menu/menu_host.cc', - 'controls/menu/menu_host.h', - 'controls/menu/menu_host_root_view.cc', - 'controls/menu/menu_host_root_view.h', - 'controls/menu/menu_image_util.cc', - 'controls/menu/menu_image_util.h', - 'controls/menu/menu_insertion_delegate_win.h', - 'controls/menu/menu_item_view.cc', - 'controls/menu/menu_item_view.h', - 'controls/menu/menu_listener.cc', - 'controls/menu/menu_listener.h', - 'controls/menu/menu_message_loop.h', - 'controls/menu/menu_message_loop_mac.cc', - 'controls/menu/menu_message_loop_mac.h', - 'controls/menu/menu_model_adapter.cc', - 'controls/menu/menu_model_adapter.h', - 'controls/menu/menu_runner.cc', - 'controls/menu/menu_runner.h', - 'controls/menu/menu_runner_handler.h', - 'controls/menu/menu_runner_impl.cc', - 'controls/menu/menu_runner_impl.h', - 'controls/menu/menu_runner_impl_adapter.cc', - 'controls/menu/menu_runner_impl_adapter.h', - 'controls/menu/menu_runner_impl_cocoa.h', - 'controls/menu/menu_runner_impl_cocoa.mm', - 'controls/menu/menu_runner_impl_interface.h', - 'controls/menu/menu_scroll_view_container.cc', - 'controls/menu/menu_scroll_view_container.h', - 'controls/menu/menu_separator.h', - 'controls/menu/menu_separator_views.cc', - 'controls/menu/menu_separator_win.cc', - 'controls/menu/menu_types.h', - 'controls/menu/native_menu_win.cc', - 'controls/menu/native_menu_win.h', - 'controls/menu/submenu_view.cc', - 'controls/menu/submenu_view.h', - 'controls/message_box_view.cc', - 'controls/message_box_view.h', - 'controls/native/native_view_host.cc', - 'controls/native/native_view_host.h', - 'controls/native/native_view_host_mac.h', - 'controls/native/native_view_host_mac.mm', - 'controls/prefix_delegate.h', - 'controls/prefix_selector.cc', - 'controls/prefix_selector.h', - 'controls/progress_bar.cc', - 'controls/progress_bar.h', - 'controls/resize_area.cc', - 'controls/resize_area.h', - 'controls/resize_area_delegate.h', - 'controls/scroll_view.cc', - 'controls/scroll_view.h', - 'controls/scrollbar/base_scroll_bar.cc', - 'controls/scrollbar/base_scroll_bar.h', - 'controls/scrollbar/base_scroll_bar_button.cc', - 'controls/scrollbar/base_scroll_bar_button.h', - 'controls/scrollbar/base_scroll_bar_thumb.cc', - 'controls/scrollbar/base_scroll_bar_thumb.h', - 'controls/scrollbar/cocoa_scroll_bar.h', - 'controls/scrollbar/cocoa_scroll_bar.mm', - 'controls/scrollbar/native_scroll_bar.cc', - 'controls/scrollbar/native_scroll_bar.h', - 'controls/scrollbar/native_scroll_bar_views.cc', - 'controls/scrollbar/native_scroll_bar_views.h', - 'controls/scrollbar/native_scroll_bar_wrapper.h', - 'controls/scrollbar/overlay_scroll_bar.cc', - 'controls/scrollbar/overlay_scroll_bar.h', - 'controls/scrollbar/scroll_bar.cc', - 'controls/scrollbar/scroll_bar.h', - 'controls/separator.cc', - 'controls/separator.h', - 'controls/single_split_view.cc', - 'controls/single_split_view.h', - 'controls/single_split_view_listener.h', - 'controls/slide_out_view.cc', - 'controls/slide_out_view.h', - 'controls/slider.cc', - 'controls/slider.h', - 'controls/styled_label.cc', - 'controls/styled_label.h', - 'controls/styled_label_listener.h', - 'controls/tabbed_pane/tabbed_pane.cc', - 'controls/tabbed_pane/tabbed_pane.h', - 'controls/tabbed_pane/tabbed_pane_listener.h', - 'controls/table/table_header.cc', - 'controls/table/table_header.h', - 'controls/table/table_utils.cc', - 'controls/table/table_utils.h', - 'controls/table/table_view.cc', - 'controls/table/table_view.h', - 'controls/table/table_view_observer.h', - 'controls/table/table_view_row_background_painter.h', - 'controls/textfield/textfield.cc', - 'controls/textfield/textfield.h', - 'controls/textfield/textfield_controller.cc', - 'controls/textfield/textfield_controller.h', - 'controls/textfield/textfield_model.cc', - 'controls/textfield/textfield_model.h', - 'controls/throbber.cc', - 'controls/throbber.h', - 'controls/tree/tree_view.cc', - 'controls/tree/tree_view.h', - 'controls/tree/tree_view_controller.cc', - 'controls/tree/tree_view_controller.h', - 'debug_utils.cc', - 'debug_utils.h', - 'drag_controller.h', - 'drag_utils.cc', - 'drag_utils.h', - 'drag_utils_mac.mm', - 'event_monitor.h', - 'event_monitor_mac.h', - 'event_monitor_mac.mm', - 'focus/external_focus_tracker.cc', - 'focus/external_focus_tracker.h', - 'focus/focus_manager.cc', - 'focus/focus_manager.h', - 'focus/focus_manager_delegate.h', - 'focus/focus_manager_factory.cc', - 'focus/focus_manager_factory.h', - 'focus/focus_search.cc', - 'focus/focus_search.h', - 'focus/view_storage.cc', - 'focus/view_storage.h', - 'focus/widget_focus_manager.cc', - 'focus/widget_focus_manager.h', - 'layout/box_layout.cc', - 'layout/box_layout.h', - 'layout/fill_layout.cc', - 'layout/fill_layout.h', - 'layout/grid_layout.cc', - 'layout/grid_layout.h', - 'layout/layout_constants.h', - 'layout/layout_manager.cc', - 'layout/layout_manager.h', - 'linux_ui/linux_ui.cc', - 'linux_ui/linux_ui.h', - 'linux_ui/status_icon_linux.cc', - 'linux_ui/status_icon_linux.h', - 'linux_ui/window_button_order_observer.h', - 'linux_ui/window_button_order_provider.cc', - 'masked_targeter_delegate.cc', - 'masked_targeter_delegate.h', - 'metrics.cc', - 'metrics.h', - 'metrics_mac.cc', - 'mouse_constants.h', - 'mouse_watcher.cc', - 'mouse_watcher.h', - 'mouse_watcher_view_host.cc', - 'mouse_watcher_view_host.h', - 'native_cursor.h', - 'native_cursor_mac.mm', - 'native_theme_delegate.h', - 'painter.cc', - 'painter.h', - 'pointer_watcher.h', - 'rect_based_targeting_utils.cc', - 'rect_based_targeting_utils.h', - 'repeat_controller.cc', - 'repeat_controller.h', - 'round_rect_painter.cc', - 'round_rect_painter.h', - 'shadow_border.cc', - 'shadow_border.h', - 'style/mac/combobox_background_mac.cc', - 'style/mac/combobox_background_mac.h', - 'style/mac/dialog_button_border_mac.cc', - 'style/mac/dialog_button_border_mac.h', - 'style/platform_style.cc', - 'style/platform_style.h', - 'style/platform_style_mac.mm', - 'view.cc', - 'view.h', - 'view_constants.cc', - 'view_constants.h', - 'view_model.cc', - 'view_model.h', - 'view_model_utils.cc', - 'view_model_utils.h', - 'view_targeter.cc', - 'view_targeter.h', - 'view_targeter_delegate.cc', - 'view_targeter_delegate.h', - 'views_delegate.cc', - 'views_delegate.h', - 'views_export.h', - 'views_exports.cc', - 'views_switches.cc', - 'views_switches.h', - 'views_touch_selection_controller_factory.h', - 'views_touch_selection_controller_factory_mac.cc', - 'widget/drop_helper.cc', - 'widget/drop_helper.h', - 'widget/monitor_win.cc', - 'widget/monitor_win.h', - 'widget/native_widget.h', - 'widget/native_widget_delegate.h', - 'widget/native_widget_mac.h', - 'widget/native_widget_mac.mm', - 'widget/native_widget_private.h', - 'widget/root_view.cc', - 'widget/root_view.h', - 'widget/root_view_targeter.cc', - 'widget/root_view_targeter.h', - 'widget/tooltip_manager.cc', - 'widget/tooltip_manager.h', - 'widget/widget.cc', - 'widget/widget.h', - 'widget/widget_aura_utils.cc', - 'widget/widget_aura_utils.h', - 'widget/widget_delegate.cc', - 'widget/widget_delegate.h', - 'widget/widget_deletion_observer.cc', - 'widget/widget_deletion_observer.h', - 'widget/widget_observer.h', - 'widget/widget_removals_observer.h', - 'window/client_view.cc', - 'window/client_view.h', - 'window/custom_frame_view.cc', - 'window/custom_frame_view.h', - 'window/dialog_client_view.cc', - 'window/dialog_client_view.h', - 'window/dialog_delegate.cc', - 'window/dialog_delegate.h', - 'window/frame_background.cc', - 'window/frame_background.h', - 'window/frame_buttons.h', - 'window/native_frame_view.cc', - 'window/native_frame_view.h', - 'window/non_client_view.cc', - 'window/non_client_view.h', - 'window/window_button_order_provider.cc', - 'window/window_button_order_provider.h', - 'window/window_resources.h', - 'window/window_shape.cc', - 'window/window_shape.h', - ], - 'views_win_sources': [ - 'widget/widget_hwnd_utils.cc', - 'widget/widget_hwnd_utils.h', - 'win/fullscreen_handler.cc', - 'win/fullscreen_handler.h', - 'win/hwnd_message_handler.cc', - 'win/hwnd_message_handler.h', - 'win/hwnd_message_handler_delegate.h', - 'win/hwnd_util.h', - 'win/hwnd_util_aurawin.cc', - 'win/scoped_fullscreen_visibility.cc', - 'win/scoped_fullscreen_visibility.h', - 'win/windows_session_change_observer.cc', - 'win/windows_session_change_observer.h', - ], - 'views_aura_sources': [ - 'accessibility/ax_aura_obj_cache.cc', - 'accessibility/ax_aura_obj_cache.h', - 'accessibility/ax_view_obj_wrapper.cc', - 'accessibility/ax_view_obj_wrapper.h', - 'accessibility/ax_widget_obj_wrapper.cc', - 'accessibility/ax_widget_obj_wrapper.h', - 'accessibility/ax_window_obj_wrapper.cc', - 'accessibility/ax_window_obj_wrapper.h', - 'bubble/bubble_window_targeter.cc', - 'bubble/bubble_window_targeter.h', - 'bubble/tray_bubble_view.cc', - 'bubble/tray_bubble_view.h', - 'controls/menu/display_change_listener_aura.cc', - 'controls/menu/menu_key_event_handler.cc', - 'controls/menu/menu_key_event_handler.h', - 'controls/menu/menu_message_loop_aura.cc', - 'controls/menu/menu_message_loop_aura.h', - 'controls/native/native_view_host_aura.cc', - 'controls/native/native_view_host_aura.h', - 'corewm/cursor_height_provider_win.cc', - 'corewm/cursor_height_provider_win.h', - 'corewm/tooltip.h', - 'corewm/tooltip_aura.cc', - 'corewm/tooltip_aura.h', - 'corewm/tooltip_controller.cc', - 'corewm/tooltip_controller.h', - 'corewm/tooltip_win.cc', - 'corewm/tooltip_win.h', - 'drag_utils_aura.cc', - 'event_monitor_aura.cc', - 'event_monitor_aura.h', - 'metrics_aura.cc', - 'native_cursor_aura.cc', - 'touchui/touch_selection_controller_impl.cc', - 'touchui/touch_selection_controller_impl.h', - 'touchui/touch_selection_menu_runner_views.cc', - 'touchui/touch_selection_menu_runner_views.h', - 'view_constants_aura.cc', - 'view_constants_aura.h', - 'views_touch_selection_controller_factory_aura.cc', - 'widget/native_widget_aura.cc', - 'widget/native_widget_aura.h', - 'widget/tooltip_manager_aura.cc', - 'widget/tooltip_manager_aura.h', - 'widget/window_reorderer.cc', - 'widget/window_reorderer.h', - ], - 'views_desktop_aura_sources': [ - 'widget/desktop_aura/desktop_capture_client.cc', - 'widget/desktop_aura/desktop_capture_client.h', - 'widget/desktop_aura/desktop_cursor_loader_updater.h', - 'widget/desktop_aura/desktop_drop_target_win.cc', - 'widget/desktop_aura/desktop_drop_target_win.h', - 'widget/desktop_aura/desktop_event_client.cc', - 'widget/desktop_aura/desktop_event_client.h', - 'widget/desktop_aura/desktop_focus_rules.cc', - 'widget/desktop_aura/desktop_focus_rules.h', - 'widget/desktop_aura/desktop_native_cursor_manager.cc', - 'widget/desktop_aura/desktop_native_cursor_manager.h', - 'widget/desktop_aura/desktop_native_widget_aura.cc', - 'widget/desktop_aura/desktop_native_widget_aura.h', - 'widget/desktop_aura/desktop_screen.h', - 'widget/desktop_aura/desktop_screen_position_client.cc', - 'widget/desktop_aura/desktop_screen_position_client.h', - 'widget/desktop_aura/desktop_window_tree_host.h', - ], - 'views_desktop_aura_linux_sources': [ - 'style/platform_style_linux.cc', - 'widget/desktop_aura/desktop_cursor_loader_updater_auralinux.cc', - 'widget/desktop_aura/desktop_cursor_loader_updater_auralinux.h', - ], - 'views_desktop_aura_x11_sources': [ - 'accessibility/native_view_accessibility_auralinux.cc', - 'accessibility/native_view_accessibility_auralinux.h', - 'widget/desktop_aura/desktop_drag_drop_client_aurax11.cc', - 'widget/desktop_aura/desktop_drag_drop_client_aurax11.h', - 'widget/desktop_aura/desktop_screen_x11.cc', - 'widget/desktop_aura/desktop_screen_x11.h', - 'widget/desktop_aura/desktop_window_tree_host_x11.cc', - 'widget/desktop_aura/desktop_window_tree_host_x11.h', - 'widget/desktop_aura/x11_desktop_handler.cc', - 'widget/desktop_aura/x11_desktop_handler.h', - 'widget/desktop_aura/x11_desktop_window_move_client.cc', - 'widget/desktop_aura/x11_desktop_window_move_client.h', - 'widget/desktop_aura/x11_move_loop.h', - 'widget/desktop_aura/x11_move_loop_delegate.h', - 'widget/desktop_aura/x11_pointer_grab.cc', - 'widget/desktop_aura/x11_pointer_grab.h', - 'widget/desktop_aura/x11_topmost_window_finder.cc', - 'widget/desktop_aura/x11_topmost_window_finder.h', - 'widget/desktop_aura/x11_whole_screen_move_loop.cc', - 'widget/desktop_aura/x11_whole_screen_move_loop.h', - 'widget/desktop_aura/x11_window_event_filter.cc', - 'widget/desktop_aura/x11_window_event_filter.h', - ], - 'views_desktop_aura_win_sources': [ - 'widget/desktop_aura/desktop_cursor_loader_updater_aurawin.cc', - 'widget/desktop_aura/desktop_drag_drop_client_win.cc', - 'widget/desktop_aura/desktop_drag_drop_client_win.h', - 'widget/desktop_aura/desktop_screen_win.cc', - 'widget/desktop_aura/desktop_screen_win.h', - 'widget/desktop_aura/desktop_window_tree_host_win.cc', - 'widget/desktop_aura/desktop_window_tree_host_win.h', - ], - 'views_desktop_aura_ozone_sources': [ - 'widget/desktop_aura/desktop_factory_ozone.cc', - 'widget/desktop_aura/desktop_factory_ozone.h', - 'widget/desktop_aura/desktop_screen_ozone.cc', - 'widget/desktop_aura/desktop_window_tree_host_ozone.cc', - ], - 'views_test_support_sources': [ - 'animation/test/flood_fill_ink_drop_ripple_test_api.cc', - 'animation/test/flood_fill_ink_drop_ripple_test_api.h', - 'animation/test/ink_drop_highlight_test_api.cc', - 'animation/test/ink_drop_highlight_test_api.h', - 'animation/test/ink_drop_host_view_test_api.cc', - 'animation/test/ink_drop_host_view_test_api.h', - 'animation/test/ink_drop_impl_test_api.cc', - 'animation/test/ink_drop_impl_test_api.h', - 'animation/test/ink_drop_ripple_test_api.cc', - 'animation/test/ink_drop_ripple_test_api.h', - 'animation/test/ink_drop_utils.cc', - 'animation/test/square_ink_drop_ripple_test_api.cc', - 'animation/test/square_ink_drop_ripple_test_api.h', - 'animation/test/test_ink_drop.cc', - 'animation/test/test_ink_drop.h', - 'animation/test/test_ink_drop_animation_observer_helper.h', - 'animation/test/test_ink_drop_highlight_observer.cc', - 'animation/test/test_ink_drop_highlight_observer.h', - 'animation/test/test_ink_drop_host.cc', - 'animation/test/test_ink_drop_host.h', - 'animation/test/test_ink_drop_ripple_observer.cc', - 'animation/test/test_ink_drop_ripple_observer.h', - 'controls/textfield/textfield_test_api.cc', - 'controls/textfield/textfield_test_api.h', - 'test/capture_tracking_view.cc', - 'test/capture_tracking_view.h', - 'test/combobox_test_api.cc', - 'test/combobox_test_api.h', - 'test/desktop_test_views_delegate.h', - 'test/desktop_test_views_delegate_mac.mm', - 'test/event_generator_delegate_mac.h', - 'test/event_generator_delegate_mac.mm', - 'test/focus_manager_test.cc', - 'test/focus_manager_test.h', - 'test/menu_runner_test_api.cc', - 'test/menu_runner_test_api.h', - 'test/menu_test_utils.cc', - 'test/menu_test_utils.h', - 'test/native_widget_factory.cc', - 'test/native_widget_factory.h', - 'test/scoped_views_test_helper.cc', - 'test/scoped_views_test_helper.h', - 'test/slider_test_api.cc', - 'test/slider_test_api.h', - 'test/test_views.cc', - 'test/test_views.h', - 'test/test_views_delegate.h', - 'test/test_views_delegate_mac.mm', - 'test/test_widget_observer.cc', - 'test/test_widget_observer.h', - 'test/views_test_base.cc', - 'test/views_test_base.h', - 'test/views_test_helper.cc', - 'test/views_test_helper.h', - 'test/views_test_helper_mac.h', - 'test/views_test_helper_mac.mm', - 'test/widget_test.cc', - 'test/widget_test.h', - 'test/widget_test_mac.mm', - 'test/x11_property_change_waiter.cc', - 'test/x11_property_change_waiter.h', - 'views_test_suite.cc', - 'views_test_suite.h', - ], - 'views_test_support_aura_sources': [ - 'corewm/tooltip_controller_test_helper.cc', - 'corewm/tooltip_controller_test_helper.h', - 'test/desktop_test_views_delegate_aura.cc', - 'test/test_views_delegate_aura.cc', - 'test/views_test_helper_aura.cc', - 'test/views_test_helper_aura.h', - 'test/widget_test_aura.cc', - ], - 'views_test_support_desktop_aura_x11_sources': [ - 'test/desktop_screen_x11_test_api.cc', - 'test/desktop_screen_x11_test_api.h', - 'test/ui_controls_factory_desktop_aurax11.cc', - 'test/ui_controls_factory_desktop_aurax11.h', - ], - 'views_unittests_sources': [ - 'accessibility/native_view_accessibility_unittest.cc', - 'accessibility/native_view_accessibility_win_unittest.cc', - 'accessible_pane_view_unittest.cc', - 'animation/bounds_animator_unittest.cc', - 'animation/flood_fill_ink_drop_ripple_unittest.cc', - 'animation/ink_drop_highlight_unittest.cc', - 'animation/ink_drop_host_view_unittest.cc', - 'animation/ink_drop_impl_unittest.cc', - 'animation/ink_drop_ripple_unittest.cc', - 'animation/ink_drop_unittest.cc', - 'animation/square_ink_drop_ripple_unittest.cc', - 'border_unittest.cc', - 'bubble/bubble_border_unittest.cc', - 'bubble/bubble_dialog_delegate_unittest.cc', - 'bubble/bubble_frame_view_unittest.cc', - 'bubble/bubble_window_targeter_unittest.cc', - 'cocoa/bridged_native_widget_unittest.mm', - 'cocoa/cocoa_mouse_capture_unittest.mm', - 'cocoa/drag_drop_client_mac_unittest.mm', - 'controls/button/blue_button_unittest.cc', - 'controls/button/custom_button_unittest.cc', - 'controls/button/image_button_unittest.cc', - 'controls/button/label_button_unittest.cc', - 'controls/button/menu_button_unittest.cc', - 'controls/combobox/combobox_unittest.cc', - 'controls/label_unittest.cc', - 'controls/menu/menu_controller_unittest.cc', - 'controls/menu/menu_item_view_unittest.cc', - 'controls/menu/menu_model_adapter_unittest.cc', - 'controls/menu/menu_runner_cocoa_unittest.mm', - 'controls/menu/menu_runner_unittest.cc', - 'controls/native/native_view_host_mac_unittest.mm', - 'controls/native/native_view_host_test_base.cc', - 'controls/native/native_view_host_test_base.h', - 'controls/native/native_view_host_unittest.cc', - 'controls/prefix_selector_unittest.cc', - 'controls/progress_bar_unittest.cc', - 'controls/scroll_view_unittest.cc', - 'controls/scrollbar/scrollbar_unittest.cc', - 'controls/single_split_view_unittest.cc', - 'controls/slider_unittest.cc', - 'controls/styled_label_unittest.cc', - 'controls/tabbed_pane/tabbed_pane_unittest.cc', - 'controls/table/table_utils_unittest.cc', - 'controls/table/table_view_unittest.cc', - 'controls/table/test_table_model.cc', - 'controls/table/test_table_model.h', - 'controls/textfield/textfield_model_unittest.cc', - 'controls/textfield/textfield_unittest.cc', - 'controls/tree/tree_view_unittest.cc', - 'event_monitor_unittest.cc', - 'focus/focus_manager_unittest.cc', - 'focus/focus_traversal_unittest.cc', - 'layout/box_layout_unittest.cc', - 'layout/grid_layout_unittest.cc', - 'rect_based_targeting_utils_unittest.cc', - 'run_all_unittests_main.cc', - 'style/mac/dialog_button_border_mac_unittest.cc', - 'view_model_unittest.cc', - 'view_model_utils_unittest.cc', - 'view_targeter_unittest.cc', - 'view_unittest.cc', - 'widget/native_widget_mac_accessibility_unittest.mm', - 'widget/native_widget_mac_unittest.mm', - 'widget/native_widget_unittest.cc', - 'widget/root_view_unittest.cc', - 'widget/widget_unittest.cc', - 'widget/window_reorderer_unittest.cc', - 'window/custom_frame_view_unittest.cc', - 'window/dialog_client_view_unittest.cc', - 'window/dialog_delegate_unittest.cc', - ], - 'views_unittests_desktop_sources': [ - 'widget/desktop_widget_unittest.cc', - ], - 'views_unittests_aura_sources': [ - 'accessibility/ax_aura_obj_cache_unittest.cc', - 'controls/native/native_view_host_aura_unittest.cc', - 'corewm/tooltip_controller_unittest.cc', - 'touchui/touch_selection_controller_impl_unittest.cc', - 'touchui/touch_selection_menu_runner_views_unittest.cc', - 'view_unittest_aura.cc', - 'widget/native_widget_aura_unittest.cc', - ], - 'views_unittests_desktop_aura_sources': [ - 'widget/desktop_aura/desktop_focus_rules_unittest.cc', - 'widget/desktop_aura/desktop_native_widget_aura_unittest.cc', - ], - 'views_unittests_desktop_aurax11_sources': [ - 'widget/desktop_aura/desktop_drag_drop_client_aurax11_unittest.cc', - 'widget/desktop_aura/desktop_screen_x11_unittest.cc', - 'widget/desktop_aura/desktop_window_tree_host_x11_unittest.cc', - ], - }, - 'targets': [ - { - # GN version: //ui/views - 'target_name': 'views', - 'type': '<(component)', - 'dependencies': [ - '../../base/base.gyp:base', - '../../base/base.gyp:base_i18n', - '../../base/third_party/dynamic_annotations/dynamic_annotations.gyp:dynamic_annotations', - '../../skia/skia.gyp:skia', - '../../third_party/icu/icu.gyp:icui18n', - '../../third_party/icu/icu.gyp:icuuc', - '../../url/url.gyp:url_lib', - '../accessibility/accessibility.gyp:accessibility', - '../accessibility/accessibility.gyp:ax_gen', - '../base/ime/ui_base_ime.gyp:ui_base_ime', - '../base/ui_base.gyp:ui_base', - '../compositor/compositor.gyp:compositor', - '../display/display.gyp:display', - '../events/events.gyp:events', - '../events/events.gyp:events_base', - '../events/platform/events_platform.gyp:events_platform', - '../gfx/gfx.gyp:gfx', - '../gfx/gfx.gyp:gfx_geometry', - '../gfx/gfx.gyp:gfx_range', - '../gfx/gfx.gyp:gfx_vector_icons', - '../native_theme/native_theme.gyp:native_theme', - '../resources/ui_resources.gyp:ui_resources', - '../strings/ui_strings.gyp:ui_strings', - 'resources/views_resources.gyp:views_resources', - ], - 'all_dependent_settings': { - 'defines': [ - 'TOOLKIT_VIEWS=1', - ], - }, - 'export_dependent_settings': [ - '../accessibility/accessibility.gyp:ax_gen', - 'resources/views_resources.gyp:views_resources', - ], - 'defines': [ - 'VIEWS_IMPLEMENTATION', - ], - 'sources': [ - '<@(views_sources)', - ], - 'conditions': [ - ['use_aura==0', { - 'sources!': [ - 'bubble/tray_bubble_view.cc', - 'bubble/tray_bubble_view.h', - ], - }], - ['chromeos==0 and use_x11==1', { - 'dependencies': [ - '../display/display.gyp:display_util', - ], - }], - ['OS=="linux" and chromeos==0 and use_ozone==0', { - 'dependencies': [ - '../../build/linux/system.gyp:atk', - ], - }], - ['OS=="linux" and chromeos==0', { - 'dependencies': [ - '../shell_dialogs/shell_dialogs.gyp:shell_dialogs', - ], - 'sources!': [ - 'window/window_button_order_provider.cc', - ], - }, { # OS=="linux" and chromeos==0 - 'sources/': [ - ['exclude', 'linux_ui'], - ], - 'sources!': [ - 'controls/menu/menu_config_linux.cc', - ], - }], - ['OS=="win"', { - 'sources': [ - '<@(views_win_sources)', - ], - 'dependencies': [ - # For accessibility - '../../third_party/iaccessible2/iaccessible2.gyp:iaccessible2', - ], - 'include_dirs': [ - '../../third_party/wtl/include', - ], - 'link_settings': { - 'libraries': [ - '-limm32.lib', - '-loleacc.lib', - '-lwtsapi32.lib', - ], - 'msvs_settings': { - 'VCLinkerTool': { - 'DelayLoadDLLs': [ - 'user32.dll', - ], - }, - }, - }, - # TODO(jschuh): crbug.com/167187 fix size_t to int truncations. - 'msvs_disabled_warnings': [ 4267, ], - }], - ['use_ozone==1', { - 'dependencies': [ - '../ozone/ozone.gyp:ozone', - ], - }], - ['use_x11==1', { - 'dependencies': [ - '../../build/linux/system.gyp:x11', - '../../build/linux/system.gyp:xrandr', - '../events/devices/events_devices.gyp:events_devices', - '../events/devices/x11/events_devices_x11.gyp:events_devices_x11', - '../events/keycodes/events_keycodes.gyp:keycodes_x11', - '../events/platform/x11/x11_events_platform.gyp:x11_events_platform', - '../gfx/x/gfx_x11.gyp:gfx_x11', - ], - }], - ['use_aura==1', { - 'sources': [ - '<@(views_aura_sources)', - ], - 'dependencies': [ - '../aura/aura.gyp:aura', - '../touch_selection/ui_touch_selection.gyp:ui_touch_selection', - '../wm/wm.gyp:wm', - ], - }], - ['use_aura and chromeos == 0', { - 'sources': [ '<@(views_desktop_aura_sources)' ], - 'conditions': [ - ['OS == "linux"', { - 'sources': [ '<@(views_desktop_aura_linux_sources)' ], - }], - ['use_x11 == 1', { - 'sources': [ '<@(views_desktop_aura_x11_sources)' ], - 'dependencies': [ - '../../build/linux/system.gyp:xext', - '../../ui/base/x/ui_base_x.gyp:ui_base_x', - ], - }], - ['OS == "win"', { - 'sources': [ '<@(views_desktop_aura_win_sources)' ], - }], - ['use_ozone==1', { - 'sources': [ '<@(views_desktop_aura_ozone_sources)' ], - }], - ], - }], - ['OS=="mac"', { - 'dependencies': [ - '../accelerated_widget_mac/accelerated_widget_mac.gyp:accelerated_widget_mac', - ], - 'link_settings': { - 'libraries': [ - # Required by bridged_native_widget.mm. - '$(SDKROOT)/System/Library/Frameworks/QuartzCore.framework', - ], - }, - }], - ], - }, # target_name: views - { - # GN version: //ui/views:test_support - 'target_name': 'views_test_support', - 'type': 'static_library', - 'dependencies': [ - '../../base/base.gyp:base', - '../../ipc/ipc.gyp:test_support_ipc', - '../../skia/skia.gyp:skia', - '../../testing/gtest.gyp:gtest', - '../base/ime/ui_base_ime.gyp:ui_base_ime', - '../base/ui_base.gyp:ui_base', - '../base/ui_base.gyp:ui_base_test_support', - '../compositor/compositor.gyp:compositor', - '../compositor/compositor.gyp:compositor_test_support', - '../events/events.gyp:events', - '../events/events.gyp:events_test_support', - '../events/platform/events_platform.gyp:events_platform', - '../gfx/gfx.gyp:gfx', - '../gfx/gfx.gyp:gfx_geometry', - '../gfx/gfx.gyp:gfx_range', - 'resources/views_resources.gyp:views_resources', - 'views', - ], - 'include_dirs': [ - '..', - ], - 'sources': [ - '<@(views_test_support_sources)', - # These are not listed in views_test_support_sources as they are not - # used by the gn target that pulls in views_test_support_sources. - 'test/native_widget_factory_desktop.cc', - 'test/platform_test_helper.cc', - 'test/platform_test_helper.h', - ], - 'conditions': [ - ['use_aura==1', { - 'sources': [ '<@(views_test_support_aura_sources)' ], - 'dependencies': [ - '../aura/aura.gyp:aura', - '../aura/aura.gyp:aura_test_support', - '../wm/wm.gyp:wm', - ], - }], - ['use_aura==1 and use_x11==1 and chromeos==0', { - 'sources': [ '<@(views_test_support_desktop_aura_x11_sources)' ], - }], - ], - }, # target_name: views_test_support - { - # GN version: //ui/views:views_unittests - 'target_name': 'views_unittests', - 'type': 'executable', - 'dependencies': [ - '../../base/base.gyp:base', - '../../base/base.gyp:base_i18n', - '../../base/base.gyp:test_support_base', - '../../skia/skia.gyp:skia', - '../../testing/gtest.gyp:gtest', - '../../third_party/icu/icu.gyp:icui18n', - '../../third_party/icu/icu.gyp:icuuc', - '../../url/url.gyp:url_lib', - '../accessibility/accessibility.gyp:accessibility', - '../base/ime/ui_base_ime.gyp:ui_base_ime', - '../base/ui_base.gyp:ui_base', - '../base/ui_base.gyp:ui_base_test_support', - '../compositor/compositor.gyp:compositor', - '../events/events.gyp:events', - '../events/events.gyp:events_base', - '../events/events.gyp:events_test_support', - '../gfx/gfx.gyp:gfx', - '../gfx/gfx.gyp:gfx_geometry', - '../gfx/gfx.gyp:gfx_range', - '../native_theme/native_theme.gyp:native_theme', - '../resources/ui_resources.gyp:ui_resources', - '../resources/ui_resources.gyp:ui_test_pak', - '../strings/ui_strings.gyp:ui_strings', - 'resources/views_resources.gyp:views_resources', - 'views', - 'views_test_support', - ], - 'include_dirs': [ - '..', - ], - 'sources': [ - '<@(views_unittests_sources)', - ], - 'conditions': [ - ['OS=="win"', { - 'dependencies': [ - '../../third_party/iaccessible2/iaccessible2.gyp:iaccessible2', - ], - 'link_settings': { - 'libraries': [ - '-limm32.lib', - '-loleacc.lib', - '-lcomctl32.lib', - ] - }, - 'include_dirs': [ - '../third_party/wtl/include', - ], - 'msvs_settings': { - 'VCManifestTool': { - 'AdditionalManifestFiles': [ - '$(ProjectDir)\\test\\views_unittest.manifest', - ], - }, - }, - }], - ['use_x11==1', { - 'dependencies': [ - '../../build/linux/system.gyp:x11', - '../../build/linux/system.gyp:xext', - '../events/devices/events_devices.gyp:events_devices', - '../events/platform/x11/x11_events_platform.gyp:x11_events_platform', - ], - }], - ['use_aura==1', { - 'sources': [ '<@(views_unittests_aura_sources)' ], - 'dependencies': [ - '../aura/aura.gyp:aura', - '../aura/aura.gyp:aura_test_support', - '../touch_selection/ui_touch_selection.gyp:ui_touch_selection', - '../wm/wm.gyp:wm', - ], - 'conditions': [ - ['chromeos == 0', { - 'sources': [ '<@(views_unittests_desktop_aura_sources)' ], - }], - ['chromeos == 0 and use_x11==1', { - 'sources': [ '<@(views_unittests_desktop_aurax11_sources)' ], - }], - ] - }], - ['chromeos==0', { - 'sources': [ '<@(views_unittests_desktop_sources)' ], - }], - ['use_x11==1', { - 'dependencies': [ - '../events/platform/x11/x11_events_platform.gyp:x11_events_platform', - ], - }], - ['OS=="mac"', { - # views_unittests not yet compiling on Mac. http://crbug.com/378134 - 'sources!': [ - 'bubble/bubble_window_targeter_unittest.cc', - 'controls/native/native_view_host_unittest.cc', - 'widget/window_reorderer_unittest.cc', - ], - 'dependencies': [ - '../accelerated_widget_mac/accelerated_widget_mac.gyp:accelerated_widget_mac', - ], - }], - ], - }, # target_name: views_unittests - ], # targets - 'conditions': [ - ['test_isolation_mode != "noop"', { - 'targets': [ - { - 'target_name': 'views_unittests_run', - 'type': 'none', - 'dependencies': [ - 'views_unittests', - ], - 'includes': [ - '../../build/isolate.gypi', - ], - 'sources': [ - 'views_unittests.isolate', - ], - 'conditions': [ - ['use_x11==1', - { - 'dependencies': [ - '../../tools/xdisplaycheck/xdisplaycheck.gyp:xdisplaycheck', - ], - } - ], - ], - }, - ], - }], - ['OS=="mac"', { - 'targets': [ - { - # GN version: //ui/views:macviews_interactive_ui_tests - 'target_name': 'macviews_interactive_ui_tests', - 'type': 'executable', - 'dependencies': [ - '../../base/base.gyp:base', - '../../base/base.gyp:test_support_base', - '../../skia/skia.gyp:skia', - '../../testing/gtest.gyp:gtest', - '../base/ui_base.gyp:ui_base_test_support', - '../compositor/compositor.gyp:compositor_test_support', - '../resources/ui_resources.gyp:ui_resources', - '../resources/ui_resources.gyp:ui_test_pak', - '../strings/ui_strings.gyp:ui_strings', - 'views', - 'views_test_support', - ], - 'sources': [ - 'cocoa/bridged_native_widget_interactive_uitest.mm', - 'run_all_unittests_main.cc', - 'views_test_suite.cc', - 'views_test_suite.h', - 'widget/native_widget_mac_interactive_uitest.mm', - ], - 'conditions': [ - ['use_aura == 1', { - 'dependencies': [ - '../aura/aura.gyp:aura', - '../wm/wm.gyp:wm', - ], - }], - ], - }, # target_name: macviews_interactive_ui_tests - ], # targets - }], - ], # conditions -} diff --git a/chromium/ui/views/views_delegate.cc b/chromium/ui/views/views_delegate.cc index 082f2f1eff6..9127e7272f7 100644 --- a/chromium/ui/views/views_delegate.cc +++ b/chromium/ui/views/views_delegate.cc @@ -93,7 +93,7 @@ content::WebContents* ViewsDelegate::CreateWebContents( return nullptr; } -base::TimeDelta ViewsDelegate::GetDefaultTextfieldObscuredRevealDuration() { +base::TimeDelta ViewsDelegate::GetTextfieldPasswordRevealDuration() { return base::TimeDelta(); } diff --git a/chromium/ui/views/views_delegate.h b/chromium/ui/views/views_delegate.h index 54b5c4383e8..4ef9dd7b415 100644 --- a/chromium/ui/views/views_delegate.h +++ b/chromium/ui/views/views_delegate.h @@ -164,8 +164,8 @@ class VIEWS_EXPORT ViewsDelegate { virtual void OnBeforeWidgetInit(Widget::InitParams* params, internal::NativeWidgetDelegate* delegate) = 0; - // Returns the default obscured text reveal duration. - virtual base::TimeDelta GetDefaultTextfieldObscuredRevealDuration(); + // Returns the password reveal duration for Textfield. + virtual base::TimeDelta GetTextfieldPasswordRevealDuration(); // Returns true if the operating system's window manager will always provide a // title bar with caption buttons (ignoring the setting to diff --git a/chromium/ui/views/views_unittests.isolate b/chromium/ui/views/views_unittests.isolate deleted file mode 100644 index d58415f3942..00000000000 --- a/chromium/ui/views/views_unittests.isolate +++ /dev/null @@ -1,70 +0,0 @@ -# Copyright (c) 2016 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. -{ - 'conditions': [ - ['use_x11==0', { - 'variables': { - 'command': [ - '../../testing/test_env.py', - '<(PRODUCT_DIR)/views_unittests<(EXECUTABLE_SUFFIX)', - '--brave-new-test-launcher', - '--test-launcher-bot-mode', - '--asan=<(asan)', - '--msan=<(msan)', - '--tsan=<(tsan)', - ], - }, - }], - ['use_x11==1', { - 'variables': { - 'command': [ - '../../testing/xvfb.py', - '<(PRODUCT_DIR)', - '<(PRODUCT_DIR)/views_unittests<(EXECUTABLE_SUFFIX)', - '--brave-new-test-launcher', - '--test-launcher-bot-mode', - '--asan=<(asan)', - '--msan=<(msan)', - '--tsan=<(tsan)', - ], - 'files': [ - '../../testing/xvfb.py', - '<(PRODUCT_DIR)/xdisplaycheck<(EXECUTABLE_SUFFIX)', - ], - }, - }], - ['OS=="linux" or OS=="mac" or OS=="win"', { - 'variables': { - 'files': [ - '../../testing/test_env.py', - '<(PRODUCT_DIR)/ui_test.pak', - ], - }, - }], - ['OS=="linux"', { - 'variables': { - 'files': [ - '<(PRODUCT_DIR)/libosmesa.so', - ], - }, - }], - ['OS=="mac"', { - 'variables': { - 'files': [ - '<(PRODUCT_DIR)/osmesa.so', - ], - }, - }], - ['OS=="win"', { - 'variables': { - 'files': [ - '<(PRODUCT_DIR)/osmesa.dll', - ], - }, - }], - ], - 'includes': [ - '../../base/base.isolate', - ], -} diff --git a/chromium/ui/views/widget/desktop_aura/desktop_drag_drop_client_aurax11.cc b/chromium/ui/views/widget/desktop_aura/desktop_drag_drop_client_aurax11.cc index 1d416a62584..6437f9737d7 100644 --- a/chromium/ui/views/widget/desktop_aura/desktop_drag_drop_client_aurax11.cc +++ b/chromium/ui/views/widget/desktop_aura/desktop_drag_drop_client_aurax11.cc @@ -23,8 +23,8 @@ #include "ui/base/dragdrop/os_exchange_data.h" #include "ui/base/dragdrop/os_exchange_data_provider_aurax11.h" #include "ui/base/x/selection_utils.h" -#include "ui/base/x/x11_foreign_window_manager.h" #include "ui/base/x/x11_util.h" +#include "ui/base/x/x11_window_event_manager.h" #include "ui/display/screen.h" #include "ui/events/event.h" #include "ui/events/event_utils.h" @@ -38,45 +38,142 @@ #include "ui/wm/public/drag_drop_client.h" #include "ui/wm/public/drag_drop_delegate.h" +// Reading recommended for understanding the implementation in this file: +// +// * The X Window System Concepts section in The X New Developer’s Guide +// * The X Selection Mechanism paper by Keith Packard +// * The Peer-to-Peer Communication by Means of Selections section in the +// ICCCM (X Consortium's Inter-Client Communication Conventions Manual) +// * The XDND specification, Drag-and-Drop Protocol for the X Window System +// * The XDS specification, The Direct Save Protocol for the X Window System +// +// All the readings are freely available online. + using aura::client::DragDropDelegate; using ui::OSExchangeData; namespace { -const int kMinXdndVersion = 5; +// The lowest XDND protocol version that we understand. +// +// The XDND protocol specification says that we must support all versions +// between 3 and the version we advertise in the XDndAware property. +constexpr int kMinXdndVersion = 3; + +// The value used in the XdndAware property. +// +// The XDND protocol version used between two windows will be the minimum +// between the two versions advertised in the XDndAware property. +constexpr int kMaxXdndVersion = 5; -const int kWillAcceptDrop = 1; -const int kWantFurtherPosEvents = 2; +constexpr int kWillAcceptDrop = 1; +constexpr int kWantFurtherPosEvents = 2; +// These actions have the same meaning as in the W3C Drag and Drop spec. const char kXdndActionCopy[] = "XdndActionCopy"; const char kXdndActionMove[] = "XdndActionMove"; const char kXdndActionLink[] = "XdndActionLink"; + +// "The target will do something that the source would not understand." The +// source only needs to provide a copy of the dragged data. +const char kXdndActionPrivate[] = "XdndActionPrivate"; + +// The target should ask the user what action it wants to perform. Intended to +// match Windows' right-click drag and drop, which shows a dropdown. +const char kXdndActionAsk[] = "XdndActionAsk"; + +// Triggers the XDS protocol. const char kXdndActionDirectSave[] = "XdndActionDirectSave"; +// Window property that will receive the drag and drop selection data. const char kChromiumDragReciever[] = "_CHROMIUM_DRAG_RECEIVER"; -const char kXdndSelection[] = "XdndSelection"; + +// Window property that contains the possible actions that will be presented to +// the user when the drag and drop action is kXdndActionAsk. +const char kXdndActionList[] = "XdndActionList"; + +// Window property that tells other applications the window understands XDND. +const char kXdndAware[] = "XdndAware"; + +// Window property on the source window and message used by the XDS protocol. +// This atom name intentionally includes the XDS protocol version (0). +// After the source sends the XdndDrop message, this property stores the +// (path-less) name of the file to be saved, and has the type text/plain, with +// an optional charset attribute. +// When receiving an XdndDrop event, the target needs to check for the +// XdndDirectSave property on the source window. The target then modifies the +// XdndDirectSave on the source window, and sends an XdndDirectSave message to +// the source. +// After the target sends the XdndDirectSave message, this property stores an +// URL indicating the location where the source should save the file. const char kXdndDirectSave0[] = "XdndDirectSave0"; +// Window property pointing to a proxy window to receive XDND target messages. +// The XDND source must check the proxy window must for the XdndAware property, +// and must send all XDND messages to the proxy instead of the target. However, +// the target field in the messages must still represent the original target +// window (the window pointed to by the cursor). +const char kXdndProxy[] = "XdndProxy"; + +// Window property that holds the supported drag and drop data types. +// This property is set on the XDND source window when the drag and drop data +// can be converted to more than 3 types. +const char kXdndTypeList[] = "XdndTypeList"; + +// Selection used by the XDND protocol to transfer data between applications. +const char kXdndSelection[] = "XdndSelection"; + +// Message sent from an XDND source to the target when the user confirms the +// drag and drop operation. +const char kXdndDrop[] = "XdndDrop"; + +// Message sent from an XDND source to the target to start the XDND protocol. +// The target must wait for an XDndPosition event before querying the data. +const char kXdndEnter[] = "XdndEnter"; + +// Message sent from an XDND target to the source in respose to an XdndDrop. +// The message must be sent whether the target acceepts the drop or not. +const char kXdndFinished[] = "XdndFinished"; + +// Message sent from an XDND source to the target when the user cancels the drag +// and drop operation. +const char kXdndLeave[] = "XdndLeave"; + +// Message sent by the XDND source when the cursor position changes. +// The source will also send an XdndPosition event right after the XdndEnter +// event, to tell the target about the initial cursor position and the desired +// drop action. +// The time stamp in the XdndPosition must be used when requesting selection +// information. +// After the target optionally acquires selection information, it must tell the +// source if it can accept the drop via an XdndStatus message. +const char kXdndPosition[] = "XdndPosition"; + +// Message sent by the XDND target in response to an XdndPosition message. +// The message informs the source if the target will accept the drop, and what +// action will be taken if the drop is accepted. +const char kXdndStatus[] = "XdndStatus"; + const char* kAtomsToCache[] = { kChromiumDragReciever, - "XdndActionAsk", + kXdndActionAsk, kXdndActionCopy, kXdndActionDirectSave, kXdndActionLink, - "XdndActionList", + kXdndActionList, kXdndActionMove, - "XdndActionPrivate", - "XdndAware", + kXdndActionPrivate, + kXdndAware, kXdndDirectSave0, - "XdndDrop", - "XdndEnter", - "XdndFinished", - "XdndLeave", - "XdndPosition", - "XdndProxy", // Proxy windows? + kXdndDrop, + kXdndEnter, + kXdndFinished, + kXdndLeave, + kXdndPosition, + kXdndProxy, kXdndSelection, - "XdndStatus", - "XdndTypeList", + kXdndStatus, + kXdndTypeList, ui::Clipboard::kMimeTypeText, NULL }; @@ -131,7 +228,7 @@ const uint32_t kMinAlpha = 32; const float kDragWidgetOpacity = .75f; static base::LazyInstance< - std::map< ::Window, views::DesktopDragDropClientAuraX11*> >::Leaky + std::map<::Window, views::DesktopDragDropClientAuraX11*> >::Leaky g_live_client_map = LAZY_INSTANCE_INITIALIZER; } // namespace @@ -153,23 +250,19 @@ class DesktopDragDropClientAuraX11::X11DragContext // copied from the other window before we process the XdndPosition // message. If we have that data already, dispatch immediately. Otherwise, // delay dispatching until we do. - void OnStartXdndPositionMessage(DesktopDragDropClientAuraX11* client, + void OnXdndPositionMessage(DesktopDragDropClientAuraX11* client, ::Atom suggested_action, ::Window source_window, + ::Time time_stamp, const gfx::Point& screen_point); - // Called to request the next target from the source window. This is only - // done on the first XdndPosition; after that, we cache the data offered by - // the source window. - void RequestNextTarget(); - // Called when XSelection data has been copied to our process. void OnSelectionNotify(const XSelectionEvent& xselection); // Clones the fetched targets. const ui::SelectionFormatMap& fetched_targets() { return fetched_targets_; } - // Reads the "XdndActionList" property from |source_window| and copies it + // Reads the kXdndActionList property from |source_window| and copies it // into |actions|. void ReadActions(); @@ -180,6 +273,11 @@ class DesktopDragDropClientAuraX11::X11DragContext DesktopDragDropClientAuraX11* source_client() { return source_client_; } private: + // Called to request the next target from the source window. This is only + // done on the first XdndPosition; after that, we cache the data offered by + // the source window. + void RequestNextTarget(); + // Masks the X11 atom |xdnd_operation|'s views representation onto // |drag_operation|. void MaskOperation(::Atom xdnd_operation, int* drag_operation) const; @@ -197,14 +295,13 @@ class DesktopDragDropClientAuraX11::X11DragContext // The XID of the window that's initiated the drag. unsigned long source_window_; + // Events that we have selected on |source_window_|. + std::unique_ptr<ui::XScopedEventSelector> source_window_events_; + // The DesktopDragDropClientAuraX11 for |source_window_| if |source_window_| // belongs to a Chrome window. DesktopDragDropClientAuraX11* source_client_; - // Used to unselect PropertyChangeMask on |source_window_| if |source_window_| - // does not belong to a Chrome window when X11DragContext is destroyed. - int foreign_window_manager_source_window_id_; - // The client we inform once we're done with requesting data. DesktopDragDropClientAuraX11* drag_drop_client_; @@ -215,19 +312,24 @@ class DesktopDragDropClientAuraX11::X11DragContext // Where the cursor is on screen. gfx::Point screen_point_; + // The time stamp of the last XdndPosition event we received. The XDND + // specification mandates that we use this time stamp when querying the source + // about the drag and drop data. + ::Time position_time_stamp_; + // A SelectionFormatMap of data that we have in our process. ui::SelectionFormatMap fetched_targets_; // The names of various data types offered by the other window that we // haven't fetched and put in |fetched_targets_| yet. - std::vector<Atom> unfetched_targets_; + std::vector<::Atom> unfetched_targets_; // XdndPosition messages have a suggested action. Qt applications exclusively // use this, instead of the XdndActionList which is backed by |actions_|. - Atom suggested_action_; + ::Atom suggested_action_; // Possible actions. - std::vector<Atom> actions_; + std::vector<::Atom> actions_; DISALLOW_COPY_AND_ASSIGN(X11DragContext); }; @@ -241,34 +343,39 @@ DesktopDragDropClientAuraX11::X11DragContext::X11DragContext( source_window_(event.data.l[0]), source_client_( DesktopDragDropClientAuraX11::GetForWindow(source_window_)), - foreign_window_manager_source_window_id_(0), drag_drop_client_(NULL), waiting_to_handle_position_(false), suggested_action_(None) { if (!source_client_) { - bool get_types = ((event.data.l[1] & 1) != 0); + bool get_types_from_property = ((event.data.l[1] & 1) != 0); - if (get_types) { + if (get_types_from_property) { if (!ui::GetAtomArrayProperty(source_window_, - "XdndTypeList", + kXdndTypeList, &unfetched_targets_)) { return; } } else { // data.l[2,3,4] contain the first three types. Unused slots can be None. for (int i = 0; i < 3; ++i) { - if (event.data.l[2+i] != None) { - unfetched_targets_.push_back(event.data.l[2+i]); + if (event.data.l[2 + i] != None) { + unfetched_targets_.push_back(event.data.l[2 + i]); } } } +#if DCHECK_IS_ON() + DVLOG(1) << "XdndEnter has " << unfetched_targets_.size() << " data types"; + for (::Atom target : unfetched_targets_) { + DVLOG(1) << "XdndEnter data type: " << target; + } +#endif // DCHECK_IS_ON() + // The window doesn't have a DesktopDragDropClientAuraX11, that means it's // created by some other process. Listen for messages on it. ui::PlatformEventSource::GetInstance()->AddPlatformEventDispatcher(this); - foreign_window_manager_source_window_id_ = - ui::XForeignWindowManager::GetInstance()->RequestEvents( - source_window_, PropertyChangeMask); + source_window_events_.reset( + new ui::XScopedEventSelector(source_window_, PropertyChangeMask)); // We must perform a full sync here because we could be racing // |source_window_|. @@ -287,15 +394,14 @@ DesktopDragDropClientAuraX11::X11DragContext::~X11DragContext() { if (!source_client_) { // Unsubscribe from message events. ui::PlatformEventSource::GetInstance()->RemovePlatformEventDispatcher(this); - ui::XForeignWindowManager::GetInstance()->CancelRequest( - foreign_window_manager_source_window_id_); } } -void DesktopDragDropClientAuraX11::X11DragContext::OnStartXdndPositionMessage( +void DesktopDragDropClientAuraX11::X11DragContext::OnXdndPositionMessage( DesktopDragDropClientAuraX11* client, ::Atom suggested_action, ::Window source_window, + ::Time time_stamp, const gfx::Point& screen_point) { DCHECK_EQ(source_window_, source_window); suggested_action_ = suggested_action; @@ -305,6 +411,7 @@ void DesktopDragDropClientAuraX11::X11DragContext::OnStartXdndPositionMessage( // the position message and ask the other window for its data. screen_point_ = screen_point; drag_drop_client_ = client; + position_time_stamp_ = time_stamp; waiting_to_handle_position_ = true; fetched_targets_ = ui::SelectionFormatMap(); @@ -315,6 +422,10 @@ void DesktopDragDropClientAuraX11::X11DragContext::OnStartXdndPositionMessage( } void DesktopDragDropClientAuraX11::X11DragContext::RequestNextTarget() { + DCHECK(!unfetched_targets_.empty()); + DCHECK(drag_drop_client_); + DCHECK(waiting_to_handle_position_); + ::Atom target = unfetched_targets_.back(); unfetched_targets_.pop_back(); @@ -323,7 +434,7 @@ void DesktopDragDropClientAuraX11::X11DragContext::RequestNextTarget() { target, atom_cache_->GetAtom(kChromiumDragReciever), local_window_, - CurrentTime); + position_time_stamp_); } void DesktopDragDropClientAuraX11::X11DragContext::OnSelectionNotify( @@ -334,13 +445,24 @@ void DesktopDragDropClientAuraX11::X11DragContext::OnSelectionNotify( return; } DCHECK(drag_drop_client_); - DCHECK_EQ(event.property, atom_cache_->GetAtom(kChromiumDragReciever)); - scoped_refptr<base::RefCountedMemory> data; - ::Atom type = None; - if (ui::GetRawBytesOfProperty(local_window_, event.property, - &data, NULL, &type)) { - fetched_targets_.Insert(event.target, data); + DVLOG(1) << "SelectionNotify, format " << event.target; + + if (event.property != None) { + DCHECK_EQ(event.property, atom_cache_->GetAtom(kChromiumDragReciever)); + + scoped_refptr<base::RefCountedMemory> data; + ::Atom type = None; + if (ui::GetRawBytesOfProperty(local_window_, event.property, + &data, NULL, &type)) { + fetched_targets_.Insert(event.target, data); + } + } else { + // The source failed to convert the drop data to the format (target in X11 + // parlance) that we asked for. This happens, even though we only ask for + // the formats advertised by the source. http://crbug.com/628099 + LOG(ERROR) << "XConvertSelection failed for source-advertised target " + << event.target; } if (!unfetched_targets_.empty()) { @@ -354,9 +476,9 @@ void DesktopDragDropClientAuraX11::X11DragContext::OnSelectionNotify( void DesktopDragDropClientAuraX11::X11DragContext::ReadActions() { if (!source_client_) { - std::vector<Atom> atom_array; + std::vector<::Atom> atom_array; if (!ui::GetAtomArrayProperty(source_window_, - "XdndActionList", + kXdndActionList, &atom_array)) { actions_.clear(); } else { @@ -372,7 +494,7 @@ void DesktopDragDropClientAuraX11::X11DragContext::ReadActions() { int DesktopDragDropClientAuraX11::X11DragContext::GetDragOperation() const { int drag_operation = ui::DragDropTypes::DRAG_NONE; - for (std::vector<Atom>::const_iterator it = actions_.begin(); + for (std::vector<::Atom>::const_iterator it = actions_.begin(); it != actions_.end(); ++it) { MaskOperation(*it, &drag_operation); } @@ -401,7 +523,7 @@ bool DesktopDragDropClientAuraX11::X11DragContext::CanDispatchEvent( uint32_t DesktopDragDropClientAuraX11::X11DragContext::DispatchEvent( const ui::PlatformEvent& event) { if (event->type == PropertyNotify && - event->xproperty.atom == atom_cache_->GetAtom("XdndActionList")) { + event->xproperty.atom == atom_cache_->GetAtom(kXdndActionList)) { ReadActions(); return ui::POST_DISPATCH_STOP_PROPAGATION; } @@ -435,8 +557,8 @@ DesktopDragDropClientAuraX11::DesktopDragDropClientAuraX11( g_live_client_map.Get()[xwindow] = this; // Mark that we are aware of drag and drop concepts. - unsigned long xdnd_version = kMinXdndVersion; - XChangeProperty(xdisplay_, xwindow_, atom_cache_.GetAtom("XdndAware"), + unsigned long xdnd_version = kMaxXdndVersion; + XChangeProperty(xdisplay_, xwindow_, atom_cache_.GetAtom(kXdndAware), XA_ATOM, 32, PropModeReplace, reinterpret_cast<unsigned char*>(&xdnd_version), 1); } @@ -453,7 +575,7 @@ DesktopDragDropClientAuraX11::~DesktopDragDropClientAuraX11() { // static DesktopDragDropClientAuraX11* DesktopDragDropClientAuraX11::GetForWindow( ::Window window) { - std::map< ::Window, DesktopDragDropClientAuraX11*>::const_iterator it = + std::map<::Window, DesktopDragDropClientAuraX11*>::const_iterator it = g_live_client_map.Get().find(window); if (it == g_live_client_map.Get().end()) return NULL; @@ -466,11 +588,21 @@ void DesktopDragDropClientAuraX11::Init() { void DesktopDragDropClientAuraX11::OnXdndEnter( const XClientMessageEvent& event) { - DVLOG(1) << "XdndEnter"; - int version = (event.data.l[1] & 0xff000000) >> 24; - if (version < 3) { - LOG(ERROR) << "Received old XdndEnter message."; + DVLOG(1) << "OnXdndEnter, version " << version; + + if (version < kMinXdndVersion) { + // This protocol version is not documented in the XDND standard (last + // revised in 1999), so we don't support it. Since don't understand the + // protocol spoken by the source, we can't tell it that we can't talk to it. + LOG(ERROR) << "XdndEnter message discarded because its version is too old."; + return; + } + if (version > kMaxXdndVersion) { + // The XDND version used should be the minimum between the versions + // advertised by the source and the target. We advertise kMaxXdndVersion, so + // this should never happen when talking to an XDND-compliant application. + LOG(ERROR) << "XdndEnter message discarded because its version is too new."; return; } @@ -480,25 +612,26 @@ void DesktopDragDropClientAuraX11::OnXdndEnter( new X11DragContext(&atom_cache_, xwindow_, event)); // In the Windows implementation, we immediately call DesktopDropTargetWin:: - // Translate(). Here, we wait until we receive an XdndPosition message - // because the enter message doesn't convey any positioning - // information. + // Translate(). The XDND specification demands that we wait until we receive + // an XdndPosition message before we use XConvertSelection or send an + // XdndStatus message. } void DesktopDragDropClientAuraX11::OnXdndLeave( const XClientMessageEvent& event) { - DVLOG(1) << "XdndLeave"; + DVLOG(1) << "OnXdndLeave"; NotifyDragLeave(); target_current_context_.reset(); } void DesktopDragDropClientAuraX11::OnXdndPosition( const XClientMessageEvent& event) { - DVLOG(1) << "XdndPosition"; + DVLOG(1) << "OnXdndPosition"; unsigned long source_window = event.data.l[0]; int x_root_window = event.data.l[2] >> 16; int y_root_window = event.data.l[2] & 0xffff; + ::Time time_stamp = event.data.l[3]; ::Atom suggested_action = event.data.l[4]; if (!target_current_context_.get()) { @@ -506,16 +639,14 @@ void DesktopDragDropClientAuraX11::OnXdndPosition( return; } - // If we already have all the data from this drag, we complete it - // immediately. - target_current_context_->OnStartXdndPositionMessage( - this, suggested_action, source_window, + target_current_context_->OnXdndPositionMessage( + this, suggested_action, source_window, time_stamp, gfx::Point(x_root_window, y_root_window)); } void DesktopDragDropClientAuraX11::OnXdndStatus( const XClientMessageEvent& event) { - DVLOG(1) << "XdndStatus"; + DVLOG(1) << "OnXdndStatus"; unsigned long source_window = event.data.l[0]; @@ -581,7 +712,7 @@ void DesktopDragDropClientAuraX11::OnXdndStatus( void DesktopDragDropClientAuraX11::OnXdndFinished( const XClientMessageEvent& event) { - DVLOG(1) << "XdndFinished"; + DVLOG(1) << "OnXdndFinished"; unsigned long source_window = event.data.l[0]; if (source_current_window_ != source_window) return; @@ -598,7 +729,7 @@ void DesktopDragDropClientAuraX11::OnXdndFinished( void DesktopDragDropClientAuraX11::OnXdndDrop( const XClientMessageEvent& event) { - DVLOG(1) << "XdndDrop"; + DVLOG(1) << "OnXdndDrop"; unsigned long source_window = event.data.l[0]; @@ -607,8 +738,9 @@ void DesktopDragDropClientAuraX11::OnXdndDrop( aura::client::DragDropDelegate* delegate = aura::client::GetDragDropDelegate(target_window_); if (delegate) { - ui::OSExchangeData data(new ui::OSExchangeDataProviderAuraX11( - xwindow_, target_current_context_->fetched_targets())); + ui::OSExchangeData data( + base::MakeUnique<ui::OSExchangeDataProviderAuraX11>( + xwindow_, target_current_context_->fetched_targets())); ui::DropTargetEvent event(data, target_window_location_, @@ -634,7 +766,7 @@ void DesktopDragDropClientAuraX11::OnXdndDrop( XEvent xev; xev.xclient.type = ClientMessage; - xev.xclient.message_type = atom_cache_.GetAtom("XdndFinished"); + xev.xclient.message_type = atom_cache_.GetAtom(kXdndFinished); xev.xclient.format = 32; xev.xclient.window = source_window; xev.xclient.data.l[0] = xwindow_; @@ -646,6 +778,7 @@ void DesktopDragDropClientAuraX11::OnXdndDrop( void DesktopDragDropClientAuraX11::OnSelectionNotify( const XSelectionEvent& xselection) { + DVLOG(1) << "OnSelectionNotify"; if (target_current_context_) target_current_context_->OnSelectionNotify(xselection); @@ -680,7 +813,7 @@ int DesktopDragDropClientAuraX11::StartDragAndDrop( source_provider_->TakeOwnershipOfSelection(); - std::vector< ::Atom> actions = GetOfferedDragOperations(); + std::vector<::Atom> actions = GetOfferedDragOperations(); if (!source_provider_->file_contents_name().empty()) { actions.push_back(atom_cache_.GetAtom(kXdndActionDirectSave)); ui::SetStringProperty( @@ -689,7 +822,7 @@ int DesktopDragDropClientAuraX11::StartDragAndDrop( atom_cache_.GetAtom(ui::Clipboard::kMimeTypeText), source_provider_->file_contents_name().AsUTF8Unsafe()); } - ui::SetAtomArrayProperty(xwindow_, "XdndActionList", "ATOM", actions); + ui::SetAtomArrayProperty(xwindow_, kXdndActionList, "ATOM", actions); gfx::ImageSkia drag_image = source_provider_->GetDragImage(); if (IsValidDragImage(drag_image)) { @@ -729,7 +862,7 @@ int DesktopDragDropClientAuraX11::StartDragAndDrop( source_provider_ = NULL; g_current_drag_drop_client = NULL; drag_operation_ = 0; - XDeleteProperty(xdisplay_, xwindow_, atom_cache_.GetAtom("XdndActionList")); + XDeleteProperty(xdisplay_, xwindow_, atom_cache_.GetAtom(kXdndActionList)); XDeleteProperty(xdisplay_, xwindow_, atom_cache_.GetAtom(kXdndDirectSave0)); return negotiated_operation_; @@ -857,14 +990,18 @@ XID DesktopDragDropClientAuraX11::FindWindowFor( if (target == None) return None; + // TODO(crbug/651775): The proxy window should be reported separately from the + // target window. XDND messages should be sent to the proxy, and their + // window field should point to the target. + // Figure out which window we should test as XdndAware. If |target| has // XdndProxy, it will set that proxy on target, and if not, |target|'s // original value will remain. - ui::GetXIDProperty(target, "XdndProxy", &target); + ui::GetXIDProperty(target, kXdndProxy, &target); int version; - if (ui::GetIntProperty(target, "XdndAware", &version) && - version >= kMinXdndVersion) { + if (ui::GetIntProperty(target, kXdndAware, &version) && + version >= kMaxXdndVersion) { return target; } return None; @@ -878,22 +1015,22 @@ void DesktopDragDropClientAuraX11::SendXClientEvent(::Window xid, DesktopDragDropClientAuraX11* short_circuit = GetForWindow(xid); if (short_circuit) { Atom message_type = xev->xclient.message_type; - if (message_type == atom_cache_.GetAtom("XdndEnter")) { + if (message_type == atom_cache_.GetAtom(kXdndEnter)) { short_circuit->OnXdndEnter(xev->xclient); return; - } else if (message_type == atom_cache_.GetAtom("XdndLeave")) { + } else if (message_type == atom_cache_.GetAtom(kXdndLeave)) { short_circuit->OnXdndLeave(xev->xclient); return; - } else if (message_type == atom_cache_.GetAtom("XdndPosition")) { + } else if (message_type == atom_cache_.GetAtom(kXdndPosition)) { short_circuit->OnXdndPosition(xev->xclient); return; - } else if (message_type == atom_cache_.GetAtom("XdndStatus")) { + } else if (message_type == atom_cache_.GetAtom(kXdndStatus)) { short_circuit->OnXdndStatus(xev->xclient); return; - } else if (message_type == atom_cache_.GetAtom("XdndFinished")) { + } else if (message_type == atom_cache_.GetAtom(kXdndFinished)) { short_circuit->OnXdndFinished(xev->xclient); return; - } else if (message_type == atom_cache_.GetAtom("XdndDrop")) { + } else if (message_type == atom_cache_.GetAtom(kXdndDrop)) { short_circuit->OnXdndDrop(xev->xclient); return; } @@ -981,8 +1118,9 @@ void DesktopDragDropClientAuraX11::DragTranslate( if (!*delegate) return; - data->reset(new OSExchangeData(new ui::OSExchangeDataProviderAuraX11( - xwindow_, target_current_context_->fetched_targets()))); + data->reset(new OSExchangeData( + base::MakeUnique<ui::OSExchangeDataProviderAuraX11>( + xwindow_, target_current_context_->fetched_targets()))); gfx::Point location = root_location; aura::Window::ConvertPointToTarget(root_window_, target_window_, &location); @@ -1051,8 +1189,8 @@ DesktopDragDropClientAuraX11::AtomToDragOperation(::Atom atom) { return ui::DragDropTypes::DRAG_NONE; } -std::vector< ::Atom> DesktopDragDropClientAuraX11::GetOfferedDragOperations() { - std::vector< ::Atom> operations; +std::vector<::Atom> DesktopDragDropClientAuraX11::GetOfferedDragOperations() { + std::vector<::Atom> operations; if (drag_operation_ & ui::DragDropTypes::DRAG_COPY) operations.push_back(atom_cache_.GetAtom(kXdndActionCopy)); if (drag_operation_ & ui::DragDropTypes::DRAG_MOVE) @@ -1086,7 +1224,7 @@ void DesktopDragDropClientAuraX11::CompleteXdndPosition( // sets this nor respects it if set. XEvent xev; xev.xclient.type = ClientMessage; - xev.xclient.message_type = atom_cache_.GetAtom("XdndStatus"); + xev.xclient.message_type = atom_cache_.GetAtom(kXdndStatus); xev.xclient.format = 32; xev.xclient.window = source_window; xev.xclient.data.l[0] = xwindow_; @@ -1102,11 +1240,11 @@ void DesktopDragDropClientAuraX11::CompleteXdndPosition( void DesktopDragDropClientAuraX11::SendXdndEnter(::Window dest_window) { XEvent xev; xev.xclient.type = ClientMessage; - xev.xclient.message_type = atom_cache_.GetAtom("XdndEnter"); + xev.xclient.message_type = atom_cache_.GetAtom(kXdndEnter); xev.xclient.format = 32; xev.xclient.window = dest_window; xev.xclient.data.l[0] = xwindow_; - xev.xclient.data.l[1] = (kMinXdndVersion << 24); // The version number. + xev.xclient.data.l[1] = (kMaxXdndVersion << 24); // The version number. xev.xclient.data.l[2] = 0; xev.xclient.data.l[3] = 0; xev.xclient.data.l[4] = 0; @@ -1116,7 +1254,7 @@ void DesktopDragDropClientAuraX11::SendXdndEnter(::Window dest_window) { if (targets.size() > 3) { xev.xclient.data.l[1] |= 1; - ui::SetAtomArrayProperty(xwindow_, "XdndTypeList", "ATOM", targets); + ui::SetAtomArrayProperty(xwindow_, kXdndTypeList, "ATOM", targets); } else { // Pack the targets into the enter message. for (size_t i = 0; i < targets.size(); ++i) @@ -1129,7 +1267,7 @@ void DesktopDragDropClientAuraX11::SendXdndEnter(::Window dest_window) { void DesktopDragDropClientAuraX11::SendXdndLeave(::Window dest_window) { XEvent xev; xev.xclient.type = ClientMessage; - xev.xclient.message_type = atom_cache_.GetAtom("XdndLeave"); + xev.xclient.message_type = atom_cache_.GetAtom(kXdndLeave); xev.xclient.format = 32; xev.xclient.window = dest_window; xev.xclient.data.l[0] = xwindow_; @@ -1148,7 +1286,7 @@ void DesktopDragDropClientAuraX11::SendXdndPosition( XEvent xev; xev.xclient.type = ClientMessage; - xev.xclient.message_type = atom_cache_.GetAtom("XdndPosition"); + xev.xclient.message_type = atom_cache_.GetAtom(kXdndPosition); xev.xclient.format = 32; xev.xclient.window = dest_window; xev.xclient.data.l[0] = xwindow_; @@ -1173,7 +1311,7 @@ void DesktopDragDropClientAuraX11::SendXdndPosition( void DesktopDragDropClientAuraX11::SendXdndDrop(::Window dest_window) { XEvent xev; xev.xclient.type = ClientMessage; - xev.xclient.message_type = atom_cache_.GetAtom("XdndDrop"); + xev.xclient.message_type = atom_cache_.GetAtom(kXdndDrop); xev.xclient.format = 32; xev.xclient.window = dest_window; xev.xclient.data.l[0] = xwindow_; @@ -1188,7 +1326,7 @@ void DesktopDragDropClientAuraX11::CreateDragWidget( const gfx::ImageSkia& image) { Widget* widget = new Widget; Widget::InitParams params(Widget::InitParams::TYPE_DRAG); - params.opacity = Widget::InitParams::OPAQUE_WINDOW; + params.opacity = Widget::InitParams::TRANSLUCENT_WINDOW; params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; params.accept_events = false; diff --git a/chromium/ui/views/widget/desktop_aura/desktop_drop_target_win.cc b/chromium/ui/views/widget/desktop_aura/desktop_drop_target_win.cc index 875763dad35..76e022d1193 100644 --- a/chromium/ui/views/widget/desktop_aura/desktop_drop_target_win.cc +++ b/chromium/ui/views/widget/desktop_aura/desktop_drop_target_win.cc @@ -4,6 +4,7 @@ #include "ui/views/widget/desktop_aura/desktop_drop_target_win.h" +#include "base/memory/ptr_util.h" #include "base/metrics/histogram.h" #include "base/win/win_util.h" #include "ui/aura/window.h" @@ -148,7 +149,8 @@ void DesktopDropTargetWin::Translate( if (!*delegate) return; - data->reset(new OSExchangeData(new OSExchangeDataProviderWin(data_object))); + data->reset(new OSExchangeData( + base::MakeUnique<OSExchangeDataProviderWin>(data_object))); location = root_location; aura::Window::ConvertPointToTarget(root_window_, target_window_, &location); event->reset(new ui::DropTargetEvent( diff --git a/chromium/ui/views/widget/desktop_aura/desktop_native_widget_aura.cc b/chromium/ui/views/widget/desktop_aura/desktop_native_widget_aura.cc index d49af384df8..b3d06cfd2f8 100644 --- a/chromium/ui/views/widget/desktop_aura/desktop_native_widget_aura.cc +++ b/chromium/ui/views/widget/desktop_aura/desktop_native_widget_aura.cc @@ -37,6 +37,7 @@ #include "ui/views/widget/desktop_aura/desktop_screen_position_client.h" #include "ui/views/widget/desktop_aura/desktop_window_tree_host.h" #include "ui/views/widget/drop_helper.h" +#include "ui/views/widget/focus_manager_event_handler.h" #include "ui/views/widget/native_widget_aura.h" #include "ui/views/widget/root_view.h" #include "ui/views/widget/tooltip_manager_aura.h" @@ -208,27 +209,6 @@ class DesktopNativeWidgetAuraWindowTreeClient : } // namespace -class FocusManagerEventHandler : public ui::EventHandler { - public: - explicit FocusManagerEventHandler( - DesktopNativeWidgetAura* desktop_native_widget_aura) - : desktop_native_widget_aura_(desktop_native_widget_aura) {} - - // Implementation of ui::EventHandler: - void OnKeyEvent(ui::KeyEvent* event) override { - Widget* widget = desktop_native_widget_aura_->GetWidget(); - if (widget && widget->GetFocusManager()->GetFocusedView() && - !widget->GetFocusManager()->OnKeyEvent(*event)) { - event->StopPropagation(); - } - } - - private: - DesktopNativeWidgetAura* desktop_native_widget_aura_; - - DISALLOW_COPY_AND_ASSIGN(FocusManagerEventHandler); -}; - class RootWindowDestructionObserver : public aura::WindowObserver { public: explicit RootWindowDestructionObserver(DesktopNativeWidgetAura* parent) @@ -316,6 +296,8 @@ void DesktopNativeWidgetAura::OnHostClosed() { capture_client_.reset(); // Uses host_->dispatcher() at destruction. + focus_manager_event_handler_.reset(); + // FocusController uses |content_window_|. Destroy it now so that we don't // have to worry about the possibility of FocusController attempting to use // |content_window_| after it's been destroyed but before all child windows @@ -515,8 +497,8 @@ void DesktopNativeWidgetAura::InitNativeWidget( } if (params.type == Widget::InitParams::TYPE_WINDOW) { - focus_manager_event_handler_.reset(new FocusManagerEventHandler(this)); - host_->window()->AddPreTargetHandler(focus_manager_event_handler_.get()); + focus_manager_event_handler_ = base::MakeUnique<FocusManagerEventHandler>( + GetWidget(), host_->window()); } event_client_.reset(new DesktopEventClient); @@ -651,6 +633,9 @@ void DesktopNativeWidgetAura::SetWindowIcons(const gfx::ImageSkia& window_icon, const gfx::ImageSkia& app_icon) { if (content_window_) desktop_window_tree_host_->SetWindowIcons(window_icon, app_icon); + + NativeWidgetAura::AssignIconToAuraWindow(content_window_, window_icon, + app_icon); } void DesktopNativeWidgetAura::InitModalType(ui::ModalType modal_type) { @@ -704,12 +689,9 @@ void DesktopNativeWidgetAura::StackAtTop() { desktop_window_tree_host_->StackAtTop(); } -void DesktopNativeWidgetAura::StackBelow(gfx::NativeView native_view) { -} - -void DesktopNativeWidgetAura::SetShape(SkRegion* shape) { +void DesktopNativeWidgetAura::SetShape(std::unique_ptr<SkRegion> shape) { if (content_window_) - desktop_window_tree_host_->SetShape(shape); + desktop_window_tree_host_->SetShape(std::move(shape)); } void DesktopNativeWidgetAura::Close() { @@ -743,6 +725,7 @@ void DesktopNativeWidgetAura::Hide() { void DesktopNativeWidgetAura::ShowMaximizedWithBounds( const gfx::Rect& restored_bounds) { + // IsVisible() should check the same objects here for visibility. if (!content_window_) return; desktop_window_tree_host_->ShowMaximizedWithBounds(restored_bounds); @@ -750,6 +733,7 @@ void DesktopNativeWidgetAura::ShowMaximizedWithBounds( } void DesktopNativeWidgetAura::ShowWithWindowState(ui::WindowShowState state) { + // IsVisible() should check the same objects here for visibility. if (!content_window_) return; desktop_window_tree_host_->ShowWindowWithState(state); @@ -757,7 +741,13 @@ void DesktopNativeWidgetAura::ShowWithWindowState(ui::WindowShowState state) { } bool DesktopNativeWidgetAura::IsVisible() const { - return content_window_ && desktop_window_tree_host_->IsVisible(); + // The objects checked here should be the same objects changed in + // ShowWithWindowState and ShowMaximizedWithBounds. For example, MS Windows + // platform code might show the desktop window tree host early, meaning we + // aren't fully visible as we haven't shown the content window. Callers may + // short-circuit a call to show this widget if they think its already visible. + return content_window_ && content_window_->IsVisible() && + desktop_window_tree_host_->IsVisible(); } void DesktopNativeWidgetAura::Activate() { @@ -788,6 +778,11 @@ void DesktopNativeWidgetAura::SetVisibleOnAllWorkspaces(bool always_visible) { desktop_window_tree_host_->SetVisibleOnAllWorkspaces(always_visible); } +bool DesktopNativeWidgetAura::IsVisibleOnAllWorkspaces() const { + return content_window_ && + desktop_window_tree_host_->IsVisibleOnAllWorkspaces(); +} + void DesktopNativeWidgetAura::Maximize() { if (content_window_) desktop_window_tree_host_->Maximize(); diff --git a/chromium/ui/views/widget/desktop_aura/desktop_native_widget_aura.h b/chromium/ui/views/widget/desktop_aura/desktop_native_widget_aura.h index 1e57f7f2c58..a6a05912fa2 100644 --- a/chromium/ui/views/widget/desktop_aura/desktop_native_widget_aura.h +++ b/chromium/ui/views/widget/desktop_aura/desktop_native_widget_aura.h @@ -128,8 +128,7 @@ class VIEWS_EXPORT DesktopNativeWidgetAura void SetSize(const gfx::Size& size) override; void StackAbove(gfx::NativeView native_view) override; void StackAtTop() override; - void StackBelow(gfx::NativeView native_view) override; - void SetShape(SkRegion* shape) override; + void SetShape(std::unique_ptr<SkRegion> shape) override; void Close() override; void CloseNow() override; void Show() override; @@ -143,6 +142,7 @@ class VIEWS_EXPORT DesktopNativeWidgetAura void SetAlwaysOnTop(bool always_on_top) override; bool IsAlwaysOnTop() const override; void SetVisibleOnAllWorkspaces(bool always_visible) override; + bool IsVisibleOnAllWorkspaces() const override; void Maximize() override; void Minimize() override; bool IsMaximized() const override; @@ -231,7 +231,6 @@ class VIEWS_EXPORT DesktopNativeWidgetAura const gfx::Point& new_origin) override; private: - friend class FocusManagerEventHandler; friend class RootWindowDestructionObserver; // To save a clear on platforms where the window is never transparent, the diff --git a/chromium/ui/views/widget/desktop_aura/desktop_native_widget_aura_unittest.cc b/chromium/ui/views/widget/desktop_aura/desktop_native_widget_aura_unittest.cc index 5865426151c..ff2437ee1e4 100644 --- a/chromium/ui/views/widget/desktop_aura/desktop_native_widget_aura_unittest.cc +++ b/chromium/ui/views/widget/desktop_aura/desktop_native_widget_aura_unittest.cc @@ -7,6 +7,7 @@ #include "base/bind.h" #include "base/macros.h" #include "base/run_loop.h" +#include "base/single_thread_task_runner.h" #include "build/build_config.h" #include "ui/aura/client/aura_constants.h" #include "ui/aura/client/cursor_client.h" @@ -101,6 +102,21 @@ TEST_F(DesktopNativeWidgetAuraTest, NativeViewInitiallyHidden) { EXPECT_FALSE(widget.GetNativeView()->IsVisible()); } +// Verifies that if the DesktopWindowTreeHost is already shown, the native view +// still reports not visible as we haven't shown the content window. +TEST_F(DesktopNativeWidgetAuraTest, WidgetNotVisibleOnlyWindowTreeHostShown) { + Widget widget; + Widget::InitParams init_params = + CreateParams(Widget::InitParams::TYPE_WINDOW); + init_params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; + init_params.native_widget = new DesktopNativeWidgetAura(&widget); + widget.Init(init_params); + DesktopNativeWidgetAura* desktop_native_widget_aura = + static_cast<DesktopNativeWidgetAura*>(widget.native_widget()); + desktop_native_widget_aura->host()->Show(); + EXPECT_FALSE(widget.IsVisible()); +} + // Verify that the cursor state is shared between two native widgets. TEST_F(DesktopNativeWidgetAuraTest, GlobalCursorState) { // Create two native widgets, each owning different root windows. @@ -239,10 +255,10 @@ TEST_F(DesktopNativeWidgetAuraTest, WidgetCanBeDestroyedFromNestedLoop) { // |RunWithDispatcher()| below. base::RunLoop run_loop; base::Closure quit_runloop = run_loop.QuitClosure(); - message_loop()->PostTask(FROM_HERE, - base::Bind(&QuitNestedLoopAndCloseWidget, - base::Passed(&widget), - base::Unretained(&quit_runloop))); + message_loop()->task_runner()->PostTask( + FROM_HERE, + base::Bind(&QuitNestedLoopAndCloseWidget, base::Passed(&widget), + base::Unretained(&quit_runloop))); run_loop.Run(); } @@ -509,6 +525,8 @@ TEST_F(DesktopAuraWidgetTest, CloseWidgetDuringMouseReleased) { RunCloseWidgetDuringDispatchTest(this, ui::ET_MOUSE_RELEASED); } +namespace { + // Provides functionality to create a window modal dialog. class ModalDialogDelegate : public DialogDelegateView { public: @@ -522,6 +540,8 @@ class ModalDialogDelegate : public DialogDelegateView { DISALLOW_COPY_AND_ASSIGN(ModalDialogDelegate); }; +} // namespace + // This test verifies that whether mouse events when a modal dialog is // displayed are eaten or recieved by the dialog. TEST_F(WidgetTest, WindowMouseModalityTest) { diff --git a/chromium/ui/views/widget/desktop_aura/desktop_screen_x11.cc b/chromium/ui/views/widget/desktop_aura/desktop_screen_x11.cc index 06335ed0071..96f20cca216 100644 --- a/chromium/ui/views/widget/desktop_aura/desktop_screen_x11.cc +++ b/chromium/ui/views/widget/desktop_aura/desktop_screen_x11.cc @@ -72,7 +72,7 @@ std::vector<display::Display> GetFallbackDisplayList() { gfx::Rect bounds_in_pixels(0, 0, width, height); display::Display gfx_display(0, bounds_in_pixels); if (!display::Display::HasForceDeviceScaleFactor() && - !ui::IsDisplaySizeBlackListed(physical_size)) { + !display::IsDisplaySizeBlackListed(physical_size)) { const float device_scale_factor = GetDeviceScaleFactor(); DCHECK_LE(1.0f, device_scale_factor); gfx_display.SetScaleAndBounds(device_scale_factor, bounds_in_pixels); @@ -327,14 +327,16 @@ std::vector<display::Display> DesktopScreenX11::BuildDisplaysFromXRandRInfo() { if (!is_connected) continue; + bool is_primary_display = output_id == primary_display_id; + if (output_info->crtc) { gfx::XScopedPtr<XRRCrtcInfo, gfx::XObjectDeleter<XRRCrtcInfo, void, XRRFreeCrtcInfo>> crtc(XRRGetCrtcInfo(xdisplay_, resources.get(), output_info->crtc)); int64_t display_id = -1; - if (!ui::EDIDParserX11(output_id).GetDisplayId(static_cast<uint8_t>(i), - &display_id)) { + if (!display::EDIDParserX11(output_id).GetDisplayId( + static_cast<uint8_t>(i), &display_id)) { // It isn't ideal, but if we can't parse the EDID data, fallback on the // display number. display_id = i; @@ -349,7 +351,9 @@ std::vector<display::Display> DesktopScreenX11::BuildDisplaysFromXRandRInfo() { if (has_work_area) { gfx::Rect intersection_in_pixels = crtc_bounds; - intersection_in_pixels.Intersect(work_area_in_pixels); + if (is_primary_display) { + intersection_in_pixels.Intersect(work_area_in_pixels); + } // SetScaleAndBounds() above does the conversion from pixels to DIP for // us, but set_work_area does not, so we need to do it here. display.set_work_area(gfx::Rect( @@ -374,7 +378,7 @@ std::vector<display::Display> DesktopScreenX11::BuildDisplaysFromXRandRInfo() { break; } - if (output_id == primary_display_id) + if (is_primary_display) primary_display_index_ = displays.size(); displays.push_back(display); diff --git a/chromium/ui/views/widget/desktop_aura/desktop_window_tree_host.h b/chromium/ui/views/widget/desktop_aura/desktop_window_tree_host.h index 9136f26665e..17c38520e74 100644 --- a/chromium/ui/views/widget/desktop_aura/desktop_window_tree_host.h +++ b/chromium/ui/views/widget/desktop_aura/desktop_window_tree_host.h @@ -19,7 +19,7 @@ class Window; namespace client { class DragDropClient; } -} +} // namespace aura namespace gfx { class ImageSkia; @@ -95,8 +95,8 @@ class VIEWS_EXPORT DesktopWindowTreeHost { virtual gfx::Rect GetWorkAreaBoundsInScreen() const = 0; // Sets the shape of the root window. If |native_region| is NULL then the - // window reverts to rectangular. Takes ownership of |native_region|. - virtual void SetShape(SkRegion* native_region) = 0; + // window reverts to rectangular. + virtual void SetShape(std::unique_ptr<SkRegion> native_region) = 0; virtual void Activate() = 0; virtual void Deactivate() = 0; @@ -113,6 +113,7 @@ class VIEWS_EXPORT DesktopWindowTreeHost { virtual bool IsAlwaysOnTop() const = 0; virtual void SetVisibleOnAllWorkspaces(bool always_visible) = 0; + virtual bool IsVisibleOnAllWorkspaces() const = 0; // Returns true if the title changed. virtual bool SetWindowTitle(const base::string16& title) = 0; diff --git a/chromium/ui/views/widget/desktop_aura/desktop_window_tree_host_win.cc b/chromium/ui/views/widget/desktop_aura/desktop_window_tree_host_win.cc index de96b6309b1..964fe4a0b59 100644 --- a/chromium/ui/views/widget/desktop_aura/desktop_window_tree_host_win.cc +++ b/chromium/ui/views/widget/desktop_aura/desktop_window_tree_host_win.cc @@ -304,35 +304,35 @@ gfx::Rect DesktopWindowTreeHostWin::GetWorkAreaBoundsInScreen() const { return display::win::ScreenWin::ScreenToDIPRect(GetHWND(), pixel_bounds); } -void DesktopWindowTreeHostWin::SetShape(SkRegion* native_region) { - if (native_region) { - // TODO(wez): This would be a lot simpler if we were passed an SkPath. - // See crbug.com/410593. - SkRegion* shape = native_region; - SkRegion device_region; - if (display::win::GetDPIScale() > 1.0) { - shape = &device_region; - const float& scale = display::win::GetDPIScale(); - std::vector<SkIRect> rects; - for (SkRegion::Iterator it(*native_region); !it.done(); it.next()) { - const SkIRect& rect = it.rect(); - SkRect scaled_rect = - SkRect::MakeLTRB(rect.left() * scale, rect.top() * scale, - rect.right() * scale, rect.bottom() * scale); - SkIRect rounded_scaled_rect; - scaled_rect.roundOut(&rounded_scaled_rect); - rects.push_back(rounded_scaled_rect); - } - if (!rects.empty()) - device_region.setRects(&rects[0], rects.size()); - } +void DesktopWindowTreeHostWin::SetShape( + std::unique_ptr<SkRegion> native_region) { + if (!native_region) { + message_handler_->SetRegion(nullptr); + return; + } - message_handler_->SetRegion(gfx::CreateHRGNFromSkRegion(*shape)); - } else { - message_handler_->SetRegion(NULL); + // TODO(wez): This would be a lot simpler if we were passed an SkPath. + // See crbug.com/410593. + SkRegion* shape = native_region.get(); + SkRegion device_region; + const float scale = display::win::ScreenWin::GetScaleFactorForHWND(GetHWND()); + if (scale > 1.0) { + shape = &device_region; + std::vector<SkIRect> rects; + for (SkRegion::Iterator it(*native_region); !it.done(); it.next()) { + const SkIRect& rect = it.rect(); + SkRect scaled_rect = + SkRect::MakeLTRB(rect.left() * scale, rect.top() * scale, + rect.right() * scale, rect.bottom() * scale); + SkIRect rounded_scaled_rect; + scaled_rect.roundOut(&rounded_scaled_rect); + rects.push_back(rounded_scaled_rect); + } + if (!rects.empty()) + device_region.setRects(&rects[0], rects.size()); } - delete native_region; + message_handler_->SetRegion(gfx::CreateHRGNFromSkRegion(*shape)); } void DesktopWindowTreeHostWin::Activate() { @@ -380,7 +380,11 @@ bool DesktopWindowTreeHostWin::IsAlwaysOnTop() const { } void DesktopWindowTreeHostWin::SetVisibleOnAllWorkspaces(bool always_visible) { - // Windows does not have the concept of workspaces. + // Chrome does not yet support Windows 10 desktops. +} + +bool DesktopWindowTreeHostWin::IsVisibleOnAllWorkspaces() const { + return false; } bool DesktopWindowTreeHostWin::SetWindowTitle(const base::string16& title) { @@ -928,7 +932,7 @@ void DesktopWindowTreeHostWin::HandleWindowSizeChanging() { compositor()->DisableSwapUntilResize(); } -void DesktopWindowTreeHostWin::HandleWindowSizeChanged() { +void DesktopWindowTreeHostWin::HandleWindowSizeUnchanged() { // A resize may not have occurred if the window size happened not to have // changed (can occur on Windows 10 when snapping a window to the side of // the screen). In that case do a resize to the current size to reenable diff --git a/chromium/ui/views/widget/desktop_aura/desktop_window_tree_host_win.h b/chromium/ui/views/widget/desktop_aura/desktop_window_tree_host_win.h index e71604dd928..884df902ab2 100644 --- a/chromium/ui/views/widget/desktop_aura/desktop_window_tree_host_win.h +++ b/chromium/ui/views/widget/desktop_aura/desktop_window_tree_host_win.h @@ -68,7 +68,7 @@ class VIEWS_EXPORT DesktopWindowTreeHostWin gfx::Rect GetRestoredBounds() const override; std::string GetWorkspace() const override; gfx::Rect GetWorkAreaBoundsInScreen() const override; - void SetShape(SkRegion* native_region) override; + void SetShape(std::unique_ptr<SkRegion> native_region) override; void Activate() override; void Deactivate() override; bool IsActive() const override; @@ -81,6 +81,7 @@ class VIEWS_EXPORT DesktopWindowTreeHostWin void SetAlwaysOnTop(bool always_on_top) override; bool IsAlwaysOnTop() const override; void SetVisibleOnAllWorkspaces(bool always_visible) override; + bool IsVisibleOnAllWorkspaces() const override; bool SetWindowTitle(const base::string16& title) override; void ClearNativeFocus() override; Widget::MoveLoopResult RunMoveLoop( @@ -195,7 +196,7 @@ class VIEWS_EXPORT DesktopWindowTreeHostWin void PostHandleMSG(UINT message, WPARAM w_param, LPARAM l_param) override; bool HandleScrollEvent(const ui::ScrollEvent& event) override; void HandleWindowSizeChanging() override; - void HandleWindowSizeChanged() override; + void HandleWindowSizeUnchanged() override; void HandleWindowScaleFactorChanged(float window_scale_factor) override; Widget* GetWidget(); diff --git a/chromium/ui/views/widget/desktop_aura/desktop_window_tree_host_x11.cc b/chromium/ui/views/widget/desktop_aura/desktop_window_tree_host_x11.cc index 82ba30c804f..816faa3f312 100644 --- a/chromium/ui/views/widget/desktop_aura/desktop_window_tree_host_x11.cc +++ b/chromium/ui/views/widget/desktop_aura/desktop_window_tree_host_x11.cc @@ -32,12 +32,14 @@ #include "ui/base/ime/input_method.h" #include "ui/base/x/x11_util.h" #include "ui/base/x/x11_util_internal.h" +#include "ui/base/x/x11_window_event_manager.h" #include "ui/display/display.h" #include "ui/display/screen.h" #include "ui/events/devices/x11/device_data_manager_x11.h" #include "ui/events/devices/x11/device_list_cache_x11.h" #include "ui/events/devices/x11/touch_factory_x11.h" #include "ui/events/event_utils.h" +#include "ui/events/null_event_targeter.h" #include "ui/events/platform/platform_event_source.h" #include "ui/events/platform/x11/x11_event_source.h" #include "ui/gfx/geometry/insets.h" @@ -91,6 +93,7 @@ const char* kAtomsToCache[] = { "UTF8_STRING", "WM_DELETE_WINDOW", "WM_PROTOCOLS", + "_NET_ACTIVE_WINDOW", "_NET_FRAME_EXTENTS", "_NET_WM_CM_S0", "_NET_WM_DESKTOP", @@ -155,6 +158,24 @@ std::vector<::Window> GetParentsList(XDisplay* xdisplay, ::Window window) { return result; } +int XI2ModeToXMode(int xi2_mode) { + switch (xi2_mode) { + case XINotifyNormal: + return NotifyNormal; + case XINotifyGrab: + case XINotifyPassiveGrab: + return NotifyGrab; + case XINotifyUngrab: + case XINotifyPassiveUngrab: + return NotifyUngrab; + case XINotifyWhileGrabbed: + return NotifyWhileGrabbed; + default: + NOTREACHED(); + return NotifyNormal; + } +} + } // namespace //////////////////////////////////////////////////////////////////////////////// @@ -181,9 +202,14 @@ DesktopWindowTreeHostX11::DesktopWindowTreeHostX11( window_parent_(NULL), custom_window_shape_(false), urgency_hint_set_(false), + has_pointer_grab_(false), activatable_(true), - close_widget_factory_(this) { -} + has_pointer_(false), + has_window_focus_(false), + has_pointer_focus_(false), + modal_dialog_xid_(0), + close_widget_factory_(this), + weak_factory_(this) {} DesktopWindowTreeHostX11::~DesktopWindowTreeHostX11() { window()->ClearProperty(kHostForRootWindow); @@ -230,29 +256,156 @@ gfx::Rect DesktopWindowTreeHostX11::GetX11RootWindowOuterBounds() const { return window_shape_.get(); } -void DesktopWindowTreeHostX11::HandleNativeWidgetActivationChanged( - bool active) { - if (active) { +void DesktopWindowTreeHostX11::BeforeActivationStateChanged() { + was_active_ = IsActive(); + had_pointer_ = has_pointer_; + had_pointer_grab_ = has_pointer_grab_; + had_window_focus_ = has_window_focus_; +} + +void DesktopWindowTreeHostX11::AfterActivationStateChanged() { + if (had_pointer_grab_ && !has_pointer_grab_) + dispatcher()->OnHostLostMouseGrab(); + + bool had_pointer_capture = had_pointer_ || had_pointer_grab_; + bool has_pointer_capture = has_pointer_ || has_pointer_grab_; + if (had_pointer_capture && !has_pointer_capture) + OnHostLostWindowCapture(); + + if (!was_active_ && IsActive()) { FlashFrame(false); OnHostActivated(); + // TODO(thomasanderson): Remove this window shuffling and use XWindowCache + // instead. open_windows().remove(xwindow_); open_windows().insert(open_windows().begin(), xwindow_); - } else { - ReleaseCapture(); } - desktop_native_widget_aura_->HandleActivationChanged(active); + if (was_active_ != IsActive()) { + desktop_native_widget_aura_->HandleActivationChanged(IsActive()); + native_widget_delegate_->AsWidget()->GetRootView()->SchedulePaint(); + } +} + +void DesktopWindowTreeHostX11::OnCrossingEvent(bool enter, + bool focus_in_window_or_ancestor, + int mode, + int detail) { + // NotifyInferior on a crossing event means the pointer moved into or out of a + // child window, but the pointer is still within |xwindow_|. + if (detail == NotifyInferior) + return; + + BeforeActivationStateChanged(); + + if (mode == NotifyGrab) + has_pointer_grab_ = enter; + else if (mode == NotifyUngrab) + has_pointer_grab_ = false; + + has_pointer_ = enter; + if (focus_in_window_or_ancestor && !has_window_focus_) { + // If we reach this point, we know the focus is in an ancestor or the + // pointer root. The definition of |has_pointer_focus_| is (An ancestor + // window or the PointerRoot is focused) && |has_pointer_|. Therefore, we + // can just use |has_pointer_| in the assignment. The transitions for when + // the focus changes are handled in OnFocusEvent(). + has_pointer_focus_ = has_pointer_; + } + + AfterActivationStateChanged(); +} + +void DesktopWindowTreeHostX11::OnFocusEvent(bool focus_in, + int mode, + int detail) { + // NotifyInferior on a focus event means the focus moved into or out of a + // child window, but the focus is still within |xwindow_|. + if (detail == NotifyInferior) + return; + + bool notify_grab = mode == NotifyGrab || mode == NotifyUngrab; + + BeforeActivationStateChanged(); + + // For every focus change, the X server sends normal focus events which are + // useful for tracking |has_window_focus_|, but supplements these events with + // NotifyPointer events which are only useful for tracking pointer focus. + + // For |has_pointer_focus_| and |has_window_focus_|, we continue tracking + // state during a grab, but ignore grab/ungrab events themselves. + if (!notify_grab && detail != NotifyPointer) + has_window_focus_ = focus_in; + + if (!notify_grab && has_pointer_) { + switch (detail) { + case NotifyAncestor: + case NotifyVirtual: + // If we reach this point, we know |has_pointer_| was true before and + // after this event. Since the definition of |has_pointer_focus_| is + // (An ancestor window or the PointerRoot is focused) && |has_pointer_|, + // we only need to worry about transitions on the first conjunct. + // Therefore, |has_pointer_focus_| will become true when: + // 1. Focus moves from |xwindow_| to an ancestor + // (FocusOut with NotifyAncestor) + // 2. Focus moves from a decendant of |xwindow_| to an ancestor + // (FocusOut with NotifyVirtual) + // |has_pointer_focus_| will become false when: + // 1. Focus moves from an ancestor to |xwindow_| + // (FocusIn with NotifyAncestor) + // 2. Focus moves from an ancestor to a child of |xwindow_| + // (FocusIn with NotifyVirtual) + has_pointer_focus_ = !focus_in; + break; + case NotifyPointer: + // The remaining cases for |has_pointer_focus_| becoming true are: + // 3. Focus moves from |xwindow_| to the PointerRoot + // 4. Focus moves from a decendant of |xwindow_| to the PointerRoot + // 5. Focus moves from None to the PointerRoot + // 6. Focus moves from Other to the PointerRoot + // 7. Focus moves from None to an ancestor of |xwindow_| + // 8. Focus moves from Other to an ancestor fo |xwindow_| + // In each case, we will get a FocusIn with a detail of NotifyPointer. + // The remaining cases for |has_pointer_focus_| becoming false are: + // 3. Focus moves from the PointerRoot to |xwindow_| + // 4. Focus moves from the PointerRoot to a decendant of |xwindow| + // 5. Focus moves from the PointerRoot to None + // 6. Focus moves from an ancestor of |xwindow_| to None + // 7. Focus moves from the PointerRoot to Other + // 8. Focus moves from an ancestor of |xwindow_| to Other + // In each case, we will get a FocusOut with a detail of NotifyPointer. + has_pointer_focus_ = focus_in; + break; + case NotifyNonlinear: + case NotifyNonlinearVirtual: + // We get Nonlinear(Virtual) events when + // 1. Focus moves from Other to |xwindow_| + // (FocusIn with NotifyNonlinear) + // 2. Focus moves from Other to a decendant of |xwindow_| + // (FocusIn with NotifyNonlinearVirtual) + // 3. Focus moves from |xwindow_| to Other + // (FocusOut with NotifyNonlinear) + // 4. Focus moves from a decendant of |xwindow_| to Other + // (FocusOut with NotifyNonlinearVirtual) + // |has_pointer_focus_| should be false before and after this event. + has_pointer_focus_ = false; + default: + break; + } + } + + ignore_keyboard_input_ = false; - native_widget_delegate_->AsWidget()->GetRootView()->SchedulePaint(); + AfterActivationStateChanged(); } void DesktopWindowTreeHostX11::AddObserver( - views::DesktopWindowTreeHostObserverX11* observer) { + DesktopWindowTreeHostObserverX11* observer) { observer_list_.AddObserver(observer); } void DesktopWindowTreeHostX11::RemoveObserver( - views::DesktopWindowTreeHostObserverX11* observer) { + DesktopWindowTreeHostObserverX11* observer) { observer_list_.RemoveObserver(observer); } @@ -308,8 +461,8 @@ void DesktopWindowTreeHostX11::OnNativeWidgetCreated( window()->SetProperty(kViewsWindowForRootWindow, content_window_); window()->SetProperty(kHostForRootWindow, this); - // Ensure that the X11DesktopHandler exists so that it dispatches activation - // messages to us. + // Ensure that the X11DesktopHandler exists so that it tracks create/destroy + // notify events. X11DesktopHandler::get(); // TODO(erg): Unify this code once the other consumer goes away. @@ -406,7 +559,7 @@ void DesktopWindowTreeHostX11::ShowWindowWithState( ui::WindowShowState show_state) { if (compositor()) compositor()->SetVisible(true); - if (!window_mapped_) + if (!IsVisible()) MapWindow(show_state); switch (show_state) { @@ -423,14 +576,6 @@ void DesktopWindowTreeHostX11::ShowWindowWithState( break; } - // Makes the window activated by default if the state is not INACTIVE or - // MINIMIZED. - if (show_state != ui::SHOW_STATE_INACTIVE && - show_state != ui::SHOW_STATE_MINIMIZED && - activatable_) { - Activate(); - } - native_widget_delegate_->AsWidget()->SetInitialFocus(show_state); } @@ -443,7 +588,7 @@ void DesktopWindowTreeHostX11::ShowMaximizedWithBounds( } bool DesktopWindowTreeHostX11::IsVisible() const { - return window_mapped_; + return window_mapped_ && !wait_for_unmap_; } void DesktopWindowTreeHostX11::SetSize(const gfx::Size& requested_size) { @@ -568,17 +713,28 @@ gfx::Rect DesktopWindowTreeHostX11::GetRestoredBounds() const { } std::string DesktopWindowTreeHostX11::GetWorkspace() const { - int workspace_id; - if (ui::GetIntProperty(xwindow_, "_NET_WM_DESKTOP", &workspace_id)) - return base::IntToString(workspace_id); - return std::string(); + if (workspace_.empty()) + const_cast<DesktopWindowTreeHostX11*>(this)->UpdateWorkspace(); + return workspace_; +} + +bool DesktopWindowTreeHostX11::UpdateWorkspace() { + int workspace_int; + if (!ui::GetWindowDesktop(xwindow_, &workspace_int)) + return false; + std::string workspace_str = base::IntToString(workspace_int); + if (workspace_ == workspace_str) + return false; + workspace_ = workspace_str; + return true; } gfx::Rect DesktopWindowTreeHostX11::GetWorkAreaBoundsInScreen() const { return ToDIPRect(GetWorkAreaBoundsInPixels()); } -void DesktopWindowTreeHostX11::SetShape(SkRegion* native_region) { +void DesktopWindowTreeHostX11::SetShape( + std::unique_ptr<SkRegion> native_region) { custom_window_shape_ = false; window_shape_.reset(); @@ -598,28 +754,86 @@ void DesktopWindowTreeHostX11::SetShape(SkRegion* native_region) { } custom_window_shape_ = true; - delete native_region; } ResetWindowRegion(); } void DesktopWindowTreeHostX11::Activate() { - if (!window_mapped_) + if (!IsVisible() || !activatable_) return; - X11DesktopHandler::get()->ActivateWindow(xwindow_); + BeforeActivationStateChanged(); + + ignore_keyboard_input_ = false; + + // wmii says that it supports _NET_ACTIVE_WINDOW but does not. + // https://code.google.com/p/wmii/issues/detail?id=266 + static bool wm_supports_active_window = + ui::GuessWindowManager() != ui::WM_WMII && + ui::WmSupportsHint(atom_cache_.GetAtom("_NET_ACTIVE_WINDOW")); + + Time timestamp = ui::X11EventSource::GetInstance()->GetTimestamp(); + + if (wm_supports_active_window) { + XEvent xclient; + memset(&xclient, 0, sizeof(xclient)); + xclient.type = ClientMessage; + xclient.xclient.window = xwindow_; + xclient.xclient.message_type = atom_cache_.GetAtom("_NET_ACTIVE_WINDOW"); + xclient.xclient.format = 32; + xclient.xclient.data.l[0] = 1; // Specified we are an app. + xclient.xclient.data.l[1] = timestamp; + // TODO(thomasanderson): if another chrome window is active, specify that in + // data.l[2]. The EWMH spec claims this may make the WM more likely to + // service our _NET_ACTIVE_WINDOW request. + xclient.xclient.data.l[2] = None; + xclient.xclient.data.l[3] = 0; + xclient.xclient.data.l[4] = 0; + + XSendEvent(xdisplay_, x_root_window_, False, + SubstructureRedirectMask | SubstructureNotifyMask, &xclient); + } else { + XRaiseWindow(xdisplay_, xwindow_); + // Directly ask the X server to give focus to the window. Note that the call + // will raise an X error if the window is not mapped. + XSetInputFocus(xdisplay_, xwindow_, RevertToParent, timestamp); + // At this point, we know we will receive focus, and some tests depend on a + // window being IsActive() immediately after an Activate(), so just set this + // state now. + has_pointer_focus_ = false; + has_window_focus_ = true; + } + AfterActivationStateChanged(); } void DesktopWindowTreeHostX11::Deactivate() { - if (!IsActive()) - return; + BeforeActivationStateChanged(); + + // Ignore future input events. + ignore_keyboard_input_ = true; ReleaseCapture(); - X11DesktopHandler::get()->DeactivateWindow(xwindow_); + XLowerWindow(xdisplay_, xwindow_); + + AfterActivationStateChanged(); } bool DesktopWindowTreeHostX11::IsActive() const { - return X11DesktopHandler::get()->IsActiveWindow(xwindow_); + // Focus and stacking order are independent in X11. Since we cannot guarantee + // a window is topmost iff it has focus, just use the focus state to determine + // if a window is active. Note that Activate() and Deactivate() change the + // stacking order in addition to changing the focus state. + bool is_active = + (has_window_focus_ || has_pointer_focus_) && !ignore_keyboard_input_; + + // is_active => window_mapped_ + // !window_mapped_ => !is_active + DCHECK(!is_active || window_mapped_); + + // |has_window_focus_| and |has_pointer_focus_| are mutually exclusive. + DCHECK(!has_window_focus_ || !has_pointer_focus_); + + return is_active; } void DesktopWindowTreeHostX11::Maximize() { @@ -640,7 +854,7 @@ void DesktopWindowTreeHostX11::Maximize() { // Some WMs do not respect maximization hints on unmapped windows, so we // save this one for later too. - should_maximize_after_map_ = !window_mapped_; + should_maximize_after_map_ = !IsVisible(); // When we are in the process of requesting to maximize a window, we can // accurately keep track of our restored bounds instead of relying on the @@ -705,6 +919,7 @@ void DesktopWindowTreeHostX11::SetVisibleOnAllWorkspaces(bool always_visible) { return; } + workspace_ = base::IntToString(kAllDesktops); XEvent xevent; memset (&xevent, 0, sizeof (xevent)); xevent.type = ClientMessage; @@ -721,6 +936,14 @@ void DesktopWindowTreeHostX11::SetVisibleOnAllWorkspaces(bool always_visible) { &xevent); } +bool DesktopWindowTreeHostX11::IsVisibleOnAllWorkspaces() const { + // We don't need a check for _NET_WM_STATE_STICKY because that would specify + // that the window remain in a fixed position even if the viewport scrolls. + // This is different from the type of workspace that's associated with + // _NET_WM_DESKTOP. + return GetWorkspace() == base::IntToString(kAllDesktops); +} + bool DesktopWindowTreeHostX11::SetWindowTitle(const base::string16& title) { if (window_title_ == title) return false; @@ -735,7 +958,7 @@ bool DesktopWindowTreeHostX11::SetWindowTitle(const base::string16& title) { reinterpret_cast<const unsigned char*>(utf8str.c_str()), utf8str.size()); XTextProperty xtp; - char *c_utf8_str = const_cast<char *>(utf8str.c_str()); + char* c_utf8_str = const_cast<char*>(utf8str.c_str()); if (Xutf8TextListToTextProperty(xdisplay_, &c_utf8_str, 1, XUTF8StringStyle, &xtp) == Success) { XSetWMName(xdisplay_, xwindow_, &xtp); @@ -952,7 +1175,7 @@ void DesktopWindowTreeHostX11::SizeConstraintsChanged() { gfx::Transform DesktopWindowTreeHostX11::GetRootTransform() const { display::Display display = display::Screen::GetScreen()->GetPrimaryDisplay(); - if (window_mapped_) { + if (IsVisible()) { aura::Window* win = const_cast<aura::Window*>(window()); display = display::Screen::GetScreen()->GetDisplayNearestWindow(win); } @@ -977,9 +1200,8 @@ void DesktopWindowTreeHostX11::ShowImpl() { } void DesktopWindowTreeHostX11::HideImpl() { - if (window_mapped_) { + if (IsVisible()) { XWithdrawWindow(xdisplay_, xwindow_, 0); - window_mapped_ = false; wait_for_unmap_ = true; } native_widget_delegate_->OnNativeWidgetVisibilityChanged(false); @@ -998,6 +1220,8 @@ void DesktopWindowTreeHostX11::SetBounds( XWindowChanges changes = {0}; unsigned value_mask = 0; + delayed_resize_task_.Cancel(); + if (size_changed) { // Update the minimum and maximum sizes in case they have changed. UpdateMinAndMaxSize(); @@ -1065,7 +1289,9 @@ void DesktopWindowTreeHostX11::SetCapture() { if (old_capturer) old_capturer->OnHostLostWindowCapture(); - GrabPointer(xwindow_, true, None); + // If the pointer is already in |xwindow_|, we will not get a crossing event + // with a mode of NotifyGrab, so we must record the grab state manually. + has_pointer_grab_ |= !GrabPointer(xwindow_, true, None); } void DesktopWindowTreeHostX11::ReleaseCapture() { @@ -1075,6 +1301,7 @@ void DesktopWindowTreeHostX11::ReleaseCapture() { // asynchronous is likely inconsequential. g_current_capture = NULL; UngrabPointer(); + has_pointer_grab_ = false; OnHostLostWindowCapture(); } @@ -1135,24 +1362,33 @@ void DesktopWindowTreeHostX11::InitX11Window( if (swa.override_redirect) attribute_mask |= CWOverrideRedirect; - Visual* visual; - int depth; - ui::ChooseVisualForWindow(&visual, &depth); - if (depth == 32) { - attribute_mask |= CWColormap; - swa.colormap = - XCreateColormap(xdisplay_, x_root_window_, visual, AllocNone); + bool enable_transparent_visuals; + switch (params.opacity) { + case Widget::InitParams::OPAQUE_WINDOW: + enable_transparent_visuals = false; + break; + case Widget::InitParams::TRANSLUCENT_WINDOW: + enable_transparent_visuals = true; + break; + case Widget::InitParams::INFER_OPACITY: + default: + enable_transparent_visuals = params.type == Widget::InitParams::TYPE_DRAG; + } - // x.org will BadMatch if we don't set a border when the depth isn't the - // same as the parent depth. - attribute_mask |= CWBorderPixel; - swa.border_pixel = 0; + Visual* visual = CopyFromParent; + int depth = CopyFromParent; + Colormap colormap = CopyFromParent; + ui::XVisualManager::GetInstance()->ChooseVisualForWindow( + enable_transparent_visuals, &visual, &depth, &colormap, + &use_argb_visual_); - // A compositing manager is required to support transparency. - use_argb_visual_ = - XGetSelectionOwner(xdisplay_, atom_cache_.GetAtom("_NET_WM_CM_S0")) != - None; - } + attribute_mask |= CWColormap; + swa.colormap = colormap; + + // x.org will BadMatch if we don't set a border when the depth isn't the + // same as the parent depth. + attribute_mask |= CWBorderPixel; + swa.border_pixel = 0; bounds_in_pixels_ = ToPixelRect(params.bounds); bounds_in_pixels_.set_size(AdjustSize(bounds_in_pixels_.size())); @@ -1163,7 +1399,7 @@ void DesktopWindowTreeHostX11::InitX11Window( depth, InputOutput, visual, attribute_mask, &swa); if (ui::PlatformEventSource::GetInstance()) ui::PlatformEventSource::GetInstance()->AddPlatformEventDispatcher(this); - open_windows().push_back(xwindow_); + open_windows().push_front(xwindow_); // TODO(erg): Maybe need to set a ViewProp here like in RWHL::RWHL(). @@ -1173,7 +1409,7 @@ void DesktopWindowTreeHostX11::InitX11Window( ExposureMask | VisibilityChangeMask | StructureNotifyMask | PropertyChangeMask | PointerMotionMask; - XSelectInput(xdisplay_, xwindow_, event_mask); + xwindow_events_.reset(new ui::XScopedEventSelector(xwindow_, event_mask)); XFlush(xdisplay_); if (ui::IsXInput2Available()) @@ -1230,6 +1466,7 @@ void DesktopWindowTreeHostX11::InitX11Window( if (is_always_on_top_) state_atom_list.push_back(atom_cache_.GetAtom("_NET_WM_STATE_ABOVE")); + workspace_.clear(); if (params.visible_on_all_workspaces) { state_atom_list.push_back(atom_cache_.GetAtom("_NET_WM_STATE_STICKY")); ui::SetIntProperty(xwindow_, "_NET_WM_DESKTOP", "CARDINAL", kAllDesktops); @@ -1402,7 +1639,7 @@ void DesktopWindowTreeHostX11::OnFrameExtentsUpdated() { } void DesktopWindowTreeHostX11::UpdateMinAndMaxSize() { - if (!window_mapped_) + if (!IsVisible()) return; gfx::Size minimum_in_pixels = @@ -1458,7 +1695,6 @@ void DesktopWindowTreeHostX11::UpdateWMUserTime( PropModeReplace, reinterpret_cast<const unsigned char *>(&wm_user_time_ms), 1); - X11DesktopHandler::get()->set_wm_user_time_ms(wm_user_time_ms); } } @@ -1546,7 +1782,8 @@ void DesktopWindowTreeHostX11::DispatchTouchEvent(ui::TouchEvent* event) { } void DesktopWindowTreeHostX11::DispatchKeyEvent(ui::KeyEvent* event) { - GetInputMethod()->DispatchKeyEvent(event); + if (native_widget_delegate_->AsWidget()->IsActive()) + GetInputMethod()->DispatchKeyEvent(event); } void DesktopWindowTreeHostX11::ConvertEventToDifferentHost( @@ -1578,7 +1815,7 @@ void DesktopWindowTreeHostX11::ResetWindowRegion() { if (!IsMaximized() && !IsFullscreen()) { gfx::Path window_mask; - views::Widget* widget = native_widget_delegate_->AsWidget(); + Widget* widget = native_widget_delegate_->AsWidget(); if (widget->non_client_view()) { // Some frame views define a custom (non-rectangular) window mask. If // so, use it to define the window shape. If not, fall through. @@ -1659,17 +1896,16 @@ void DesktopWindowTreeHostX11::MapWindow(ui::WindowShowState show_state) { // If SHOW_STATE_INACTIVE, tell the window manager not to focus the window // when mapping. This is done by setting the _NET_WM_USER_TIME to 0. See e.g. // http://standards.freedesktop.org/wm-spec/latest/ar01s05.html - unsigned long wm_user_time_ms = (show_state == ui::SHOW_STATE_INACTIVE) ? - 0 : X11DesktopHandler::get()->wm_user_time_ms(); + ignore_keyboard_input_ = show_state == ui::SHOW_STATE_INACTIVE; + unsigned long wm_user_time_ms = + ignore_keyboard_input_ + ? 0 + : ui::X11EventSource::GetInstance()->GetTimestamp(); if (show_state == ui::SHOW_STATE_INACTIVE || wm_user_time_ms != 0) { - XChangeProperty(xdisplay_, - xwindow_, - atom_cache_.GetAtom("_NET_WM_USER_TIME"), - XA_CARDINAL, - 32, - PropModeReplace, - reinterpret_cast<const unsigned char *>(&wm_user_time_ms), - 1); + XChangeProperty( + xdisplay_, xwindow_, atom_cache_.GetAtom("_NET_WM_USER_TIME"), + XA_CARDINAL, 32, PropModeReplace, + reinterpret_cast<const unsigned char*>(&wm_user_time_ms), 1); } ui::X11EventSource* event_source = ui::X11EventSource::GetInstance(); @@ -1731,15 +1967,15 @@ uint32_t DesktopWindowTreeHostX11::DispatchEvent( switch (xev->type) { case EnterNotify: case LeaveNotify: { + OnCrossingEvent(xev->type == EnterNotify, xev->xcrossing.focus, + xev->xcrossing.mode, xev->xcrossing.detail); + // Ignore EventNotify and LeaveNotify events from children of |xwindow_|. // NativeViewGLSurfaceGLX adds a child to |xwindow_|. - // TODO(pkotwicz|tdanderson): Figure out whether the suppression is - // necessary. crbug.com/385716 - if (xev->xcrossing.detail == NotifyInferior) - break; - - ui::MouseEvent mouse_event(xev); - DispatchMouseEvent(&mouse_event); + if (xev->xcrossing.detail != NotifyInferior) { + ui::MouseEvent mouse_event(xev); + DispatchMouseEvent(&mouse_event); + } break; } case Expose: { @@ -1755,8 +1991,7 @@ uint32_t DesktopWindowTreeHostX11::DispatchEvent( } case KeyRelease: { // There is no way to deactivate a window in X11 so ignore input if - // window is supposed to be 'inactive'. See comments in - // X11DesktopHandler::DeactivateWindow() for more details. + // window is supposed to be 'inactive'. if (!IsActive() && !HasCapture()) break; @@ -1787,17 +2022,10 @@ uint32_t DesktopWindowTreeHostX11::DispatchEvent( } break; } - case FocusOut: - if (xev->xfocus.mode != NotifyGrab) { - ReleaseCapture(); - OnHostLostWindowCapture(); - X11DesktopHandler::get()->ProcessXEvent(xev); - } else { - dispatcher()->OnHostLostMouseGrab(); - } - break; case FocusIn: - X11DesktopHandler::get()->ProcessXEvent(xev); + case FocusOut: + OnFocusEvent(xev->type == FocusIn, event->xfocus.mode, + event->xfocus.detail); break; case ConfigureNotify: { DCHECK_EQ(xwindow_, xev->xconfigure.window); @@ -1838,6 +2066,23 @@ uint32_t DesktopWindowTreeHostX11::DispatchEvent( if (!factory->ShouldProcessXI2Event(xev)) break; + XIEnterEvent* enter_event = static_cast<XIEnterEvent*>(xev->xcookie.data); + switch (static_cast<XIEvent*>(xev->xcookie.data)->evtype) { + case XI_Enter: + case XI_Leave: + OnCrossingEvent(enter_event->evtype == XI_Enter, enter_event->focus, + XI2ModeToXMode(enter_event->mode), + enter_event->detail); + return ui::POST_DISPATCH_STOP_PROPAGATION; + case XI_FocusIn: + case XI_FocusOut: + OnFocusEvent(enter_event->evtype == XI_FocusIn, + XI2ModeToXMode(enter_event->mode), enter_event->detail); + return ui::POST_DISPATCH_STOP_PROPAGATION; + default: + break; + } + ui::EventType type = ui::EventTypeFromNative(xev); XEvent last_event; int num_coalesced = 0; @@ -1919,7 +2164,12 @@ uint32_t DesktopWindowTreeHostX11::DispatchEvent( break; } case UnmapNotify: { + window_mapped_ = false; wait_for_unmap_ = false; + has_pointer_ = false; + has_pointer_grab_ = false; + has_pointer_focus_ = false; + has_window_focus_ = false; FOR_EACH_OBSERVER(DesktopWindowTreeHostObserverX11, observer_list_, OnWindowUnmapped(xwindow_)); @@ -1996,12 +2246,14 @@ uint32_t DesktopWindowTreeHostX11::DispatchEvent( } case PropertyNotify: { ::Atom changed_atom = xev->xproperty.atom; - if (changed_atom == atom_cache_.GetAtom("_NET_WM_STATE")) + if (changed_atom == atom_cache_.GetAtom("_NET_WM_STATE")) { OnWMStateUpdated(); - else if (changed_atom == atom_cache_.GetAtom("_NET_FRAME_EXTENTS")) + } else if (changed_atom == atom_cache_.GetAtom("_NET_FRAME_EXTENTS")) { OnFrameExtentsUpdated(); - else if (changed_atom == atom_cache_.GetAtom("_NET_WM_DESKTOP")) - OnHostWorkspaceChanged(); + } else if (changed_atom == atom_cache_.GetAtom("_NET_WM_DESKTOP")) { + if (UpdateWorkspace()) + OnHostWorkspaceChanged(); + } break; } case SelectionNotify: { @@ -2053,6 +2305,31 @@ gfx::Rect DesktopWindowTreeHostX11::ToPixelRect( return gfx::ToEnclosingRect(rect_in_pixels); } +XID DesktopWindowTreeHostX11::GetModalDialog() { + return modal_dialog_xid_; +} + +std::unique_ptr<base::Closure> + DesktopWindowTreeHostX11::DisableEventListening(XID dialog) { + DCHECK(dialog); + DCHECK(!modal_dialog_xid_); + modal_dialog_xid_ = dialog; + // ScopedWindowTargeter is used to temporarily replace the event-targeter + // with NullEventTargeter to make |dialog| modal. + targeter_for_modal_.reset(new aura::ScopedWindowTargeter(window(), + std::unique_ptr<ui::EventTargeter>(new ui::NullEventTargeter))); + + return base::MakeUnique<base::Closure>(base::Bind( + &DesktopWindowTreeHostX11::EnableEventListening, + weak_factory_.GetWeakPtr())); +} + +void DesktopWindowTreeHostX11::EnableEventListening() { + DCHECK(modal_dialog_xid_); + modal_dialog_xid_ = 0; + targeter_for_modal_.reset(); +} + //////////////////////////////////////////////////////////////////////////////// // DesktopWindowTreeHost, public: @@ -2066,7 +2343,7 @@ DesktopWindowTreeHost* DesktopWindowTreeHost::Create( // static ui::NativeTheme* DesktopWindowTreeHost::GetNativeTheme(aura::Window* window) { - const views::LinuxUI* linux_ui = views::LinuxUI::instance(); + const LinuxUI* linux_ui = LinuxUI::instance(); if (linux_ui) { ui::NativeTheme* native_theme = linux_ui->GetNativeTheme(window); if (native_theme) diff --git a/chromium/ui/views/widget/desktop_aura/desktop_window_tree_host_x11.h b/chromium/ui/views/widget/desktop_aura/desktop_window_tree_host_x11.h index fa262f13528..a4b68b38221 100644 --- a/chromium/ui/views/widget/desktop_aura/desktop_window_tree_host_x11.h +++ b/chromium/ui/views/widget/desktop_aura/desktop_window_tree_host_x11.h @@ -15,6 +15,7 @@ #include "base/macros.h" #include "base/memory/weak_ptr.h" #include "base/observer_list.h" +#include "ui/aura/scoped_window_targeter.h" #include "ui/aura/window_tree_host.h" #include "ui/base/cursor/cursor_loader_x11.h" #include "ui/events/platform/platform_event_dispatcher.h" @@ -32,6 +33,7 @@ class ImageSkiaRep; namespace ui { class EventHandler; +class XScopedEventSelector; } namespace views { @@ -72,12 +74,8 @@ class VIEWS_EXPORT DesktopWindowTreeHostX11 // otherwise. ::Region GetWindowShape() const; - // Called by X11DesktopHandler to notify us that the native windowing system - // has changed our activation. - void HandleNativeWidgetActivationChanged(bool active); - - void AddObserver(views::DesktopWindowTreeHostObserverX11* observer); - void RemoveObserver(views::DesktopWindowTreeHostObserverX11* observer); + void AddObserver(DesktopWindowTreeHostObserverX11* observer); + void RemoveObserver(DesktopWindowTreeHostObserverX11* observer); // Swaps the current handler for events in the non client view with |handler|. void SwapNonClientEventHandler(std::unique_ptr<ui::EventHandler> handler); @@ -86,6 +84,13 @@ class VIEWS_EXPORT DesktopWindowTreeHostX11 // internal list of open windows. static void CleanUpWindowList(void (*func)(aura::Window* window)); + // Disables event listening to make |dialog| modal. + std::unique_ptr<base::Closure> DisableEventListening(XID dialog); + + // Returns XID of dialog currently displayed. When it returns 0, + // there is no dialog on the host window. + XID GetModalDialog(); + protected: // Overridden from DesktopWindowTreeHost: void Init(aura::Window* content_window, @@ -111,7 +116,7 @@ class VIEWS_EXPORT DesktopWindowTreeHostX11 gfx::Rect GetRestoredBounds() const override; std::string GetWorkspace() const override; gfx::Rect GetWorkAreaBoundsInScreen() const override; - void SetShape(SkRegion* native_region) override; + void SetShape(std::unique_ptr<SkRegion> native_region) override; void Activate() override; void Deactivate() override; bool IsActive() const override; @@ -124,6 +129,7 @@ class VIEWS_EXPORT DesktopWindowTreeHostX11 void SetAlwaysOnTop(bool always_on_top) override; bool IsAlwaysOnTop() const override; void SetVisibleOnAllWorkspaces(bool always_visible) override; + bool IsVisibleOnAllWorkspaces() const override; bool SetWindowTitle(const base::string16& title) override; void ClearNativeFocus() override; Widget::MoveLoopResult RunMoveLoop( @@ -185,6 +191,27 @@ class VIEWS_EXPORT DesktopWindowTreeHostX11 // Called when |xwindow_|'s _NET_FRAME_EXTENTS property is updated. void OnFrameExtentsUpdated(); + // Record the activation state. + void BeforeActivationStateChanged(); + + // Handle the state change since BeforeActivationStateChanged(). + void AfterActivationStateChanged(); + + // Called on an XEnterWindowEvent, XLeaveWindowEvent, XIEnterEvent, or an + // XILeaveEvent. + void OnCrossingEvent(bool enter, + bool focus_in_window_or_ancestor, + int mode, + int detail); + + // Called on an XFocusInEvent, XFocusOutEvent, XIFocusInEvent, or an + // XIFocusOutEvent. + void OnFocusEvent(bool focus_in, int mode, int detail); + + // Makes a round trip to the X server to get the enclosing workspace for this + // window. Returns true iff |workspace_| was changed. + bool UpdateWorkspace(); + // Updates |xwindow_|'s minimum and maximum size. void UpdateMinAndMaxSize(); @@ -247,11 +274,17 @@ class VIEWS_EXPORT DesktopWindowTreeHostX11 gfx::Rect ToDIPRect(const gfx::Rect& rect_in_pixels) const; gfx::Rect ToPixelRect(const gfx::Rect& rect_in_dip) const; + // Enables event listening after closing |dialog|. + void EnableEventListening(); + // X11 things // The display and the native X window hosting the root window. XDisplay* xdisplay_; ::Window xwindow_; + // Events selected on |xwindow_|. + std::unique_ptr<ui::XScopedEventSelector> xwindow_events_; + // The native root window. ::Window x_root_window_; @@ -261,6 +294,8 @@ class VIEWS_EXPORT DesktopWindowTreeHostX11 bool window_mapped_; // Should we wait for an UnmapNotify before trying to remap the window? + // If |wait_for_unmap_| is true, we have sent an XUnmapWindow request to the + // server and have yet to receive an UnmapNotify. bool wait_for_unmap_; // The bounds of |xwindow_|. @@ -284,6 +319,9 @@ class VIEWS_EXPORT DesktopWindowTreeHostX11 // |xwindow_|'s maximum size. gfx::Size max_size_in_pixels_; + // The workspace containing |xwindow_|. + std::string workspace_; + // The window manager state bits. std::set< ::Atom> window_properties_; @@ -348,11 +386,49 @@ class VIEWS_EXPORT DesktopWindowTreeHostX11 // the frame when |xwindow_| gains focus or handles a mouse button event. bool urgency_hint_set_; + // Does |xwindow_| have the pointer grab (XI2 or normal)? + bool has_pointer_grab_; + bool activatable_; + // The focus-tracking state variables are as described in + // gtk/docs/focus_tracking.txt + // + // |xwindow_| is active iff: + // (|has_window_focus_| || |has_pointer_focus_|) && + // !|ignore_keyboard_input_| + + // Is the pointer in |xwindow_| or one of its children? + bool has_pointer_; + + // Is |xwindow_| or one of its children focused? + bool has_window_focus_; + + // (An ancestor window or the PointerRoot is focused) && |has_pointer_|. + // |has_pointer_focus_| == true is the odd case where we will receive keyboard + // input when |has_window_focus_| == false. |has_window_focus_| and + // |has_pointer_focus_| are mutually exclusive. + bool has_pointer_focus_; + + // X11 does not support defocusing windows; you can only focus a different + // window. If we would like to be defocused, we just ignore keyboard input we + // no longer care about. + bool ignore_keyboard_input_; + + // Used for tracking activation state in {Before|After}ActivationStateChanged. + bool was_active_; + bool had_pointer_; + bool had_pointer_grab_; + bool had_window_focus_; + base::CancelableCallback<void()> delayed_resize_task_; + std::unique_ptr<aura::ScopedWindowTargeter> targeter_for_modal_; + + XID modal_dialog_xid_; + base::WeakPtrFactory<DesktopWindowTreeHostX11> close_widget_factory_; + base::WeakPtrFactory<DesktopWindowTreeHostX11> weak_factory_; DISALLOW_COPY_AND_ASSIGN(DesktopWindowTreeHostX11); }; diff --git a/chromium/ui/views/widget/desktop_aura/desktop_window_tree_host_x11_interactive_uitest.cc b/chromium/ui/views/widget/desktop_aura/desktop_window_tree_host_x11_interactive_uitest.cc index af342ffd29e..117b3868a85 100644 --- a/chromium/ui/views/widget/desktop_aura/desktop_window_tree_host_x11_interactive_uitest.cc +++ b/chromium/ui/views/widget/desktop_aura/desktop_window_tree_host_x11_interactive_uitest.cc @@ -270,6 +270,9 @@ TEST_F(DesktopWindowTreeHostX11Test, InputMethodFocus) { // widget->GetInputMethod()->GetTextInputType()); widget->Activate(); + ActivationWaiter waiter( + widget->GetNativeWindow()->GetHost()->GetAcceleratedWidget()); + waiter.Wait(); EXPECT_TRUE(widget->IsActive()); EXPECT_EQ(ui::TEXT_INPUT_TYPE_TEXT, diff --git a/chromium/ui/views/widget/desktop_aura/desktop_window_tree_host_x11_unittest.cc b/chromium/ui/views/widget/desktop_aura/desktop_window_tree_host_x11_unittest.cc index 52a3827f2bc..5426fe48922 100644 --- a/chromium/ui/views/widget/desktop_aura/desktop_window_tree_host_x11_unittest.cc +++ b/chromium/ui/views/widget/desktop_aura/desktop_window_tree_host_x11_unittest.cc @@ -53,10 +53,9 @@ class WMStateWaiter : public X11PropertyChangeWaiter { : X11PropertyChangeWaiter(window, "_NET_WM_STATE"), hint_(hint), wait_till_set_(wait_till_set) { - - const char* kAtomsToCache[] = { + const char* const kAtomsToCache[] = { hint, - NULL + nullptr }; atom_cache_.reset(new ui::X11AtomCache(gfx::GetXDisplay(), kAtomsToCache)); } @@ -90,7 +89,7 @@ class WMStateWaiter : public X11PropertyChangeWaiter { // A NonClientFrameView with a window mask with the bottom right corner cut out. class ShapedNonClientFrameView : public NonClientFrameView { public: - explicit ShapedNonClientFrameView() { + ShapedNonClientFrameView() { } ~ShapedNonClientFrameView() override {} @@ -287,12 +286,12 @@ TEST_F(DesktopWindowTreeHostX11Test, Shape) { shape2.lineTo(100, 0); shape2.close(); - SkRegion* shape_region = new SkRegion; + auto shape_region = base::MakeUnique<SkRegion>(); shape_region->setPath(shape2, SkRegion(shape2.getBounds().round())); - std::unique_ptr<Widget> widget2(CreateWidget(NULL)); + std::unique_ptr<Widget> widget2(CreateWidget(nullptr)); widget2->Show(); - widget2->SetShape(shape_region); + widget2->SetShape(std::move(shape_region)); ui::X11EventSource::GetInstance()->DispatchXEvents(); XID xid2 = widget2->GetNativeWindow()->GetHost()->GetAcceleratedWidget(); @@ -346,10 +345,10 @@ TEST_F(DesktopWindowTreeHostX11Test, WindowManagerTogglesFullscreen) { // Emulate the window manager exiting fullscreen via a window manager // accelerator key. It should not affect the widget's fullscreen state. { - const char* kAtomsToCache[] = { + const char* const kAtomsToCache[] = { "_NET_WM_STATE", "_NET_WM_STATE_FULLSCREEN", - NULL + nullptr }; Display* display = gfx::GetXDisplay(); ui::X11AtomCache atom_cache(display, kAtomsToCache); @@ -397,10 +396,10 @@ TEST_F(DesktopWindowTreeHostX11Test, ToggleMinimizePropogateToContentWindow) { // Minimize by sending _NET_WM_STATE_HIDDEN { - const char* kAtomsToCache[] = { + const char* const kAtomsToCache[] = { "_NET_WM_STATE", "_NET_WM_STATE_HIDDEN", - NULL + nullptr }; ui::X11AtomCache atom_cache(display, kAtomsToCache); @@ -429,10 +428,10 @@ TEST_F(DesktopWindowTreeHostX11Test, ToggleMinimizePropogateToContentWindow) { // Show from minimized by sending _NET_WM_STATE_FOCUSED { - const char* kAtomsToCache[] = { + const char* const kAtomsToCache[] = { "_NET_WM_STATE", "_NET_WM_STATE_FOCUSED", - NULL + nullptr }; ui::X11AtomCache atom_cache(display, kAtomsToCache); diff --git a/chromium/ui/views/widget/desktop_aura/x11_desktop_handler.cc b/chromium/ui/views/widget/desktop_aura/x11_desktop_handler.cc index 8ec4b1e7b9a..6f4d6630284 100644 --- a/chromium/ui/views/widget/desktop_aura/x11_desktop_handler.cc +++ b/chromium/ui/views/widget/desktop_aura/x11_desktop_handler.cc @@ -8,20 +8,21 @@ #include <X11/Xlib.h> #include "base/message_loop/message_loop.h" +#include "base/strings/string_number_conversions.h" #include "ui/aura/env.h" #include "ui/aura/window_event_dispatcher.h" -#include "ui/base/x/x11_foreign_window_manager.h" #include "ui/base/x/x11_menu_list.h" #include "ui/base/x/x11_util.h" +#include "ui/base/x/x11_window_event_manager.h" #include "ui/events/platform/platform_event_source.h" #include "ui/gfx/x/x11_error_tracker.h" #include "ui/views/widget/desktop_aura/desktop_window_tree_host_x11.h" namespace { -const char* kAtomsToCache[] = { - "_NET_ACTIVE_WINDOW", - NULL +const char* const kAtomsToCache[] = { + "_NET_CURRENT_DESKTOP", + nullptr }; // Our global instance. Deleted when our Env() is deleted. @@ -42,30 +43,14 @@ X11DesktopHandler* X11DesktopHandler::get() { X11DesktopHandler::X11DesktopHandler() : xdisplay_(gfx::GetXDisplay()), x_root_window_(DefaultRootWindow(xdisplay_)), - x_active_window_(None), - wm_user_time_ms_(CurrentTime), - current_window_(None), - current_window_active_state_(NOT_ACTIVE), - atom_cache_(xdisplay_, kAtomsToCache), - wm_supports_active_window_(false) { + atom_cache_(xdisplay_, kAtomsToCache) { if (ui::PlatformEventSource::GetInstance()) ui::PlatformEventSource::GetInstance()->AddPlatformEventDispatcher(this); aura::Env::GetInstance()->AddObserver(this); - XWindowAttributes attr; - XGetWindowAttributes(xdisplay_, x_root_window_, &attr); - XSelectInput(xdisplay_, x_root_window_, - attr.your_event_mask | PropertyChangeMask | - StructureNotifyMask | SubstructureNotifyMask); - - if (ui::GuessWindowManager() == ui::WM_WMII) { - // wmii says that it supports _NET_ACTIVE_WINDOW but does not. - // https://code.google.com/p/wmii/issues/detail?id=266 - wm_supports_active_window_ = false; - } else { - wm_supports_active_window_ = - ui::WmSupportsHint(atom_cache_.GetAtom("_NET_ACTIVE_WINDOW")); - } + x_root_window_events_.reset(new ui::XScopedEventSelector( + x_root_window_, + PropertyChangeMask | StructureNotifyMask | SubstructureNotifyMask)); } X11DesktopHandler::~X11DesktopHandler() { @@ -74,105 +59,27 @@ X11DesktopHandler::~X11DesktopHandler() { ui::PlatformEventSource::GetInstance()->RemovePlatformEventDispatcher(this); } -void X11DesktopHandler::ActivateWindow(::Window window) { - if ((current_window_ == None || current_window_ == window) && - current_window_active_state_ == NOT_ACTIVE) { - // |window| is most likely still active wrt to the X server. Undo the - // changes made in DeactivateWindow(). - OnActiveWindowChanged(window, ACTIVE); - - // Go through the regular activation path such that calling - // DeactivateWindow() and ActivateWindow() immediately afterwards results - // in an active X window. - } - - if (wm_supports_active_window_) { - DCHECK_EQ(gfx::GetXDisplay(), xdisplay_); - - // If the window is not already active, send a hint to activate it - if (x_active_window_ != window) { - if (wm_user_time_ms_ == CurrentTime) { - set_wm_user_time_ms( - ui::X11EventSource::GetInstance()->UpdateLastSeenServerTime()); - } - XEvent xclient; - memset(&xclient, 0, sizeof(xclient)); - xclient.type = ClientMessage; - xclient.xclient.window = window; - xclient.xclient.message_type = atom_cache_.GetAtom("_NET_ACTIVE_WINDOW"); - xclient.xclient.format = 32; - xclient.xclient.data.l[0] = 1; // Specified we are an app. - xclient.xclient.data.l[1] = wm_user_time_ms_; - xclient.xclient.data.l[2] = None; - xclient.xclient.data.l[3] = 0; - xclient.xclient.data.l[4] = 0; - - XSendEvent(xdisplay_, x_root_window_, False, - SubstructureRedirectMask | SubstructureNotifyMask, - &xclient); - } else { - OnActiveWindowChanged(window, ACTIVE); - } - } else { - XRaiseWindow(xdisplay_, window); - // Directly ask the X server to give focus to the window. Note - // that the call will raise an X error if the window is not - // mapped. - XSetInputFocus(xdisplay_, window, RevertToParent, CurrentTime); - - OnActiveWindowChanged(window, ACTIVE); - } -} - -void X11DesktopHandler::set_wm_user_time_ms(Time time_ms) { - if (time_ms != CurrentTime) { - int64_t event_time_64 = time_ms; - int64_t time_difference = wm_user_time_ms_ - event_time_64; - // Ignore timestamps that go backwards. However, X server time is a 32-bit - // millisecond counter, so if the time goes backwards by more than half the - // range of the 32-bit counter, treat it as a rollover. - if (time_difference < 0 || time_difference > (UINT32_MAX >> 1)) - wm_user_time_ms_ = time_ms; - } +void X11DesktopHandler::AddObserver(X11DesktopHandlerObserver* observer) { + observers_.AddObserver(observer); } -void X11DesktopHandler::DeactivateWindow(::Window window) { - if (!IsActiveWindow(window)) - return; - - XLowerWindow(xdisplay_, window); - - // Per ICCCM: http://tronche.com/gui/x/icccm/sec-4.html#s-4.1.7 - // "Clients should not give up the input focus of their own volition. - // They should ignore input that they receive instead." - // - // There is nothing else that we can do. Pretend that we have been - // deactivated and ignore keyboard input in DesktopWindowTreeHostX11. - OnActiveWindowChanged(window, NOT_ACTIVE); +void X11DesktopHandler::RemoveObserver(X11DesktopHandlerObserver* observer) { + observers_.RemoveObserver(observer); } -bool X11DesktopHandler::IsActiveWindow(::Window window) const { - return window == current_window_ && current_window_active_state_ == ACTIVE; +std::string X11DesktopHandler::GetWorkspace() { + if (workspace_.empty()) + UpdateWorkspace(); + return workspace_; } -void X11DesktopHandler::ProcessXEvent(XEvent* event) { - // Ignore focus events that are being sent only because the pointer is over - // our window, even if the input focus is in a different window. - if (event->xfocus.detail == NotifyPointer) - return; - - switch (event->type) { - case FocusIn: - if (current_window_ != event->xfocus.window) - OnActiveWindowChanged(event->xfocus.window, ACTIVE); - break; - case FocusOut: - if (current_window_ == event->xfocus.window) - OnActiveWindowChanged(None, NOT_ACTIVE); - break; - default: - NOTREACHED(); +bool X11DesktopHandler::UpdateWorkspace() { + int desktop; + if (ui::GetCurrentDesktop(&desktop)) { + workspace_ = base::IntToString(desktop); + return true; } + return false; } bool X11DesktopHandler::CanDispatchEvent(const ui::PlatformEvent& event) { @@ -184,31 +91,20 @@ bool X11DesktopHandler::CanDispatchEvent(const ui::PlatformEvent& event) { uint32_t X11DesktopHandler::DispatchEvent(const ui::PlatformEvent& event) { switch (event->type) { case PropertyNotify: { - // Check for a change to the active window. - CHECK_EQ(x_root_window_, event->xproperty.window); - ::Atom active_window_atom = atom_cache_.GetAtom("_NET_ACTIVE_WINDOW"); - if (event->xproperty.atom == active_window_atom) { - ::Window window; - if (ui::GetXIDProperty(x_root_window_, "_NET_ACTIVE_WINDOW", &window) && - window) { - x_active_window_ = window; - OnActiveWindowChanged(window, ACTIVE); - } else { - x_active_window_ = None; + if (event->xproperty.atom == + atom_cache_.GetAtom("_NET_CURRENT_DESKTOP")) { + if (UpdateWorkspace()) { + FOR_EACH_OBSERVER(views::X11DesktopHandlerObserver, observers_, + OnWorkspaceChanged(workspace_)); } } break; } - case CreateNotify: OnWindowCreatedOrDestroyed(event->type, event->xcreatewindow.window); break; case DestroyNotify: OnWindowCreatedOrDestroyed(event->type, event->xdestroywindow.window); - // If the current active window is being destroyed, reset our tracker. - if (x_active_window_ == event->xdestroywindow.window) { - x_active_window_ = None; - } break; default: NOTREACHED(); @@ -225,30 +121,6 @@ void X11DesktopHandler::OnWillDestroyEnv() { delete this; } -void X11DesktopHandler::OnActiveWindowChanged(::Window xid, - ActiveState active_state) { - if (current_window_ == xid && current_window_active_state_ == active_state) - return; - - if (current_window_active_state_ == ACTIVE) { - DesktopWindowTreeHostX11* old_host = - views::DesktopWindowTreeHostX11::GetHostForXID(current_window_); - if (old_host) - old_host->HandleNativeWidgetActivationChanged(false); - } - - // Update the current window ID to effectively change the active widget. - current_window_ = xid; - current_window_active_state_ = active_state; - - if (active_state == ACTIVE) { - DesktopWindowTreeHostX11* new_host = - views::DesktopWindowTreeHostX11::GetHostForXID(xid); - if (new_host) - new_host->HandleNativeWidgetActivationChanged(true); - } -} - void X11DesktopHandler::OnWindowCreatedOrDestroyed(int event_type, XID window) { // Menus created by Chrome can be drag and drop targets. Since they are @@ -266,11 +138,6 @@ void X11DesktopHandler::OnWindowCreatedOrDestroyed(int event_type, } else { ui::XMenuList::GetInstance()->MaybeUnregisterMenu(window); } - - if (event_type == DestroyNotify) { - // Notify the XForeignWindowManager that |window| has been destroyed. - ui::XForeignWindowManager::GetInstance()->OnWindowDestroyed(window); - } } } // namespace views diff --git a/chromium/ui/views/widget/desktop_aura/x11_desktop_handler.h b/chromium/ui/views/widget/desktop_aura/x11_desktop_handler.h index 47a1ff1a7ff..06d78ba8941 100644 --- a/chromium/ui/views/widget/desktop_aura/x11_desktop_handler.h +++ b/chromium/ui/views/widget/desktop_aura/x11_desktop_handler.h @@ -13,17 +13,23 @@ #include <vector> #include "base/macros.h" +#include "base/observer_list.h" #include "ui/aura/env_observer.h" #include "ui/events/platform/platform_event_dispatcher.h" #include "ui/events/platform/x11/x11_event_source.h" #include "ui/gfx/x/x11_atom_cache.h" #include "ui/gfx/x/x11_types.h" #include "ui/views/views_export.h" +#include "ui/views/widget/desktop_aura/x11_desktop_handler_observer.h" namespace base { template <typename T> struct DefaultSingletonTraits; } +namespace ui { +class XScopedEventSelector; +} + namespace views { // A singleton that owns global objects related to the desktop and listens for @@ -35,27 +41,12 @@ class VIEWS_EXPORT X11DesktopHandler : public ui::PlatformEventDispatcher, // Returns the singleton handler. static X11DesktopHandler* get(); - // Gets/sets the X11 server time of the most recent mouse click, touch or - // key press on a Chrome window. - int wm_user_time_ms() const { return wm_user_time_ms_; } - void set_wm_user_time_ms(Time time_ms); - - // Sends a request to the window manager to activate |window|. - // This method should only be called if the window is already mapped. - void ActivateWindow(::Window window); + // Adds/removes X11DesktopHandlerObservers. + void AddObserver(X11DesktopHandlerObserver* observer); + void RemoveObserver(X11DesktopHandlerObserver* observer); - // Attempts to get the window manager to deactivate |window| by moving it to - // the bottom of the stack. Regardless of whether |window| was actually - // deactivated, sets the window as inactive in our internal state. - void DeactivateWindow(::Window window); - - // Checks if the current active window is |window|. - bool IsActiveWindow(::Window window) const; - - // Processes activation/focus related events. Some of these events are - // dispatched to the X11 window dispatcher, and not to the X11 root-window - // dispatcher. The window dispatcher sends these events to here. - void ProcessXEvent(XEvent* event); + // Gets the current workspace ID. + std::string GetWorkspace(); // ui::PlatformEventDispatcher bool CanDispatchEvent(const ui::PlatformEvent& event) override; @@ -66,44 +57,30 @@ class VIEWS_EXPORT X11DesktopHandler : public ui::PlatformEventDispatcher, void OnWillDestroyEnv() override; private: - enum ActiveState { - ACTIVE, - NOT_ACTIVE - }; - X11DesktopHandler(); ~X11DesktopHandler() override; - // Handles changes in activation. - void OnActiveWindowChanged(::Window window, ActiveState active_state); - // Called when |window| has been created or destroyed. |window| may not be // managed by Chrome. void OnWindowCreatedOrDestroyed(int event_type, XID window); + // Makes a round trip to the X server to get the current workspace. + bool UpdateWorkspace(); + // The display and the native X window hosting the root window. XDisplay* xdisplay_; // The native root window. ::Window x_root_window_; - // The last known active X window - ::Window x_active_window_; - - // The X11 server time of the most recent mouse click, touch, or key press - // on a Chrome window. - Time wm_user_time_ms_; - - // The active window according to X11 server. - ::Window current_window_; - - // Whether we should treat |current_window_| as active. In particular, we - // pretend that a window is deactivated after a call to DeactivateWindow(). - ActiveState current_window_active_state_; + // Events selected on x_root_window_. + std::unique_ptr<ui::XScopedEventSelector> x_root_window_events_; ui::X11AtomCache atom_cache_; - bool wm_supports_active_window_; + base::ObserverList<X11DesktopHandlerObserver> observers_; + + std::string workspace_; DISALLOW_COPY_AND_ASSIGN(X11DesktopHandler); }; diff --git a/chromium/ui/views/widget/desktop_aura/x11_desktop_handler_observer.h b/chromium/ui/views/widget/desktop_aura/x11_desktop_handler_observer.h new file mode 100644 index 00000000000..252ca213343 --- /dev/null +++ b/chromium/ui/views/widget/desktop_aura/x11_desktop_handler_observer.h @@ -0,0 +1,26 @@ +// Copyright 2016 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_WIDGET_DESKTOP_AURA_X11_DESKTOP_HANDLER_OBSERVER_H_ +#define UI_VIEWS_WIDGET_DESKTOP_AURA_X11_DESKTOP_HANDLER_OBSERVER_H_ + +#include <string> + +#include "ui/views/views_export.h" + +namespace views { + +class VIEWS_EXPORT X11DesktopHandlerObserver { + public: + // Called when the (platform-specific) workspace ID changes to + // |new_workspace|. + virtual void OnWorkspaceChanged(const std::string& new_workspace) = 0; + + protected: + virtual ~X11DesktopHandlerObserver() {} +}; + +} // namespace views + +#endif // UI_DISPLAY_DESKTOP_OBSERVER_H_ diff --git a/chromium/ui/views/widget/desktop_aura/x11_topmost_window_finder_interactive_uitest.cc b/chromium/ui/views/widget/desktop_aura/x11_topmost_window_finder_interactive_uitest.cc index 22dc294f8fb..9ca36c2da39 100644 --- a/chromium/ui/views/widget/desktop_aura/x11_topmost_window_finder_interactive_uitest.cc +++ b/chromium/ui/views/widget/desktop_aura/x11_topmost_window_finder_interactive_uitest.cc @@ -45,7 +45,7 @@ class MinimizeWaiter : public X11PropertyChangeWaiter { public: explicit MinimizeWaiter(XID window) : X11PropertyChangeWaiter(window, "_NET_WM_STATE") { - const char* kAtomsToCache[] = { "_NET_WM_STATE_HIDDEN", NULL }; + const char* const kAtomsToCache[] = {"_NET_WM_STATE_HIDDEN", nullptr}; atom_cache_.reset(new ui::X11AtomCache(gfx::GetXDisplay(), kAtomsToCache)); } @@ -84,7 +84,7 @@ class StackingClientListWaiter : public X11PropertyChangeWaiter { void Wait() override { // StackingClientListWaiter may be created after // _NET_CLIENT_LIST_STACKING already contains |expected_windows|. - if (!ShouldKeepOnWaiting(NULL)) + if (!ShouldKeepOnWaiting(nullptr)) return; X11PropertyChangeWaiter::Wait(); @@ -248,7 +248,7 @@ TEST_F(X11TopmostWindowFinderTest, Basic) { EXPECT_EQ(window1, FindTopmostLocalProcessWindowAt(150, 150)); EXPECT_EQ(xid2, FindTopmostXWindowAt(250, 150)); - EXPECT_EQ(NULL, FindTopmostLocalProcessWindowAt(250, 150)); + EXPECT_FALSE(FindTopmostLocalProcessWindowAt(250, 150)); EXPECT_EQ(xid3, FindTopmostXWindowAt(250, 250)); EXPECT_EQ(window3, FindTopmostLocalProcessWindowAt(250, 250)); @@ -262,14 +262,12 @@ TEST_F(X11TopmostWindowFinderTest, Basic) { EXPECT_NE(xid1, FindTopmostXWindowAt(1000, 1000)); EXPECT_NE(xid2, FindTopmostXWindowAt(1000, 1000)); EXPECT_NE(xid3, FindTopmostXWindowAt(1000, 1000)); - EXPECT_EQ(NULL, FindTopmostLocalProcessWindowAt(1000, 1000)); + EXPECT_FALSE(FindTopmostLocalProcessWindowAt(1000, 1000)); EXPECT_EQ(window1, FindTopmostLocalProcessWindowWithIgnore(150, 150, window3)); - EXPECT_EQ(NULL, - FindTopmostLocalProcessWindowWithIgnore(250, 250, window3)); - EXPECT_EQ(NULL, - FindTopmostLocalProcessWindowWithIgnore(150, 250, window3)); + EXPECT_FALSE(FindTopmostLocalProcessWindowWithIgnore(250, 250, window3)); + EXPECT_FALSE(FindTopmostLocalProcessWindowWithIgnore(150, 250, window3)); EXPECT_EQ(window1, FindTopmostLocalProcessWindowWithIgnore(150, 195, window3)); @@ -320,11 +318,10 @@ TEST_F(X11TopmostWindowFinderTest, NonRectangular) { std::unique_ptr<Widget> widget1( CreateAndShowWidget(gfx::Rect(100, 100, 100, 100))); XID xid1 = widget1->GetNativeWindow()->GetHost()->GetAcceleratedWidget(); - SkRegion* skregion1 = new SkRegion; + auto skregion1 = base::MakeUnique<SkRegion>(); skregion1->op(SkIRect::MakeXYWH(0, 10, 10, 90), SkRegion::kUnion_Op); skregion1->op(SkIRect::MakeXYWH(10, 0, 90, 100), SkRegion::kUnion_Op); - // Widget takes ownership of |skregion1|. - widget1->SetShape(skregion1); + widget1->SetShape(std::move(skregion1)); SkRegion skregion2; skregion2.op(SkIRect::MakeXYWH(0, 10, 10, 90), SkRegion::kUnion_Op); @@ -360,10 +357,10 @@ TEST_F(X11TopmostWindowFinderTest, NonRectangularEmptyShape) { std::unique_ptr<Widget> widget1( CreateAndShowWidget(gfx::Rect(100, 100, 100, 100))); XID xid1 = widget1->GetNativeWindow()->GetHost()->GetAcceleratedWidget(); - SkRegion* skregion1 = new SkRegion; + auto skregion1 = base::MakeUnique<SkRegion>(); skregion1->op(SkIRect::MakeXYWH(0, 0, 0, 0), SkRegion::kUnion_Op); // Widget takes ownership of |skregion1|. - widget1->SetShape(skregion1); + widget1->SetShape(std::move(skregion1)); XID xids[] = { xid1 }; StackingClientListWaiter stack_waiter(xids, arraysize(xids)); @@ -381,13 +378,12 @@ TEST_F(X11TopmostWindowFinderTest, NonRectangularNullShape) { std::unique_ptr<Widget> widget1( CreateAndShowWidget(gfx::Rect(100, 100, 100, 100))); XID xid1 = widget1->GetNativeWindow()->GetHost()->GetAcceleratedWidget(); - SkRegion* skregion1 = new SkRegion; + auto skregion1 = base::MakeUnique<SkRegion>(); skregion1->op(SkIRect::MakeXYWH(0, 0, 0, 0), SkRegion::kUnion_Op); - // Widget takes ownership of |skregion1|. - widget1->SetShape(skregion1); + widget1->SetShape(std::move(skregion1)); // Remove the shape - this is now just a normal window. - widget1->SetShape(NULL); + widget1->SetShape(nullptr); XID xids[] = { xid1 }; StackingClientListWaiter stack_waiter(xids, arraysize(xids)); @@ -415,7 +411,7 @@ TEST_F(X11TopmostWindowFinderTest, Menu) { CWOverrideRedirect, &swa); { - const char* kAtomsToCache[] = { "_NET_WM_WINDOW_TYPE_MENU", NULL }; + const char* const kAtomsToCache[] = {"_NET_WM_WINDOW_TYPE_MENU", nullptr}; ui::X11AtomCache atom_cache(gfx::GetXDisplay(), kAtomsToCache); ui::SetAtomProperty(menu_xid, "_NET_WM_WINDOW_TYPE", diff --git a/chromium/ui/views/widget/desktop_aura/x11_whole_screen_move_loop.cc b/chromium/ui/views/widget/desktop_aura/x11_whole_screen_move_loop.cc index cbf3f35638c..1258c61c9cd 100644 --- a/chromium/ui/views/widget/desktop_aura/x11_whole_screen_move_loop.cc +++ b/chromium/ui/views/widget/desktop_aura/x11_whole_screen_move_loop.cc @@ -10,15 +10,19 @@ #include <utility> #include "base/bind.h" +#include "base/logging.h" #include "base/macros.h" #include "base/message_loop/message_loop.h" #include "base/run_loop.h" +#include "base/single_thread_task_runner.h" +#include "base/threading/thread_task_runner_handle.h" #include "ui/aura/client/capture_client.h" #include "ui/aura/env.h" #include "ui/aura/window.h" #include "ui/aura/window_event_dispatcher.h" #include "ui/aura/window_tree_host.h" #include "ui/base/x/x11_util.h" +#include "ui/base/x/x11_window_event_manager.h" #include "ui/events/event.h" #include "ui/events/event_utils.h" #include "ui/events/keycodes/keyboard_code_conversion_x.h" @@ -71,6 +75,8 @@ bool X11WholeScreenMoveLoop::CanDispatchEvent(const ui::PlatformEvent& event) { } uint32_t X11WholeScreenMoveLoop::DispatchEvent(const ui::PlatformEvent& event) { + DCHECK(base::MessageLoopForUI::IsCurrent()); + // This method processes all events while the move loop is active. if (!in_move_loop_) return ui::POST_DISPATCH_PERFORM_DEFAULT; @@ -89,7 +95,7 @@ uint32_t X11WholeScreenMoveLoop::DispatchEvent(const ui::PlatformEvent& event) { // Post a task to dispatch mouse movement event when control returns to // the message loop. This allows smoother dragging since the events are // dispatched without waiting for the drag widget updates. - base::MessageLoopForUI::current()->PostTask( + base::ThreadTaskRunnerHandle::Get()->PostTask( FROM_HERE, base::Bind(&X11WholeScreenMoveLoop::DispatchMouseMovement, weak_factory_.GetWeakPtr())); @@ -136,7 +142,7 @@ bool X11WholeScreenMoveLoop::RunMoveLoop(aura::Window* source, // restored when the move loop finishes. initial_cursor_ = source->GetHost()->last_cursor(); - grab_input_window_ = CreateDragInputWindow(gfx::GetXDisplay()); + CreateDragInputWindow(gfx::GetXDisplay()); // Only grab mouse capture of |grab_input_window_| if |source| does not have // capture. @@ -227,6 +233,7 @@ void X11WholeScreenMoveLoop::EndMoveLoop() { // Restore the previous dispatcher. nested_dispatcher_.reset(); delegate_->OnMoveLoopEnded(); + grab_input_window_events_.reset(); XDestroyWindow(display, grab_input_window_); grab_input_window_ = None; @@ -236,7 +243,6 @@ void X11WholeScreenMoveLoop::EndMoveLoop() { bool X11WholeScreenMoveLoop::GrabPointer(gfx::NativeCursor cursor) { XDisplay* display = gfx::GetXDisplay(); - XGrabServer(display); // Pass "owner_events" as false so that X sends all mouse events to // |grab_input_window_|. @@ -245,7 +251,6 @@ bool X11WholeScreenMoveLoop::GrabPointer(gfx::NativeCursor cursor) { DLOG(ERROR) << "Grabbing pointer for dragging failed: " << ui::GetX11ErrorString(display, ret); } - XUngrabServer(display); XFlush(display); return ret == GrabSuccess; } @@ -259,21 +264,22 @@ void X11WholeScreenMoveLoop::GrabEscKey() { } } -Window X11WholeScreenMoveLoop::CreateDragInputWindow(XDisplay* display) { +void X11WholeScreenMoveLoop::CreateDragInputWindow(XDisplay* display) { unsigned long attribute_mask = CWEventMask | CWOverrideRedirect; XSetWindowAttributes swa; memset(&swa, 0, sizeof(swa)); - swa.event_mask = ButtonPressMask | ButtonReleaseMask | PointerMotionMask | - KeyPressMask | KeyReleaseMask | StructureNotifyMask; swa.override_redirect = True; - Window window = XCreateWindow(display, - DefaultRootWindow(display), - -100, -100, 10, 10, - 0, CopyFromParent, InputOnly, CopyFromParent, - attribute_mask, &swa); - XMapRaised(display, window); - ui::X11EventSource::GetInstance()->BlockUntilWindowMapped(window); - return window; + grab_input_window_ = XCreateWindow(display, DefaultRootWindow(display), -100, + -100, 10, 10, 0, CopyFromParent, InputOnly, + CopyFromParent, attribute_mask, &swa); + uint32_t event_mask = ButtonPressMask | ButtonReleaseMask | + PointerMotionMask | KeyPressMask | KeyReleaseMask | + StructureNotifyMask; + grab_input_window_events_.reset( + new ui::XScopedEventSelector(grab_input_window_, event_mask)); + + XMapRaised(display, grab_input_window_); + ui::X11EventSource::GetInstance()->BlockUntilWindowMapped(grab_input_window_); } } // namespace views diff --git a/chromium/ui/views/widget/desktop_aura/x11_whole_screen_move_loop.h b/chromium/ui/views/widget/desktop_aura/x11_whole_screen_move_loop.h index c1eb5515ef8..7ab2dfc8cde 100644 --- a/chromium/ui/views/widget/desktop_aura/x11_whole_screen_move_loop.h +++ b/chromium/ui/views/widget/desktop_aura/x11_whole_screen_move_loop.h @@ -27,6 +27,7 @@ class Window; namespace ui { class MouseEvent; class ScopedEventDispatcher; +class XScopedEventSelector; } namespace views { @@ -58,7 +59,7 @@ class X11WholeScreenMoveLoop : public X11MoveLoop, void GrabEscKey(); // Creates an input-only window to be used during the drag. - XID CreateDragInputWindow(XDisplay* display); + void CreateDragInputWindow(XDisplay* display); // Dispatch mouse movement event to |delegate_| in a posted task. void DispatchMouseMovement(); @@ -79,6 +80,9 @@ class X11WholeScreenMoveLoop : public X11MoveLoop, // are set on this window. XID grab_input_window_; + // Events selected on |grab_input_window_|. + std::unique_ptr<ui::XScopedEventSelector> grab_input_window_events_; + // Whether the pointer was grabbed on |grab_input_window_|. bool grabbed_pointer_; diff --git a/chromium/ui/views/widget/drop_helper.h b/chromium/ui/views/widget/drop_helper.h index 4e740aafe3d..8c2c44fd690 100644 --- a/chromium/ui/views/widget/drop_helper.h +++ b/chromium/ui/views/widget/drop_helper.h @@ -7,6 +7,8 @@ #include "base/macros.h" +#include "ui/views/views_export.h" + namespace gfx { class Point; } // namespace gfx @@ -26,7 +28,7 @@ class View; // DropHelper is intended to be used by a class that interacts with the system // drag and drop. The system class invokes OnDragOver as the mouse moves, // then either OnDragExit or OnDrop when the drop is done. -class DropHelper { +class VIEWS_EXPORT DropHelper { public: explicit DropHelper(View* root_view); ~DropHelper(); diff --git a/chromium/ui/views/widget/focus_manager_event_handler.cc b/chromium/ui/views/widget/focus_manager_event_handler.cc new file mode 100644 index 00000000000..f87e4381ce2 --- /dev/null +++ b/chromium/ui/views/widget/focus_manager_event_handler.cc @@ -0,0 +1,31 @@ +// Copyright 2016 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/widget/focus_manager_event_handler.h" + +#include "ui/aura/window.h" +#include "ui/views/focus/focus_manager.h" +#include "ui/views/widget/widget.h" + +namespace views { + +FocusManagerEventHandler::FocusManagerEventHandler(Widget* widget, + aura::Window* window) + : widget_(widget), window_(window) { + DCHECK(window_); + window_->AddPreTargetHandler(this); +} + +FocusManagerEventHandler::~FocusManagerEventHandler() { + window_->RemovePreTargetHandler(this); +} + +void FocusManagerEventHandler::OnKeyEvent(ui::KeyEvent* event) { + if (widget_ && widget_->GetFocusManager()->GetFocusedView() && + !widget_->GetFocusManager()->OnKeyEvent(*event)) { + event->StopPropagation(); + } +} + +} // namespace views diff --git a/chromium/ui/views/widget/focus_manager_event_handler.h b/chromium/ui/views/widget/focus_manager_event_handler.h new file mode 100644 index 00000000000..fcba5ac2673 --- /dev/null +++ b/chromium/ui/views/widget/focus_manager_event_handler.h @@ -0,0 +1,40 @@ +// Copyright 2016 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_WIDGET_FOCUS_MANAGER_EVENT_HANDLER_H_ +#define UI_VIEWS_WIDGET_FOCUS_MANAGER_EVENT_HANDLER_H_ + +#include "base/macros.h" +#include "ui/events/event_handler.h" + +namespace aura { +class Window; +} + +namespace views { + +class Widget; + +// This class forwards KeyEvents to the FocusManager associated with a widget. +// This allows KeyEvents to be processed before other targets. +class FocusManagerEventHandler : public ui::EventHandler { + public: + FocusManagerEventHandler(Widget* widget, aura::Window* window); + ~FocusManagerEventHandler() override; + + // Implementation of ui::EventHandler: + void OnKeyEvent(ui::KeyEvent* event) override; + + private: + Widget* widget_; + + // |window_| is the event target that is associated with this class. + aura::Window* window_; + + DISALLOW_COPY_AND_ASSIGN(FocusManagerEventHandler); +}; + +} // namespace views + +#endif // UI_VIEWS_WIDGET_FOCUS_MANAGER_EVENT_HANDLER_H_ diff --git a/chromium/ui/views/widget/native_widget_aura.cc b/chromium/ui/views/widget/native_widget_aura.cc index a455eddf268..874417b0b86 100644 --- a/chromium/ui/views/widget/native_widget_aura.cc +++ b/chromium/ui/views/widget/native_widget_aura.cc @@ -35,6 +35,7 @@ #include "ui/views/drag_utils.h" #include "ui/views/views_delegate.h" #include "ui/views/widget/drop_helper.h" +#include "ui/views/widget/focus_manager_event_handler.h" #include "ui/views/widget/native_widget_delegate.h" #include "ui/views/widget/root_view.h" #include "ui/views/widget/tooltip_manager_aura.h" @@ -97,6 +98,23 @@ void NativeWidgetAura::RegisterNativeWidgetForWindow( window->set_user_data(native_widget); } +// static +void NativeWidgetAura::AssignIconToAuraWindow(aura::Window* window, + const gfx::ImageSkia& window_icon, + const gfx::ImageSkia& app_icon) { + if (!window) + return; + + if (window_icon.isNull() && app_icon.isNull()) { + window->ClearProperty(aura::client::kWindowIconKey); + return; + } + + window->SetProperty( + aura::client::kWindowIconKey, + new gfx::ImageSkia(!window_icon.isNull() ? window_icon : app_icon)); +} + //////////////////////////////////////////////////////////////////////////////// // NativeWidgetAura, internal::NativeWidgetPrivate implementation: @@ -184,6 +202,11 @@ void NativeWidgetAura::InitNativeWidget(const Widget::InitParams& params) { aura::client::SetDragDropDelegate(window_, this); } + if (params.type == Widget::InitParams::TYPE_WINDOW) { + focus_manager_event_handler_ = + base::MakeUnique<FocusManagerEventHandler>(GetWidget(), window_); + } + aura::client::SetActivationDelegate(window_, this); window_reorderer_.reset(new WindowReorderer(window_, @@ -355,7 +378,7 @@ bool NativeWidgetAura::SetWindowTitle(const base::string16& title) { void NativeWidgetAura::SetWindowIcons(const gfx::ImageSkia& window_icon, const gfx::ImageSkia& app_icon) { - // Aura doesn't have window icons. + AssignIconToAuraWindow(window_, window_icon, app_icon); } void NativeWidgetAura::InitModalType(ui::ModalType modal_type) { @@ -442,17 +465,9 @@ void NativeWidgetAura::StackAtTop() { window_->parent()->StackChildAtTop(window_); } -void NativeWidgetAura::StackBelow(gfx::NativeView native_view) { - if (window_ && window_->parent() && - window_->parent() == native_view->parent()) - window_->parent()->StackChildBelow(window_, native_view); -} - -void NativeWidgetAura::SetShape(SkRegion* region) { +void NativeWidgetAura::SetShape(std::unique_ptr<SkRegion> region) { if (window_) - window_->layer()->SetAlphaShape(base::WrapUnique(region)); - else - delete region; + window_->layer()->SetAlphaShape(std::move(region)); } void NativeWidgetAura::Close() { @@ -562,6 +577,10 @@ void NativeWidgetAura::SetVisibleOnAllWorkspaces(bool always_visible) { // Not implemented on chromeos or for child widgets. } +bool NativeWidgetAura::IsVisibleOnAllWorkspaces() const { + return false; +} + void NativeWidgetAura::Maximize() { if (window_) window_->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_MAXIMIZED); @@ -849,6 +868,8 @@ void NativeWidgetAura::OnWindowDestroying(aura::Window* window) { // If the aura::Window is destroyed, we can no longer show tooltips. tooltip_manager_.reset(); + + focus_manager_event_handler_.reset(); } void NativeWidgetAura::OnWindowDestroyed(aura::Window* window) { diff --git a/chromium/ui/views/widget/native_widget_aura.h b/chromium/ui/views/widget/native_widget_aura.h index 8ef32c57e6b..d10746f556f 100644 --- a/chromium/ui/views/widget/native_widget_aura.h +++ b/chromium/ui/views/widget/native_widget_aura.h @@ -30,6 +30,7 @@ class FontList; namespace views { class DropHelper; +class FocusManagerEventHandler; class TooltipManagerAura; class WindowReorderer; @@ -50,6 +51,11 @@ class VIEWS_EXPORT NativeWidgetAura internal::NativeWidgetPrivate* native_widget, aura::Window* window); + // Assign an icon to aura window. + static void AssignIconToAuraWindow(aura::Window* window, + const gfx::ImageSkia& window_icon, + const gfx::ImageSkia& app_icon); + // Overridden from internal::NativeWidgetPrivate: void InitNativeWidget(const Widget::InitParams& params) override; void OnWidgetInitDone() override; @@ -88,8 +94,7 @@ class VIEWS_EXPORT NativeWidgetAura void SetSize(const gfx::Size& size) override; void StackAbove(gfx::NativeView native_view) override; void StackAtTop() override; - void StackBelow(gfx::NativeView native_view) override; - void SetShape(SkRegion* shape) override; + void SetShape(std::unique_ptr<SkRegion> shape) override; void Close() override; void CloseNow() override; void Show() override; @@ -103,6 +108,7 @@ class VIEWS_EXPORT NativeWidgetAura void SetAlwaysOnTop(bool always_on_top) override; bool IsAlwaysOnTop() const override; void SetVisibleOnAllWorkspaces(bool always_visible) override; + bool IsVisibleOnAllWorkspaces() const override; void Maximize() override; void Minimize() override; bool IsMaximized() const override; @@ -194,8 +200,6 @@ class VIEWS_EXPORT NativeWidgetAura internal::NativeWidgetDelegate* delegate() { return delegate_; } private: - class ActiveWindowObserver; - bool IsDocked() const; void SetInitialFocus(ui::WindowShowState show_state); @@ -226,6 +230,9 @@ class VIEWS_EXPORT NativeWidgetAura std::unique_ptr<DropHelper> drop_helper_; int last_drop_operation_; + // Native widget's handler to receive events before the event target. + std::unique_ptr<FocusManagerEventHandler> focus_manager_event_handler_; + // The following factory is used for calls to close the NativeWidgetAura // instance. base::WeakPtrFactory<NativeWidgetAura> close_widget_factory_; diff --git a/chromium/ui/views/widget/native_widget_mac.h b/chromium/ui/views/widget/native_widget_mac.h index 5e785ec681e..59867c02b54 100644 --- a/chromium/ui/views/widget/native_widget_mac.h +++ b/chromium/ui/views/widget/native_widget_mac.h @@ -25,7 +25,7 @@ class BridgedNativeWidget; class VIEWS_EXPORT NativeWidgetMac : public internal::NativeWidgetPrivate { public: - NativeWidgetMac(internal::NativeWidgetDelegate* delegate); + explicit NativeWidgetMac(internal::NativeWidgetDelegate* delegate); ~NativeWidgetMac() override; // Retrieves the bridge associated with the given NSWindow. Returns null if @@ -83,8 +83,7 @@ class VIEWS_EXPORT NativeWidgetMac : public internal::NativeWidgetPrivate { void SetSize(const gfx::Size& size) override; void StackAbove(gfx::NativeView native_view) override; void StackAtTop() override; - void StackBelow(gfx::NativeView native_view) override; - void SetShape(SkRegion* shape) override; + void SetShape(std::unique_ptr<SkRegion> shape) override; void Close() override; void CloseNow() override; void Show() override; @@ -98,6 +97,7 @@ class VIEWS_EXPORT NativeWidgetMac : public internal::NativeWidgetPrivate { void SetAlwaysOnTop(bool always_on_top) override; bool IsAlwaysOnTop() const override; void SetVisibleOnAllWorkspaces(bool always_visible) override; + bool IsVisibleOnAllWorkspaces() const override; void Maximize() override; void Minimize() override; bool IsMaximized() const override; diff --git a/chromium/ui/views/widget/native_widget_mac.mm b/chromium/ui/views/widget/native_widget_mac.mm index eca39288062..8ddf80139c2 100644 --- a/chromium/ui/views/widget/native_widget_mac.mm +++ b/chromium/ui/views/widget/native_widget_mac.mm @@ -8,9 +8,11 @@ #include <utility> +#import "base/mac/bind_objc_block.h" #include "base/mac/foundation_util.h" #include "base/mac/scoped_nsobject.h" #include "base/strings/sys_string_conversions.h" +#include "base/threading/thread_task_runner_handle.h" #import "ui/base/cocoa/constrained_window/constrained_window_animation.h" #import "ui/base/cocoa/window_size_constants.h" #include "ui/gfx/font_list.h" @@ -104,6 +106,7 @@ void NativeWidgetMac::OnWindowWillClose() { // is still a valid pointer, then reset it. if (bridge_) { delegate_->OnNativeWidgetDestroying(); + [GetNativeWindow() setDelegate:nil]; bridge_.reset(); } delegate_->OnNativeWidgetDestroyed(); @@ -330,11 +333,7 @@ void NativeWidgetMac::StackAtTop() { NOTIMPLEMENTED(); } -void NativeWidgetMac::StackBelow(gfx::NativeView native_view) { - NOTIMPLEMENTED(); -} - -void NativeWidgetMac::SetShape(SkRegion* shape) { +void NativeWidgetMac::SetShape(std::unique_ptr<SkRegion> shape) { NOTIMPLEMENTED(); } @@ -367,7 +366,13 @@ void NativeWidgetMac::Close() { // like -performClose:, first remove the window from AppKit's display // list to avoid crashes like http://crbug.com/156101. [window orderOut:nil]; - [window performSelector:@selector(close) withObject:nil afterDelay:0]; + + // Many tests assume that base::RunLoop().RunUntilIdle() is always sufficient + // to execute a close. However, in rare cases, -performSelector:..afterDelay:0 + // does not do this. So post a regular task. + base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, base::BindBlock(^{ + [window close]; + })); } void NativeWidgetMac::CloseNow() { @@ -455,6 +460,10 @@ void NativeWidgetMac::SetVisibleOnAllWorkspaces(bool always_visible) { gfx::SetNSWindowVisibleOnAllWorkspaces(GetNativeWindow(), always_visible); } +bool NativeWidgetMac::IsVisibleOnAllWorkspaces() const { + return false; +} + void NativeWidgetMac::Maximize() { NOTIMPLEMENTED(); // See IsMaximized(). } diff --git a/chromium/ui/views/widget/native_widget_mac_accessibility_unittest.mm b/chromium/ui/views/widget/native_widget_mac_accessibility_unittest.mm index 1b37e4bcd9d..8f224aa3776 100644 --- a/chromium/ui/views/widget/native_widget_mac_accessibility_unittest.mm +++ b/chromium/ui/views/widget/native_widget_mac_accessibility_unittest.mm @@ -14,6 +14,7 @@ #import "ui/accessibility/platform/ax_platform_node_mac.h" #include "ui/base/ime/text_input_type.h" #import "ui/gfx/mac/coordinate_conversion.h" +#include "ui/views/controls/label.h" #include "ui/views/controls/textfield/textfield.h" #include "ui/views/test/widget_test.h" #include "ui/views/widget/widget.h" @@ -168,6 +169,33 @@ TEST_F(NativeWidgetMacAccessibilityTest, PositionAttribute) { AttributeValueAtMidpoint(NSAccessibilityPositionAttribute)); } +// Test for NSAccessibilityHelpAttribute. +TEST_F(NativeWidgetMacAccessibilityTest, HelpAttribute) { + Label* label = new Label(base::SysNSStringToUTF16(kTestPlaceholderText)); + label->SetSize(GetWidgetBounds().size()); + EXPECT_NSEQ(nil, AttributeValueAtMidpoint(NSAccessibilityHelpAttribute)); + label->SetTooltipText(base::SysNSStringToUTF16(kTestPlaceholderText)); + widget()->GetContentsView()->AddChildView(label); + EXPECT_NSEQ(kTestPlaceholderText, + AttributeValueAtMidpoint(NSAccessibilityHelpAttribute)); +} + +// Test for NSAccessibilityWindowAttribute and +// NSAccessibilityTopLevelUIElementAttribute. +TEST_F(NativeWidgetMacAccessibilityTest, WindowAndTopLevelUIElementAttributes) { + FlexibleRoleTestView* view = new FlexibleRoleTestView(ui::AX_ROLE_GROUP); + view->SetSize(GetWidgetBounds().size()); + widget()->GetContentsView()->AddChildView(view); + // Make sure it's |view| in the hit test by checking its accessibility role. + EXPECT_EQ(NSAccessibilityGroupRole, + AttributeValueAtMidpoint(NSAccessibilityRoleAttribute)); + EXPECT_NSEQ(widget()->GetNativeWindow(), + AttributeValueAtMidpoint(NSAccessibilityWindowAttribute)); + EXPECT_NSEQ( + widget()->GetNativeWindow(), + AttributeValueAtMidpoint(NSAccessibilityTopLevelUIElementAttribute)); +} + // Tests for accessibility attributes on a views::Textfield. // TODO(patricialor): Test against Cocoa-provided attributes as well to ensure // consistency between Cocoa and toolkit-views. @@ -276,4 +304,38 @@ TEST_F(NativeWidgetMacAccessibilityTest, TextfieldEditableAttributes) { NSAccessibilityVisibleCharacterRangeAttribute) rangeValue])); } +// Test writing accessibility attributes via an accessibility client. +TEST_F(NativeWidgetMacAccessibilityTest, TextfieldWritableAttributes) { + Textfield* textfield = AddChildTextfield(GetWidgetBounds().size()); + + // Get the textfield accessibility object. + NSPoint midpoint = gfx::ScreenPointToNSPoint(GetWidgetBounds().CenterPoint()); + id ax_node = [widget()->GetNativeWindow() accessibilityHitTest:midpoint]; + EXPECT_TRUE(ax_node); + + // Make sure it's the correct accessibility object. + id value = + [ax_node accessibilityAttributeValue:NSAccessibilityValueAttribute]; + EXPECT_NSEQ(kTestStringValue, value); + + // Write a new NSAccessibilityValueAttribute. + EXPECT_TRUE( + [ax_node accessibilityIsAttributeSettable:NSAccessibilityValueAttribute]); + [ax_node accessibilitySetValue:kTestPlaceholderText + forAttribute:NSAccessibilityValueAttribute]; + EXPECT_NSEQ(kTestPlaceholderText, + AttributeValueAtMidpoint(NSAccessibilityValueAttribute)); + EXPECT_EQ(base::SysNSStringToUTF16(kTestPlaceholderText), textfield->text()); + + // Test a read-only textfield. + textfield->SetReadOnly(true); + EXPECT_FALSE( + [ax_node accessibilityIsAttributeSettable:NSAccessibilityValueAttribute]); + [ax_node accessibilitySetValue:kTestStringValue + forAttribute:NSAccessibilityValueAttribute]; + EXPECT_NSEQ(kTestPlaceholderText, + AttributeValueAtMidpoint(NSAccessibilityValueAttribute)); + EXPECT_EQ(base::SysNSStringToUTF16(kTestPlaceholderText), textfield->text()); +} + } // namespace views diff --git a/chromium/ui/views/widget/native_widget_mac_unittest.mm b/chromium/ui/views/widget/native_widget_mac_unittest.mm index c8124a9dec4..0dec9ded500 100644 --- a/chromium/ui/views/widget/native_widget_mac_unittest.mm +++ b/chromium/ui/views/widget/native_widget_mac_unittest.mm @@ -6,14 +6,12 @@ #import <Cocoa/Cocoa.h> -#include "base/location.h" #import "base/mac/foundation_util.h" -#import "base/mac/scoped_nsobject.h" #import "base/mac/scoped_nsautorelease_pool.h" +#import "base/mac/scoped_nsobject.h" #import "base/mac/scoped_objc_class_swizzler.h" #include "base/macros.h" #include "base/run_loop.h" -#include "base/single_thread_task_runner.h" #include "base/strings/sys_string_conversions.h" #include "base/strings/utf_string_conversions.h" #include "base/test/test_timeouts.h" @@ -260,6 +258,29 @@ class SimpleBubbleView : public BubbleDialogDelegateView { DISALLOW_COPY_AND_ASSIGN(SimpleBubbleView); }; +class CustomTooltipView : public View { + public: + CustomTooltipView(const base::string16& tooltip, View* tooltip_handler) + : tooltip_(tooltip), tooltip_handler_(tooltip_handler) {} + + // View: + bool GetTooltipText(const gfx::Point& p, + base::string16* tooltip) const override { + *tooltip = tooltip_; + return true; + } + + View* GetTooltipHandlerForPoint(const gfx::Point& point) override { + return tooltip_handler_ ? tooltip_handler_ : this; + } + + private: + base::string16 tooltip_; + View* tooltip_handler_; // Weak + + DISALLOW_COPY_AND_ASSIGN(CustomTooltipView); +}; + // Test visibility states triggered externally. TEST_F(NativeWidgetMacTest, HideAndShowExternally) { Widget* widget = CreateTopLevelPlatformWidget(); @@ -419,10 +440,10 @@ TEST_F(NativeWidgetMacTest, DISABLED_OrderFrontAfterMiniaturize) { // Wait and check that child is really visible. // TODO(kirr): remove the fixed delay. - base::MessageLoop::current()->PostDelayedTask( + base::ThreadTaskRunnerHandle::Get()->PostDelayedTask( FROM_HERE, base::MessageLoop::QuitWhenIdleClosure(), base::TimeDelta::FromSeconds(2)); - base::MessageLoop::current()->Run(); + base::RunLoop().Run(); EXPECT_FALSE(widget->IsMinimized()); EXPECT_TRUE(widget->IsVisible()); @@ -809,6 +830,43 @@ TEST_F(NativeWidgetMacTest, Tooltips) { widget->CloseNow(); } +// Tests case when mouse events are handled in one Widget, +// but tooltip belongs to another. +// It happens in menus when a submenu is shown and the parent gets the +// MouseExit event. +TEST_F(NativeWidgetMacTest, TwoWidgetTooltips) { + // Init two widgets, one above another. + Widget* widget_below = CreateTopLevelPlatformWidget(); + widget_below->SetBounds(gfx::Rect(50, 50, 200, 200)); + + Widget* widget_above = + CreateChildPlatformWidget(widget_below->GetNativeView()); + widget_above->SetBounds(gfx::Rect(100, 0, 100, 200)); + + const base::string16 tooltip_above = base::ASCIIToUTF16("Front"); + CustomTooltipView* view_above = new CustomTooltipView(tooltip_above, nullptr); + view_above->SetBoundsRect(widget_above->GetContentsView()->bounds()); + widget_above->GetContentsView()->AddChildView(view_above); + + CustomTooltipView* view_below = + new CustomTooltipView(base::ASCIIToUTF16("Back"), view_above); + view_below->SetBoundsRect(widget_below->GetContentsView()->bounds()); + widget_below->GetContentsView()->AddChildView(view_below); + + widget_below->Show(); + widget_above->Show(); + + // Move mouse above second widget and check that it returns tooltip + // for second. Despite that event was handled in the first one. + ui::test::EventGenerator event_generator(GetContext(), + widget_below->GetNativeWindow()); + event_generator.MoveMouseTo(gfx::Point(120, 60)); + EXPECT_EQ(tooltip_above, TooltipTextForWidget(widget_below)); + + widget_above->CloseNow(); + widget_below->CloseNow(); +} + namespace { // Delegate to make Widgets of a provided ui::ModalType. @@ -1318,6 +1376,42 @@ TEST_F(NativeWidgetMacTest, InvalidateShadow) { test_api.SimulateFrameSwap(gfx::Size(123, 456)); EXPECT_EQ(2, [window invalidateShadowCount]); + // Hiding the window does not require shadow invalidation. + widget->Hide(); + test_api.SimulateFrameSwap(gfx::Size(123, 456)); + EXPECT_EQ(2, [window invalidateShadowCount]); + + // Showing a translucent window after hiding it, should trigger shadow + // invalidation. + widget->Show(); + test_api.SimulateFrameSwap(gfx::Size(123, 456)); + EXPECT_EQ(3, [window invalidateShadowCount]); + + widget->CloseNow(); +} + +// Test that the contentView opacity corresponds to the window type. +TEST_F(NativeWidgetMacTest, ContentOpacity) { + NativeWidgetMacTestWindow* window; + Widget::InitParams init_params = + CreateParams(Widget::InitParams::TYPE_WINDOW_FRAMELESS); + + EXPECT_EQ(init_params.opacity, Widget::InitParams::INFER_OPACITY); + Widget* widget = CreateWidgetWithTestWindow(init_params, &window); + + // Infer should default to opaque on Mac. + EXPECT_TRUE([[window contentView] isOpaque]); + widget->CloseNow(); + + init_params.opacity = Widget::InitParams::TRANSLUCENT_WINDOW; + widget = CreateWidgetWithTestWindow(init_params, &window); + EXPECT_FALSE([[window contentView] isOpaque]); + widget->CloseNow(); + + // Test opaque explicitly. + init_params.opacity = Widget::InitParams::OPAQUE_WINDOW; + widget = CreateWidgetWithTestWindow(init_params, &window); + EXPECT_TRUE([[window contentView] isOpaque]); widget->CloseNow(); } diff --git a/chromium/ui/views/widget/native_widget_private.h b/chromium/ui/views/widget/native_widget_private.h index 39d31b608fc..71b44c40468 100644 --- a/chromium/ui/views/widget/native_widget_private.h +++ b/chromium/ui/views/widget/native_widget_private.h @@ -5,6 +5,7 @@ #ifndef UI_VIEWS_WIDGET_NATIVE_WIDGET_PRIVATE_H_ #define UI_VIEWS_WIDGET_NATIVE_WIDGET_PRIVATE_H_ +#include <memory> #include <string> #include "base/strings/string16.h" @@ -176,8 +177,7 @@ class VIEWS_EXPORT NativeWidgetPrivate : public NativeWidget { virtual void SetSize(const gfx::Size& size) = 0; virtual void StackAbove(gfx::NativeView native_view) = 0; virtual void StackAtTop() = 0; - virtual void StackBelow(gfx::NativeView native_view) = 0; - virtual void SetShape(SkRegion* shape) = 0; + virtual void SetShape(std::unique_ptr<SkRegion> shape) = 0; virtual void Close() = 0; virtual void CloseNow() = 0; virtual void Show() = 0; @@ -193,6 +193,7 @@ class VIEWS_EXPORT NativeWidgetPrivate : public NativeWidget { virtual void SetAlwaysOnTop(bool always_on_top) = 0; virtual bool IsAlwaysOnTop() const = 0; virtual void SetVisibleOnAllWorkspaces(bool always_visible) = 0; + virtual bool IsVisibleOnAllWorkspaces() const = 0; virtual void Maximize() = 0; virtual void Minimize() = 0; virtual bool IsMaximized() const = 0; diff --git a/chromium/ui/views/widget/widget.cc b/chromium/ui/views/widget/widget.cc index 62b4b566809..68e6acd9c8a 100644 --- a/chromium/ui/views/widget/widget.cc +++ b/chromium/ui/views/widget/widget.cc @@ -553,12 +553,8 @@ void Widget::StackAtTop() { native_widget_->StackAtTop(); } -void Widget::StackBelow(gfx::NativeView native_view) { - native_widget_->StackBelow(native_view); -} - -void Widget::SetShape(SkRegion* shape) { - native_widget_->SetShape(shape); +void Widget::SetShape(std::unique_ptr<SkRegion> shape) { + native_widget_->SetShape(std::move(shape)); } void Widget::Close() { @@ -666,6 +662,10 @@ void Widget::SetVisibleOnAllWorkspaces(bool always_visible) { native_widget_->SetVisibleOnAllWorkspaces(always_visible); } +bool Widget::IsVisibleOnAllWorkspaces() const { + return native_widget_->IsVisibleOnAllWorkspaces(); +} + void Widget::Maximize() { native_widget_->Maximize(); } @@ -1206,7 +1206,14 @@ void Widget::OnMouseEvent(ui::MouseEvent* event) { } if (root_view) root_view->OnMouseReleased(*event); - if ((event->flags() & ui::EF_IS_NON_CLIENT) == 0) + if ((event->flags() & ui::EF_IS_NON_CLIENT) == 0 && + // If none of the "normal" buttons are pressed, this event may be from + // one of the newer mice that have buttons bound to browser forward + // back actions. Don't squelch the event and let the default handler + // process it. + (event->flags() & + (ui::EF_LEFT_MOUSE_BUTTON | ui::EF_MIDDLE_MOUSE_BUTTON | + ui::EF_RIGHT_MOUSE_BUTTON)) != 0) event->SetHandled(); return; @@ -1452,7 +1459,7 @@ void Widget::SetInitialBoundsForFramelessWindow(const gfx::Rect& bounds) { native_widget_->CenterWindow(size); } else { // Use the supplied initial bounds. - SetBoundsConstrained(bounds); + SetBounds(bounds); } } diff --git a/chromium/ui/views/widget/widget.h b/chromium/ui/views/widget/widget.h index 0376866fc25..06c8f12a46c 100644 --- a/chromium/ui/views/widget/widget.h +++ b/chromium/ui/views/widget/widget.h @@ -8,7 +8,6 @@ #include <map> #include <memory> #include <set> -#include <stack> #include <string> #include <vector> @@ -49,10 +48,6 @@ class Point; class Rect; } -namespace mus { -class Window; -} - namespace ui { class Accelerator; class Compositor; @@ -62,7 +57,8 @@ class Layer; class NativeTheme; class OSExchangeData; class ThemeProvider; -} +class Window; +} // namespace ui namespace views { @@ -170,12 +166,15 @@ class VIEWS_EXPORT Widget : public internal::NativeWidgetDelegate, enum WindowOpacity { // Infer fully opaque or not. For WinAura, top-level windows that are not - // of TYPE_WINDOW are translucent so that they can be made to fade in. In - // all other cases, windows are fully opaque. + // of TYPE_WINDOW are translucent so that they can be made to fade in. + // For LinuxAura, only windows that are TYPE_DRAG are translucent. In all + // other cases, windows are fully opaque. INFER_OPACITY, // Fully opaque. OPAQUE_WINDOW, - // Possibly translucent/transparent. + // Possibly translucent/transparent. Widgets that fade in or out using + // SetOpacity() but do not make use of an alpha channel should use + // INFER_OPACITY. TRANSLUCENT_WINDOW, }; @@ -245,7 +244,7 @@ class VIEWS_EXPORT Widget : public internal::NativeWidgetDelegate, ui::WindowShowState show_state; gfx::NativeView parent; // Used only by mus and is necessitated by mus not being a NativeView. - mus::Window* parent_mus = nullptr; + ui::Window* parent_mus = nullptr; // Specifies the initial bounds of the Widget. Default is empty, which means // the NativeWidget may specify a default size. If the parent is specified, // |bounds| is in the parent's coordinate system. If the parent is not @@ -475,12 +474,9 @@ class VIEWS_EXPORT Widget : public internal::NativeWidgetDelegate, void StackAbove(gfx::NativeView native_view); void StackAtTop(); - // Places the widget below the specified NativeView. - void StackBelow(gfx::NativeView native_view); - // Sets a shape on the widget. Passing a NULL |shape| reverts the widget to - // be rectangular. Takes ownership of |shape|. - void SetShape(SkRegion* shape); + // be rectangular. + void SetShape(std::unique_ptr<SkRegion> shape); // Hides the widget then closes it after a return to the message loop. virtual void Close(); @@ -524,6 +520,12 @@ class VIEWS_EXPORT Widget : public internal::NativeWidgetDelegate, // Sets the widget to be visible on all work spaces. void SetVisibleOnAllWorkspaces(bool always_visible); + // Is this widget currently visible on all workspaces? + // A call to SetVisibleOnAllWorkspaces(true) won't necessarily mean + // IsVisbleOnAllWorkspaces() == true (for example, when the platform doesn't + // support workspaces). + bool IsVisibleOnAllWorkspaces() const; + // Maximizes/minimizes/restores the window. void Maximize(); void Minimize(); diff --git a/chromium/ui/views/widget/widget_delegate.cc b/chromium/ui/views/widget/widget_delegate.cc index c5f9d97490c..917124976c4 100644 --- a/chromium/ui/views/widget/widget_delegate.cc +++ b/chromium/ui/views/widget/widget_delegate.cc @@ -79,11 +79,7 @@ bool WidgetDelegate::ShouldShowWindowTitle() const { } bool WidgetDelegate::ShouldShowCloseButton() const { -#if defined(OS_MACOSX) - return false; -#else return true; -#endif } bool WidgetDelegate::ShouldHandleSystemCommands() const { @@ -208,6 +204,10 @@ const Widget* WidgetDelegateView::GetWidget() const { return View::GetWidget(); } +views::View* WidgetDelegateView::GetContentsView() { + return this; +} + const char* WidgetDelegateView::GetClassName() const { return kViewClassName; } diff --git a/chromium/ui/views/widget/widget_delegate.h b/chromium/ui/views/widget/widget_delegate.h index 8a344c55623..2ce16e971af 100644 --- a/chromium/ui/views/widget/widget_delegate.h +++ b/chromium/ui/views/widget/widget_delegate.h @@ -211,6 +211,7 @@ class VIEWS_EXPORT WidgetDelegateView : public WidgetDelegate, public View { void DeleteDelegate() override; Widget* GetWidget() override; const Widget* GetWidget() const override; + views::View* GetContentsView() override; // View: const char* GetClassName() const override; diff --git a/chromium/ui/views/widget/widget_unittest.cc b/chromium/ui/views/widget/widget_unittest.cc index 45ea0283446..f7b3e691bfa 100644 --- a/chromium/ui/views/widget/widget_unittest.cc +++ b/chromium/ui/views/widget/widget_unittest.cc @@ -7,7 +7,6 @@ #include <set> #include "base/bind.h" -#include "base/command_line.h" #include "base/macros.h" #include "base/message_loop/message_loop.h" #include "base/run_loop.h" @@ -48,11 +47,6 @@ #include "base/mac/mac_util.h" #endif -#if defined(USE_X11) && !defined(OS_CHROMEOS) -#include "ui/base/x/x11_util_internal.h" // nogncheck -#include "ui/gfx/x/x11_switches.h" // nogncheck -#endif - namespace views { namespace test { @@ -935,11 +929,7 @@ TEST_F(WidgetObserverTest, WidgetBoundsChangedNative) { // Move, but don't change the size. widget->SetBounds(gfx::Rect(110, 110, 170, 100)); - // Currently fails on Mus. http://crbug.com/622575. - if (IsMus()) - EXPECT_FALSE(widget_bounds_changed()); - else - EXPECT_TRUE(widget_bounds_changed()); + EXPECT_TRUE(widget_bounds_changed()); reset(); // Moving to the same place does nothing. @@ -1996,8 +1986,6 @@ class GetNativeThemeFromDestructorView : public WidgetDelegateView { GetNativeThemeFromDestructorView() {} ~GetNativeThemeFromDestructorView() override { VerifyNativeTheme(); } - View* GetContentsView() override { return this; } - private: void VerifyNativeTheme() { ASSERT_TRUE(GetNativeTheme() != NULL); @@ -2088,6 +2076,7 @@ class WidgetBoundsObserver : public WidgetObserver { // WidgetObserver: void OnWidgetDestroying(Widget* widget) override { EXPECT_TRUE(widget->GetNativeWindow()); + EXPECT_TRUE(Widget::GetWidgetForNativeWindow(widget->GetNativeWindow())); bounds_ = widget->GetWindowBoundsInScreen(); } @@ -3640,6 +3629,8 @@ TEST_F(WidgetTest, WidgetRemovalsObserverCalledWhenMovingBetweenWidgets) { #if defined(OS_WIN) +namespace { + // Provides functionality to create a window modal dialog. class ModalDialogDelegate : public DialogDelegateView { public: @@ -3655,6 +3646,8 @@ private: DISALLOW_COPY_AND_ASSIGN(ModalDialogDelegate); }; +} // namespace + // Tests the case where an intervening owner popup window is destroyed out from // under the currently active modal top-level window. In this instance, the // remaining top-level windows should be re-enabled. @@ -3743,16 +3736,6 @@ void InitializeWidgetForOpacity( Widget& widget, Widget::InitParams init_params, const Widget::InitParams::WindowOpacity opacity) { -#if defined(USE_X11) - // On Linux, transparent visuals is currently not activated by default. - base::CommandLine* command_line = base::CommandLine::ForCurrentProcess(); - command_line->AppendSwitch(switches::kEnableTransparentVisuals); - - int depth = 0; - ui::ChooseVisualForWindow(NULL, &depth); - EXPECT_EQ(depth, 32); -#endif - init_params.opacity = opacity; init_params.show_state = ui::SHOW_STATE_NORMAL; init_params.bounds = gfx::Rect(0, 0, 500, 500); @@ -3760,31 +3743,79 @@ void InitializeWidgetForOpacity( init_params.native_widget = CreatePlatformDesktopNativeWidgetImpl(init_params, &widget, nullptr); widget.Init(init_params); +} + +class CompositingWidgetTest : public views::test::WidgetTest { + public: + CompositingWidgetTest() + : widget_types_{Widget::InitParams::TYPE_WINDOW, + Widget::InitParams::TYPE_PANEL, + Widget::InitParams::TYPE_WINDOW_FRAMELESS, + Widget::InitParams::TYPE_CONTROL, + Widget::InitParams::TYPE_POPUP, + Widget::InitParams::TYPE_MENU, + Widget::InitParams::TYPE_TOOLTIP, + Widget::InitParams::TYPE_BUBBLE, + Widget::InitParams::TYPE_DRAG} {} + ~CompositingWidgetTest() override {} + + void CheckAllWidgetsForOpacity( + const Widget::InitParams::WindowOpacity opacity) { + for (const auto& widget_type : widget_types_) { +#if defined(OS_MACOSX) + // Tooltips are native on Mac. See BridgedNativeWidget::Init. + if (widget_type == Widget::InitParams::TYPE_TOOLTIP) + continue; +#elif defined(OS_WIN) + // Other widget types would require to create a parent window and the + // the purpose of this test is mainly X11 in the first place. + if (widget_type != Widget::InitParams::TYPE_WINDOW) + continue; +#endif + Widget widget; + InitializeWidgetForOpacity(widget, CreateParams(widget_type), opacity); + + // Use NativeWidgetAura directly. + if (IsMus() && + (widget_type == Widget::InitParams::TYPE_WINDOW_FRAMELESS || + widget_type == Widget::InitParams::TYPE_CONTROL)) + continue; + + EXPECT_EQ(IsNativeWindowTransparent(widget.GetNativeWindow()), + widget.ShouldWindowContentsBeTransparent()); + + // When using the Mandoline UI Service, the translucency does not rely on + // the widget type. + if (IsMus()) + continue; #if defined(USE_X11) - EXPECT_TRUE(widget.IsTranslucentWindowOpacitySupported()); + if (HasCompositingManager() && + (widget_type == Widget::InitParams::TYPE_DRAG || + widget_type == Widget::InitParams::TYPE_WINDOW)) { + EXPECT_TRUE(widget.IsTranslucentWindowOpacitySupported()); + } else { + EXPECT_FALSE(widget.IsTranslucentWindowOpacitySupported()); + } #endif -} + } + } + + protected: + const std::vector<Widget::InitParams::Type> widget_types_; + + DISALLOW_COPY_AND_ASSIGN(CompositingWidgetTest); +}; } // namespace // Test opacity when compositing is enabled. -TEST_F(WidgetTest, Transparency_DesktopWidgetInferOpacity) { - Widget widget; - InitializeWidgetForOpacity(widget, - CreateParams(Widget::InitParams::TYPE_WINDOW), - Widget::InitParams::INFER_OPACITY); - EXPECT_EQ(IsNativeWindowTransparent(widget.GetNativeWindow()), - widget.ShouldWindowContentsBeTransparent()); +TEST_F(CompositingWidgetTest, Transparency_DesktopWidgetInferOpacity) { + CheckAllWidgetsForOpacity(Widget::InitParams::INFER_OPACITY); } -TEST_F(WidgetTest, Transparency_DesktopWidgetOpaque) { - Widget widget; - InitializeWidgetForOpacity(widget, - CreateParams(Widget::InitParams::TYPE_WINDOW), - Widget::InitParams::OPAQUE_WINDOW); - EXPECT_EQ(IsNativeWindowTransparent(widget.GetNativeWindow()), - widget.ShouldWindowContentsBeTransparent()); +TEST_F(CompositingWidgetTest, Transparency_DesktopWidgetOpaque) { + CheckAllWidgetsForOpacity(Widget::InitParams::OPAQUE_WINDOW); } // Failing on Mac. http://cbrug.com/623421 @@ -3795,13 +3826,8 @@ TEST_F(WidgetTest, Transparency_DesktopWidgetOpaque) { #define MAYBE_Transparency_DesktopWidgetTranslucent \ Transparency_DesktopWidgetTranslucent #endif -TEST_F(WidgetTest, MAYBE_Transparency_DesktopWidgetTranslucent) { - Widget widget; - InitializeWidgetForOpacity(widget, - CreateParams(Widget::InitParams::TYPE_WINDOW), - Widget::InitParams::TRANSLUCENT_WINDOW); - EXPECT_EQ(IsNativeWindowTransparent(widget.GetNativeWindow()), - widget.ShouldWindowContentsBeTransparent()); +TEST_F(CompositingWidgetTest, MAYBE_Transparency_DesktopWidgetTranslucent) { + CheckAllWidgetsForOpacity(Widget::InitParams::TRANSLUCENT_WINDOW); } #endif // !defined(OS_CHROMEOS) diff --git a/chromium/ui/views/win/hwnd_message_handler.cc b/chromium/ui/views/win/hwnd_message_handler.cc index b035fd32716..020d0a1f9e0 100644 --- a/chromium/ui/views/win/hwnd_message_handler.cc +++ b/chromium/ui/views/win/hwnd_message_handler.cc @@ -29,6 +29,7 @@ #include "ui/base/win/shell.h" #include "ui/base/win/touch_input.h" #include "ui/display/win/dpi.h" +#include "ui/display/win/screen_win.h" #include "ui/events/event.h" #include "ui/events/event_utils.h" #include "ui/events/keycodes/keyboard_code_conversion_win.h" @@ -221,14 +222,6 @@ bool IsTopLevelWindow(HWND window) { return !parent || (parent == ::GetDesktopWindow()); } -void AddScrollStylesToWindow(HWND window) { - if (::IsWindow(window)) { - long current_style = ::GetWindowLong(window, GWL_STYLE); - ::SetWindowLong(window, GWL_STYLE, - current_style | WS_VSCROLL | WS_HSCROLL); - } -} - const int kTouchDownContextResetTimeout = 500; // Windows does not flag synthesized mouse messages from touch in all cases. @@ -322,6 +315,7 @@ HWNDMessageHandler::HWNDMessageHandler(HWNDMessageHandlerDelegate* delegate) restored_enabled_(false), current_cursor_(NULL), previous_cursor_(NULL), + dpi_(0), active_mouse_tracking_flags_(0), is_right_mouse_pressed_on_caption_(false), lock_updates_count_(0), @@ -330,7 +324,6 @@ HWNDMessageHandler::HWNDMessageHandler(HWNDMessageHandlerDelegate* delegate) is_first_nccalc_(true), menu_depth_(0), id_generator_(0), - needs_scroll_styles_(false), in_size_loop_(false), touch_down_contexts_(0), last_mouse_hwheel_time_(0), @@ -355,28 +348,20 @@ void HWNDMessageHandler::Init(HWND parent, const gfx::Rect& bounds) { // Create the window. WindowImpl::Init(parent, bounds); - // TODO(ananta) - // Remove the scrolling hack code once we have scrolling working well. -#if defined(ENABLE_SCROLL_HACK) - // Certain trackpad drivers on Windows have bugs where in they don't generate - // WM_MOUSEWHEEL messages for the trackpoint and trackpad scrolling gestures - // unless there is an entry for Chrome with the class name of the Window. - // These drivers check if the window under the trackpoint has the WS_VSCROLL/ - // WS_HSCROLL style and if yes they generate the legacy WM_VSCROLL/WM_HSCROLL - // messages. We add these styles to ensure that trackpad/trackpoint scrolling - // work. - // TODO(ananta) - // Look into moving the WS_VSCROLL and WS_HSCROLL style setting logic to the - // CalculateWindowStylesFromInitParams function. Doing it there seems to - // cause some interactive tests to fail. Investigation needed. - if (IsTopLevelWindow(hwnd())) { - long current_style = ::GetWindowLong(hwnd(), GWL_STYLE); - if (!(current_style & WS_POPUP)) { - AddScrollStylesToWindow(hwnd()); - needs_scroll_styles_ = true; - } + + if (delegate_->HasFrame() && base::win::IsProcessPerMonitorDpiAware()) { + static auto enable_child_window_dpi_message_func = []() { + // Derived signature; not available in headers. + // This call gets Windows to scale the non-client area when WM_DPICHANGED + // is fired. + using EnableChildWindowDpiMessagePtr = LRESULT (WINAPI*)(HWND, BOOL); + return reinterpret_cast<EnableChildWindowDpiMessagePtr>( + GetProcAddress(GetModuleHandle(L"user32.dll"), + "EnableChildWindowDpiMessage")); + }(); + if (enable_child_window_dpi_message_func) + enable_child_window_dpi_message_func(hwnd(), TRUE); } -#endif prop_window_target_.reset(new ui::ViewProp(hwnd(), ui::WindowEventTarget::kWin32InputEventTarget, @@ -801,8 +786,7 @@ void HWNDMessageHandler::SetCursor(HCURSOR cursor) { } void HWNDMessageHandler::FrameTypeChanged() { - if (!custom_window_region_.is_valid() && - delegate_->GetFrameMode() == FrameMode::SYSTEM_DRAWN) + if (!custom_window_region_.is_valid() && IsFrameSystemDrawn()) dwm_transition_desired_ = true; if (!dwm_transition_desired_ || !IsFullscreen()) PerformDwmTransition(); @@ -1132,6 +1116,9 @@ void HWNDMessageHandler::ClientAreaSizeChanged() { return; gfx::Size s = GetClientAreaBounds().size(); delegate_->HandleClientSizeChanged(s); + + current_window_size_message_++; + sent_window_size_changing_ = false; } bool HWNDMessageHandler::GetClientAreaInsets(gfx::Insets* insets) const { @@ -1168,8 +1155,7 @@ void HWNDMessageHandler::ResetWindowRegion(bool force, bool redraw) { // the delegate to allow for a custom hit mask. if ((window_ex_style() & WS_EX_COMPOSITED) == 0 && !custom_window_region_.is_valid() && - (delegate_->GetFrameMode() == FrameMode::SYSTEM_DRAWN || - !delegate_->HasNonClientView())) { + (IsFrameSystemDrawn() || !delegate_->HasNonClientView())) { if (force) SetWindowRgn(hwnd(), NULL, redraw); return; @@ -1275,9 +1261,14 @@ void HWNDMessageHandler::ForceRedrawWindow(int attempts) { InvalidateRect(hwnd(), NULL, FALSE); } +bool HWNDMessageHandler::IsFrameSystemDrawn() const { + FrameMode frame_mode = delegate_->GetFrameMode(); + return frame_mode == FrameMode::SYSTEM_DRAWN || + frame_mode == FrameMode::SYSTEM_DRAWN_NO_CONTROLS; +} + bool HWNDMessageHandler::HasSystemFrame() const { - return delegate_->HasFrame() && - delegate_->GetFrameMode() == FrameMode::SYSTEM_DRAWN; + return delegate_->HasFrame() && IsFrameSystemDrawn(); } // Message handlers ------------------------------------------------------------ @@ -1367,6 +1358,9 @@ LRESULT HWNDMessageHandler::OnCreate(CREATESTRUCT* create_struct) { base::Bind(&HWNDMessageHandler::OnSessionChange, base::Unretained(this)))); + float scale_factor = display::win::ScreenWin::GetScaleFactorForHWND(hwnd()); + dpi_ = display::win::GetDPIFromScalingFactor(scale_factor); + // TODO(beng): move more of NWW::OnCreate here. return 0; } @@ -1412,9 +1406,18 @@ LRESULT HWNDMessageHandler::OnDpiChanged(UINT msg, if (LOWORD(w_param) != HIWORD(w_param)) NOTIMPLEMENTED() << "Received non-square scaling factors"; + // The first WM_DPICHANGED originates from EnableChildWindowDpiMessage during + // initialization. We don't want to propagate this as the client is already + // set at the current scale factor and may cause the window to display too + // soon. See http://crbug.com/625076. + int dpi = LOWORD(w_param); + if (dpi_ == dpi) + return 0; + + dpi_ = dpi; SetBoundsInternal(gfx::Rect(*reinterpret_cast<RECT*>(l_param)), false); delegate_->HandleWindowScaleFactorChanged( - display::win::GetScalingFactorFromDPI(LOWORD(w_param))); + display::win::GetScalingFactorFromDPI(dpi_)); return 0; } @@ -1424,13 +1427,6 @@ void HWNDMessageHandler::OnEnterMenuLoop(BOOL from_track_popup_menu) { } void HWNDMessageHandler::OnEnterSizeMove() { - // Please refer to the comments in the OnSize function about the scrollbar - // hack. - // Hide the Windows scrollbar if the scroll styles are present to ensure - // that a paint flicker does not occur while sizing. - if (in_size_loop_ && needs_scroll_styles_) - ShowScrollBar(hwnd(), SB_BOTH, FALSE); - delegate_->HandleBeginWMSizeMove(); SetMsgHandled(FALSE); } @@ -1449,13 +1445,6 @@ void HWNDMessageHandler::OnExitMenuLoop(BOOL is_shortcut_menu) { void HWNDMessageHandler::OnExitSizeMove() { delegate_->HandleEndWMSizeMove(); SetMsgHandled(FALSE); - // Please refer to the notes in the OnSize function for information about - // the scrolling hack. - // We hide the Windows scrollbar in the OnEnterSizeMove function. We need - // to add the scroll styles back to ensure that scrolling works in legacy - // trackpoint drivers. - if (in_size_loop_ && needs_scroll_styles_) - AddScrollStylesToWindow(hwnd()); // If the window was moved to a monitor which has a fullscreen window active, // we need to reduce the size of the fullscreen window by 1px. CheckAndHandleBackgroundFullscreenOnMonitor(hwnd()); @@ -1762,7 +1751,7 @@ LRESULT HWNDMessageHandler::OnNCCalcSize(BOOL mode, LPARAM l_param) { if (autohide_edges & ViewsDelegate::EDGE_LEFT) client_rect->left += kAutoHideTaskbarThicknessPx; if (autohide_edges & ViewsDelegate::EDGE_TOP) { - if (delegate_->GetFrameMode() == FrameMode::SYSTEM_DRAWN) { + if (IsFrameSystemDrawn()) { // Tricky bit. Due to a bug in DwmDefWindowProc()'s handling of // WM_NCHITTEST, having any nonclient area atop the window causes the // caption buttons to draw onscreen but not respond to mouse @@ -1823,7 +1812,8 @@ LRESULT HWNDMessageHandler::OnNCHitTest(const gfx::Point& point) { // If the DWM is rendering the window controls, we need to give the DWM's // default window procedure first chance to handle hit testing. - if (HasSystemFrame()) { + if (HasSystemFrame() && + delegate_->GetFrameMode() != FrameMode::SYSTEM_DRAWN_NO_CONTROLS) { LRESULT result; if (DwmDefWindowProc(hwnd(), WM_NCHITTEST, 0, MAKELPARAM(point.x(), point.y()), &result)) { @@ -1839,49 +1829,6 @@ LRESULT HWNDMessageHandler::OnNCHitTest(const gfx::Point& point) { // us. LRESULT hit_test_code = DefWindowProc(hwnd(), WM_NCHITTEST, 0, MAKELPARAM(point.x(), point.y())); - if (needs_scroll_styles_) { - switch (hit_test_code) { - // If we faked the WS_VSCROLL and WS_HSCROLL styles for this window, then - // Windows returns the HTVSCROLL or HTHSCROLL hit test codes if we hover - // or click on the non client portions of the window where the OS - // scrollbars would be drawn. These hittest codes are returned even when - // the scrollbars are hidden, which is the case in Aura. We fake the - // hittest code as HTCLIENT in this case to ensure that we receive client - // mouse messages as opposed to non client mouse messages. - case HTVSCROLL: - case HTHSCROLL: - hit_test_code = HTCLIENT; - break; - - case HTBOTTOMRIGHT: { - // Normally the HTBOTTOMRIGHT hittest code is received when we hover - // near the bottom right of the window. However due to our fake scroll - // styles, we get this code even when we hover around the area where - // the vertical scrollar down arrow would be drawn. - // We check if the hittest coordinates lie in this region and if yes - // we return HTCLIENT. - int border_width = ::GetSystemMetrics(SM_CXSIZEFRAME); - int border_height = ::GetSystemMetrics(SM_CYSIZEFRAME); - int scroll_width = ::GetSystemMetrics(SM_CXVSCROLL); - int scroll_height = ::GetSystemMetrics(SM_CYVSCROLL); - RECT window_rect; - ::GetWindowRect(hwnd(), &window_rect); - window_rect.bottom -= border_height; - window_rect.right -= border_width; - window_rect.left = window_rect.right - scroll_width; - window_rect.top = window_rect.bottom - scroll_height; - POINT pt; - pt.x = point.x(); - pt.y = point.y(); - if (::PtInRect(&window_rect, pt)) - hit_test_code = HTCLIENT; - break; - } - - default: - break; - } - } return hit_test_code; } @@ -1910,8 +1857,7 @@ void HWNDMessageHandler::OnNCPaint(HRGN rgn) { // We only do non-client painting if we're not using the system frame. // It's required to avoid some native painting artifacts from appearing when // the window is resized. - if (!delegate_->HasNonClientView() || - delegate_->GetFrameMode() == FrameMode::SYSTEM_DRAWN) { + if (!delegate_->HasNonClientView() || IsFrameSystemDrawn()) { if (ui::win::IsAeroGlassEnabled()) { // The default WM_NCPAINT handler under Aero Glass doesn't clear the // nonclient area, so it'll remain the default white color. That area is @@ -2129,19 +2075,6 @@ void HWNDMessageHandler::OnSize(UINT param, const gfx::Size& size) { // ResetWindowRegion is going to trigger WM_NCPAINT. By doing it after we've // invoked OnSize we ensure the RootView has been laid out. ResetWindowRegion(false, true); - - // We add the WS_VSCROLL and WS_HSCROLL styles to top level windows to ensure - // that legacy trackpad/trackpoint drivers generate the WM_VSCROLL and - // WM_HSCROLL messages and scrolling works. - // We want the scroll styles to be present on the window. However we don't - // want Windows to draw the scrollbars. To achieve this we hide the scroll - // bars and readd them to the window style in a posted task to ensure that we - // don't get nested WM_SIZE messages. - if (needs_scroll_styles_ && !in_size_loop_) { - ShowScrollBar(hwnd(), SB_BOTH, FALSE); - base::ThreadTaskRunnerHandle::Get()->PostTask( - FROM_HERE, base::Bind(&AddScrollStylesToWindow, hwnd())); - } } void HWNDMessageHandler::OnSysCommand(UINT notification_code, @@ -2361,6 +2294,12 @@ void HWNDMessageHandler::OnWindowPosChanging(WINDOWPOS* window_pos) { window_pos->flags & SWP_FRAMECHANGED) { delegate_->HandleWindowSizeChanging(); sent_window_size_changing_ = true; + + // It's possible that if Aero snap is being entered then the window size + // won't actually change. Post a message to ensure swaps will be re-enabled + // in that case. + PostMessage(hwnd(), WM_WINDOWSIZINGFINISHED, ++current_window_size_message_, + 0); } if (ScopedFullscreenVisibility::IsHiddenForFullscreen(hwnd())) { @@ -2394,13 +2333,24 @@ void HWNDMessageHandler::OnWindowPosChanged(WINDOWPOS* window_pos) { if (direct_manipulation_helper_) direct_manipulation_helper_->Deactivate(hwnd()); } - if (sent_window_size_changing_) { - sent_window_size_changing_ = false; - delegate_->HandleWindowSizeChanged(); - } + SetMsgHandled(FALSE); } +LRESULT HWNDMessageHandler::OnWindowSizingFinished(UINT message, + WPARAM w_param, + LPARAM l_param) { + // Check if a newer WM_WINDOWPOSCHANGING or WM_WINDOWPOSCHANGED have been + // received after this message was posted. + if (current_window_size_message_ != w_param) + return 0; + + delegate_->HandleWindowSizeUnchanged(); + sent_window_size_changing_ = false; + + return 0; +} + void HWNDMessageHandler::OnSessionChange(WPARAM status_code) { // Direct3D presents are ignored while the screen is locked, so force the // window to be redrawn on unlock. @@ -2430,15 +2380,15 @@ LRESULT HWNDMessageHandler::HandleMouseEventInternal(UINT message, // TODO(ananta) // Windows does not reliably set the touch flag on mouse messages. Look into // a better way of identifying mouse messages originating from touch. - if (ui::IsMouseEventFromTouch(message)) { + if ((message != WM_MOUSEWHEEL && message != WM_MOUSEHWHEEL) && + (ui::IsMouseEventFromTouch(message))) { LPARAM l_param_ht = l_param; // For mouse events (except wheel events), location is in window coordinates // and should be converted to screen coordinates for WM_NCHITTEST. - if (message != WM_MOUSEWHEEL && message != WM_MOUSEHWHEEL) { - POINT screen_point = CR_POINT_INITIALIZER_FROM_LPARAM(l_param_ht); - MapWindowPoints(hwnd(), HWND_DESKTOP, &screen_point, 1); - l_param_ht = MAKELPARAM(screen_point.x, screen_point.y); - } + POINT screen_point = CR_POINT_INITIALIZER_FROM_LPARAM(l_param_ht); + MapWindowPoints(hwnd(), HWND_DESKTOP, &screen_point, 1); + l_param_ht = MAKELPARAM(screen_point.x, screen_point.y); + LRESULT hittest = SendMessage(hwnd(), WM_NCHITTEST, 0, l_param_ht); if (hittest == HTCLIENT || hittest == HTNOWHERE) return 0; @@ -2594,7 +2544,7 @@ void HWNDMessageHandler::PerformDwmTransition() { // The non-client view needs to update too. delegate_->HandleFrameChanged(); - if (IsVisible() && delegate_->GetFrameMode() == FrameMode::SYSTEM_DRAWN) { + if (IsVisible() && IsFrameSystemDrawn()) { // For some reason, we need to hide the window after we change from a custom // frame to a native frame. If we don't, the client area will be filled // with black. This seems to be related to an interaction between DWM and @@ -2705,8 +2655,13 @@ bool HWNDMessageHandler::HandleMouseInputForCaption(unsigned int message, break; } - case WM_NCMOUSELEAVE: + case WM_NCMOUSELEAVE: { + // If the DWM is rendering the window controls, we need to give the DWM's + // default window procedure the chance to repaint the window border icons + if (HasSystemFrame()) + handled = DwmDefWindowProc(hwnd(), WM_NCMOUSELEAVE, 0, 0, NULL) != 0; break; + } default: left_button_down_on_caption_ = false; diff --git a/chromium/ui/views/win/hwnd_message_handler.h b/chromium/ui/views/win/hwnd_message_handler.h index a04bfa780b2..9b2ad1d08a2 100644 --- a/chromium/ui/views/win/hwnd_message_handler.h +++ b/chromium/ui/views/win/hwnd_message_handler.h @@ -59,6 +59,11 @@ class WindowsSessionChangeObserver; const int WM_NCUAHDRAWCAPTION = 0xAE; const int WM_NCUAHDRAWFRAME = 0xAF; +// The HWNDMessageHandler sends this message to itself on +// WM_WINDOWPOSCHANGING. It's used to inform the client if a +// WM_WINDOWPOSCHANGED won't be received. +const int WM_WINDOWSIZINGFINISHED = WM_USER; + // IsMsgHandled() and BEGIN_SAFE_MSG_MAP_EX are a modified version of // BEGIN_MSG_MAP_EX. The main difference is it uses a WeakPtrFactory member // (|weak_factory|) that is used in _ProcessWindowMessage() and changing @@ -313,6 +318,11 @@ class VIEWS_EXPORT HWNDMessageHandler : // onscreen. void ForceRedrawWindow(int attempts); + // Returns whether Windows should help with frame rendering (i.e. we're using + // the glass frame). + bool IsFrameSystemDrawn() const; + + // Returns true if IsFrameSystemDrawn() and there's actually a frame to draw. bool HasSystemFrame() const; // Adds or removes the frame extension into client area with @@ -373,6 +383,8 @@ class VIEWS_EXPORT HWNDMessageHandler : // Touch Events. CR_MESSAGE_HANDLER_EX(WM_TOUCH, OnTouchEvent) + CR_MESSAGE_HANDLER_EX(WM_WINDOWSIZINGFINISHED, OnWindowSizingFinished) + // Uses the general handler macro since the specific handler macro // MSG_WM_NCACTIVATE would convert WPARAM type to BOOL type. The high // word of WPARAM could be set when the window is minimized or restored. @@ -470,6 +482,7 @@ class VIEWS_EXPORT HWNDMessageHandler : LRESULT OnTouchEvent(UINT message, WPARAM w_param, LPARAM l_param); void OnWindowPosChanging(WINDOWPOS* window_pos); void OnWindowPosChanged(WINDOWPOS* window_pos); + LRESULT OnWindowSizingFinished(UINT message, WPARAM w_param, LPARAM l_param); // Receives Windows Session Change notifications. void OnSessionChange(WPARAM status_code); @@ -562,6 +575,9 @@ class VIEWS_EXPORT HWNDMessageHandler : // The icon created from the bitmap image of the app icon. base::win::ScopedHICON app_icon_; + // The current DPI. + int dpi_; + // Event handling ------------------------------------------------------------ // The flags currently being used with TrackMouseEvent to track mouse @@ -606,9 +622,6 @@ class VIEWS_EXPORT HWNDMessageHandler : // Generates touch-ids for touch-events. ui::SequentialIDGenerator id_generator_; - // Indicates if the window needs the WS_VSCROLL and WS_HSCROLL styles. - bool needs_scroll_styles_; - // Set to true if we are in the context of a sizing operation. bool in_size_loop_; @@ -642,9 +655,13 @@ class VIEWS_EXPORT HWNDMessageHandler : bool dwm_transition_desired_; // True if HandleWindowSizeChanging has been called in the delegate, but not - // HandleWindowSizeChanged. + // HandleClientSizeChanged. bool sent_window_size_changing_; + // This is used to keep track of whether a WM_WINDOWPOSCHANGED has + // been received after the WM_WINDOWPOSCHANGING. + uint32_t current_window_size_message_ = 0; + // Manages observation of Windows Session Change messages. std::unique_ptr<WindowsSessionChangeObserver> windows_session_change_observer_; diff --git a/chromium/ui/views/win/hwnd_message_handler_delegate.h b/chromium/ui/views/win/hwnd_message_handler_delegate.h index f9e027d6faa..14021805543 100644 --- a/chromium/ui/views/win/hwnd_message_handler_delegate.h +++ b/chromium/ui/views/win/hwnd_message_handler_delegate.h @@ -25,8 +25,9 @@ class TouchEvent; namespace views { enum class FrameMode { - SYSTEM_DRAWN, // "glass" frame - CUSTOM_DRAWN // "opaque" frame + SYSTEM_DRAWN, // "glass" frame + SYSTEM_DRAWN_NO_CONTROLS, // "glass" frame but with custom window controls + CUSTOM_DRAWN // "opaque" frame }; class InputMethod; @@ -46,6 +47,7 @@ class VIEWS_EXPORT HWNDMessageHandlerDelegate { // that don't have a visible frame. Those usually have the WS_POPUP style, for // which Windows will remove the frame automatically if the frame mode is // SYSTEM_DRAWN. + // TODO(bsep): Investigate deleting this when v2 Apps support is removed. virtual bool HasFrame() const = 0; virtual void SchedulePaint() = 0; @@ -239,8 +241,9 @@ class VIEWS_EXPORT HWNDMessageHandlerDelegate { // Called when the window size is about to change. virtual void HandleWindowSizeChanging() = 0; - // Called when the window size has finished changing. - virtual void HandleWindowSizeChanged() = 0; + // Called after HandleWindowSizeChanging() when it's determined the window + // size didn't actually change. + virtual void HandleWindowSizeUnchanged() = 0; // Called when the window scale factor has changed. virtual void HandleWindowScaleFactorChanged(float window_scale_factor) = 0; diff --git a/chromium/ui/views/window/custom_frame_view.cc b/chromium/ui/views/window/custom_frame_view.cc index 720091ec624..fe392518a1e 100644 --- a/chromium/ui/views/window/custom_frame_view.cc +++ b/chromium/ui/views/window/custom_frame_view.cc @@ -31,6 +31,10 @@ #include "ui/views/window/window_resources.h" #include "ui/views/window/window_shape.h" +#if defined(OS_WIN) +#include "ui/display/win/screen_win.h" +#endif + namespace views { namespace { @@ -53,11 +57,6 @@ const int kTitleIconOffsetX = 4; // The space between the title text and the caption buttons. const int kTitleCaptionSpacing = 5; -#if !defined(OS_WIN) -// The icon never shrinks below 16 px on a side. -const int kIconMinimumSize = 16; -#endif - #if defined(OS_CHROMEOS) // Chrome OS uses a dark gray. const SkColor kDefaultColorFrame = SkColorSetRGB(109, 109, 109); @@ -304,8 +303,10 @@ int CustomFrameView::IconSize() const { #if defined(OS_WIN) // This metric scales up if either the titlebar height or the titlebar font // size are increased. - return GetSystemMetrics(SM_CYSMICON); + return display::win::ScreenWin::GetSystemMetricsInDIP(SM_CYSMICON); #else + // The icon never shrinks below 16 px on a side. + const int kIconMinimumSize = 16; return std::max(GetTitleFontList().GetHeight(), kIconMinimumSize); #endif } diff --git a/chromium/ui/views/window/dialog_client_view.cc b/chromium/ui/views/window/dialog_client_view.cc index 55bf0047508..ce9099fb393 100644 --- a/chromium/ui/views/window/dialog_client_view.cc +++ b/chromium/ui/views/window/dialog_client_view.cc @@ -11,6 +11,7 @@ #include "ui/events/keycodes/keyboard_codes.h" #include "ui/views/background.h" #include "ui/views/controls/button/blue_button.h" +#include "ui/views/controls/button/custom_button.h" #include "ui/views/controls/button/label_button.h" #include "ui/views/controls/button/md_text_button.h" #include "ui/views/layout/layout_constants.h" @@ -38,14 +39,19 @@ bool ShouldShow(View* view) { } // Do the layout for a button. -void LayoutButton(LabelButton* button, gfx::Rect* row_bounds) { +void LayoutButton(LabelButton* button, + gfx::Rect* row_bounds, + int button_height) { if (!button) return; const gfx::Size size = button->GetPreferredSize(); row_bounds->set_width(row_bounds->width() - size.width()); - button->SetBounds(row_bounds->right(), row_bounds->y(), - size.width(), row_bounds->height()); + DCHECK_LE(button_height, row_bounds->height()); + button->SetBounds( + row_bounds->right(), + row_bounds->y() + (row_bounds->height() - button_height) / 2, + size.width(), button_height); row_bounds->set_width(row_bounds->width() - kRelatedButtonHSpacing); } @@ -180,12 +186,17 @@ void DialogClientView::Layout() { const int height = GetButtonsAndExtraViewRowHeight(); gfx::Rect row_bounds(bounds.x(), bounds.bottom() - height, bounds.width(), height); + // If the |extra_view_| is a also button, then the |button_height| is the + // maximum height of the three buttons, otherwise it is the maximum height + // of the ok and cancel buttons. + const int button_height = + CustomButton::AsCustomButton(extra_view_) ? height : GetButtonHeight(); if (kIsOkButtonOnLeftSide) { - LayoutButton(cancel_button_, &row_bounds); - LayoutButton(ok_button_, &row_bounds); + LayoutButton(cancel_button_, &row_bounds, button_height); + LayoutButton(ok_button_, &row_bounds, button_height); } else { - LayoutButton(ok_button_, &row_bounds); - LayoutButton(cancel_button_, &row_bounds); + LayoutButton(ok_button_, &row_bounds, button_height); + LayoutButton(cancel_button_, &row_bounds, button_height); } if (extra_view_) { int custom_padding = 0; @@ -303,9 +314,11 @@ void DialogClientView::ChildVisibilityChanged(View* child) { LabelButton* DialogClientView::CreateDialogButton(ui::DialogButton type) { const base::string16 title = GetDialogDelegate()->GetDialogButtonLabel(type); LabelButton* button = nullptr; + // The default button is always blue in Harmony. if (GetDialogDelegate()->GetDefaultDialogButton() == type && - GetDialogDelegate()->ShouldDefaultButtonBeBlue()) { - return MdTextButton::CreateSecondaryUiBlueButton(this, title); + (ui::MaterialDesignController::IsSecondaryUiMaterial() || + GetDialogDelegate()->ShouldDefaultButtonBeBlue())) { + button = MdTextButton::CreateSecondaryUiBlueButton(this, title); } else { button = MdTextButton::CreateSecondaryUiButton(this, title); } @@ -316,13 +329,18 @@ LabelButton* DialogClientView::CreateDialogButton(ui::DialogButton type) { return button; } -int DialogClientView::GetButtonsAndExtraViewRowHeight() const { - int extra_view_height = ShouldShow(extra_view_) ? - extra_view_->GetPreferredSize().height() : 0; - int buttons_height = std::max( +int DialogClientView::GetButtonHeight() const { + return std::max( ok_button_ ? ok_button_->GetPreferredSize().height() : 0, cancel_button_ ? cancel_button_->GetPreferredSize().height() : 0); - return std::max(extra_view_height, buttons_height); +} + +int DialogClientView::GetExtraViewHeight() const { + return ShouldShow(extra_view_) ? extra_view_->GetPreferredSize().height() : 0; +} + +int DialogClientView::GetButtonsAndExtraViewRowHeight() const { + return std::max(GetExtraViewHeight(), GetButtonHeight()); } gfx::Insets DialogClientView::GetButtonRowInsets() const { diff --git a/chromium/ui/views/window/dialog_client_view.h b/chromium/ui/views/window/dialog_client_view.h index 60080b00c8c..3fb88c08b25 100644 --- a/chromium/ui/views/window/dialog_client_view.h +++ b/chromium/ui/views/window/dialog_client_view.h @@ -87,6 +87,12 @@ class VIEWS_EXPORT DialogClientView : public ClientView, // Update |button|'s text and enabled state according to the delegate's state. void UpdateButton(LabelButton* button, ui::DialogButton type); + // Returns the height of the buttons. + int GetButtonHeight() const; + + // Returns the height of the extra view. + int GetExtraViewHeight() const; + // Returns the height of the row containing the buttons and the extra view. int GetButtonsAndExtraViewRowHeight() const; diff --git a/chromium/ui/views/window/dialog_client_view_unittest.cc b/chromium/ui/views/window/dialog_client_view_unittest.cc index c483d254af3..dfe45b56237 100644 --- a/chromium/ui/views/window/dialog_client_view_unittest.cc +++ b/chromium/ui/views/window/dialog_client_view_unittest.cc @@ -7,6 +7,7 @@ #include "build/build_config.h" #include "ui/base/ui_base_types.h" #include "ui/views/controls/button/label_button.h" +#include "ui/views/style/platform_style.h" #include "ui/views/test/test_views.h" #include "ui/views/test/views_test_base.h" #include "ui/views/widget/widget.h" @@ -148,7 +149,8 @@ TEST_F(DialogClientViewTest, UpdateButtons) { // Reset with just a cancel button. SetDialogButtons(ui::DIALOG_BUTTON_CANCEL); EXPECT_EQ(NULL, client_view()->ok_button()); - EXPECT_TRUE(client_view()->cancel_button()->is_default()); + EXPECT_EQ(client_view()->cancel_button()->is_default(), + PlatformStyle::kDialogDefaultButtonCanBeCancel); EXPECT_EQ(GetUpdatedClientBounds().height(), height_with_buttons); } diff --git a/chromium/ui/views/window/dialog_delegate.cc b/chromium/ui/views/window/dialog_delegate.cc index 4b8a1f98900..d98274cde71 100644 --- a/chromium/ui/views/window/dialog_delegate.cc +++ b/chromium/ui/views/window/dialog_delegate.cc @@ -16,6 +16,7 @@ #include "ui/views/bubble/bubble_frame_view.h" #include "ui/views/controls/button/label_button.h" #include "ui/views/layout/layout_constants.h" +#include "ui/views/style/platform_style.h" #include "ui/views/widget/widget.h" #include "ui/views/widget/widget_observer.h" #include "ui/views/window/dialog_client_view.h" @@ -119,7 +120,12 @@ bool DialogDelegate::Close() { void DialogDelegate::UpdateButton(LabelButton* button, ui::DialogButton type) { button->SetText(GetDialogButtonLabel(type)); button->SetEnabled(IsDialogButtonEnabled(type)); - button->SetIsDefault(type == GetDefaultDialogButton()); + bool is_default = type == GetDefaultDialogButton(); + if (!PlatformStyle::kDialogDefaultButtonCanBeCancel && + type == ui::DIALOG_BUTTON_CANCEL) { + is_default = false; + } + button->SetIsDefault(is_default); } int DialogDelegate::GetDialogButtons() const { diff --git a/chromium/ui/views/window/dialog_delegate.h b/chromium/ui/views/window/dialog_delegate.h index c74f5461ab9..8e122178640 100644 --- a/chromium/ui/views/window/dialog_delegate.h +++ b/chromium/ui/views/window/dialog_delegate.h @@ -63,7 +63,7 @@ class VIEWS_EXPORT DialogDelegate : public ui::DialogModel, virtual View* CreateFootnoteView(); // For Dialog boxes, if there is a "Cancel" button or no dialog button at all, - // this is called when the user presses the "Cancel" button or the Esc key. + // this is called when the user presses the "Cancel" button. // It can also be called on a close action if |Close| has not been // overridden. This function should return true if the window can be closed // after it returns, or false if it must remain open. @@ -76,10 +76,11 @@ class VIEWS_EXPORT DialogDelegate : public ui::DialogModel, virtual bool Accept(); // Called when the user closes the window without selecting an option, - // e.g. by pressing the close button on the window or using a window manager - // gesture. By default, this calls Accept() if the only button in the dialog - // is Accept, Cancel() otherwise. This function should return true if the - // window can be closed after it returns, or false if it must remain open. + // e.g. by pressing the close button on the window, pressing the Esc key, or + // using a window manager gesture. By default, this calls Accept() if the only + // button in the dialog is Accept, Cancel() otherwise. This function should + // return true if the window can be closed after it returns, or false if it + // must remain open. virtual bool Close(); // Updates the properties and appearance of |button| which has been created diff --git a/chromium/ui/views/word_lookup_client.h b/chromium/ui/views/word_lookup_client.h new file mode 100644 index 00000000000..d9c200f5566 --- /dev/null +++ b/chromium/ui/views/word_lookup_client.h @@ -0,0 +1,35 @@ +// Copyright 2016 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_WORD_LOOKUP_CLIENT_H_ +#define UI_VIEWS_WORD_LOOKUP_CLIENT_H_ + +#include "ui/views/views_export.h" + +namespace gfx { +struct DecoratedText; +class Point; +} + +namespace views { + +// An interface implemented by a view which supports word lookups. +class VIEWS_EXPORT WordLookupClient { + public: + // Retrieves the word displayed at the given |point| along with its styling + // information. |point| is in the coordinate system of the view. If no word is + // displayed at the point, returns a nearby word. |baseline_point| should + // correspond to the baseline point of the leftmost glyph of the |word| in the + // view's coordinates. Returns false, if no word can be retrieved. + virtual bool GetDecoratedWordAtPoint(const gfx::Point& point, + gfx::DecoratedText* decorated_word, + gfx::Point* baseline_point) = 0; + + protected: + virtual ~WordLookupClient() {} +}; + +} // namespace views + +#endif // UI_VIEWS_WORD_LOOKUP_CLIENT_H_ |