summaryrefslogtreecommitdiff
path: root/chromium/ui/views
diff options
context:
space:
mode:
authorAllan Sandfeld Jensen <allan.jensen@qt.io>2018-08-24 12:15:48 +0200
committerAllan Sandfeld Jensen <allan.jensen@qt.io>2018-08-28 13:30:04 +0000
commitb014812705fc80bff0a5c120dfcef88f349816dc (patch)
tree25a2e2d9fa285f1add86aa333389a839f81a39ae /chromium/ui/views
parent9f4560b1027ae06fdb497023cdcaf91b8511fa74 (diff)
downloadqtwebengine-chromium-b014812705fc80bff0a5c120dfcef88f349816dc.tar.gz
BASELINE: Update Chromium to 68.0.3440.125
Change-Id: I23f19369e01f688e496f5bf179abb521ad73874f Reviewed-by: Allan Sandfeld Jensen <allan.jensen@qt.io>
Diffstat (limited to 'chromium/ui/views')
-rw-r--r--chromium/ui/views/BUILD.gn22
-rw-r--r--chromium/ui/views/OWNERS5
-rw-r--r--chromium/ui/views/accessibility/ax_system_caret_win_interactive_uitest.cc2
-rw-r--r--chromium/ui/views/accessibility/native_view_accessibility_auralinux.cc20
-rw-r--r--chromium/ui/views/accessibility/native_view_accessibility_base.cc36
-rw-r--r--chromium/ui/views/accessibility/native_view_accessibility_base.h7
-rw-r--r--chromium/ui/views/accessible_pane_view.cc26
-rw-r--r--chromium/ui/views/animation/ink_drop_host_view.cc27
-rw-r--r--chromium/ui/views/animation/ink_drop_host_view.h18
-rw-r--r--chromium/ui/views/bubble/bubble_border.cc14
-rw-r--r--chromium/ui/views/bubble/bubble_border.h13
-rw-r--r--chromium/ui/views/bubble/bubble_dialog_delegate.cc58
-rw-r--r--chromium/ui/views/bubble/bubble_dialog_delegate.h17
-rw-r--r--chromium/ui/views/bubble/bubble_frame_view.cc2
-rw-r--r--chromium/ui/views/bubble/bubble_frame_view_unittest.cc1
-rw-r--r--chromium/ui/views/bubble/tray_bubble_view.cc7
-rw-r--r--chromium/ui/views/bubble/tray_bubble_view.h1
-rw-r--r--chromium/ui/views/cocoa/bridged_content_view.h10
-rw-r--r--chromium/ui/views/cocoa/bridged_content_view.mm96
-rw-r--r--chromium/ui/views/cocoa/bridged_native_widget.h6
-rw-r--r--chromium/ui/views/cocoa/bridged_native_widget.mm49
-rw-r--r--chromium/ui/views/controls/button/button.cc10
-rw-r--r--chromium/ui/views/controls/button/button.h5
-rw-r--r--chromium/ui/views/controls/button/checkbox.cc71
-rw-r--r--chromium/ui/views/controls/button/checkbox.h16
-rw-r--r--chromium/ui/views/controls/button/image_button.cc32
-rw-r--r--chromium/ui/views/controls/button/image_button.h14
-rw-r--r--chromium/ui/views/controls/button/image_button_factory_unittest.cc4
-rw-r--r--chromium/ui/views/controls/button/image_button_unittest.cc42
-rw-r--r--chromium/ui/views/controls/button/label_button.cc2
-rw-r--r--chromium/ui/views/controls/button/md_text_button.cc26
-rw-r--r--chromium/ui/views/controls/button/md_text_button.h5
-rw-r--r--chromium/ui/views/controls/button/menu_button.cc5
-rw-r--r--chromium/ui/views/controls/button/radio_button.cc13
-rw-r--r--chromium/ui/views/controls/button/radio_button.h5
-rw-r--r--chromium/ui/views/controls/combobox/combobox.cc18
-rw-r--r--chromium/ui/views/controls/combobox/combobox.h9
-rw-r--r--chromium/ui/views/controls/focus_ring.cc164
-rw-r--r--chromium/ui/views/controls/focus_ring.h94
-rw-r--r--chromium/ui/views/controls/glow_hover_controller.cc88
-rw-r--r--chromium/ui/views/controls/glow_hover_controller.h86
-rw-r--r--chromium/ui/views/controls/image_view.cc62
-rw-r--r--chromium/ui/views/controls/image_view.h4
-rw-r--r--chromium/ui/views/controls/menu/menu_config.cc47
-rw-r--r--chromium/ui/views/controls/menu/menu_config.h59
-rw-r--r--chromium/ui/views/controls/menu/menu_config_mac.mm13
-rw-r--r--chromium/ui/views/controls/menu/menu_controller.cc358
-rw-r--r--chromium/ui/views/controls/menu/menu_controller.h38
-rw-r--r--chromium/ui/views/controls/menu/menu_controller_unittest.cc119
-rw-r--r--chromium/ui/views/controls/menu/menu_host.cc4
-rw-r--r--chromium/ui/views/controls/menu/menu_item_view.cc152
-rw-r--r--chromium/ui/views/controls/menu/menu_item_view.h46
-rw-r--r--chromium/ui/views/controls/menu/menu_item_view_unittest.cc27
-rw-r--r--chromium/ui/views/controls/menu/menu_model_adapter.cc9
-rw-r--r--chromium/ui/views/controls/menu/menu_model_adapter_unittest.cc131
-rw-r--r--chromium/ui/views/controls/menu/menu_runner_cocoa_unittest.mm118
-rw-r--r--chromium/ui/views/controls/menu/menu_runner_impl.cc10
-rw-r--r--chromium/ui/views/controls/menu/menu_runner_impl_cocoa.h5
-rw-r--r--chromium/ui/views/controls/menu/menu_runner_impl_cocoa.mm7
-rw-r--r--chromium/ui/views/controls/menu/menu_runner_unittest.cc5
-rw-r--r--chromium/ui/views/controls/menu/menu_scroll_view_container.cc21
-rw-r--r--chromium/ui/views/controls/menu/menu_separator.cc14
-rw-r--r--chromium/ui/views/controls/menu/menu_types.h3
-rw-r--r--chromium/ui/views/controls/menu/submenu_view.cc3
-rw-r--r--chromium/ui/views/controls/message_box_view.cc1
-rw-r--r--chromium/ui/views/controls/native/native_view_host_aura.cc3
-rw-r--r--chromium/ui/views/controls/native/native_view_host_mac.mm15
-rw-r--r--chromium/ui/views/controls/prefix_selector.cc12
-rw-r--r--chromium/ui/views/controls/prefix_selector.h2
-rw-r--r--chromium/ui/views/controls/scroll_view.cc76
-rw-r--r--chromium/ui/views/controls/scroll_view.h7
-rw-r--r--chromium/ui/views/controls/scroll_view_unittest.cc263
-rw-r--r--chromium/ui/views/controls/scrollbar/base_scroll_bar.cc1
-rw-r--r--chromium/ui/views/controls/slider.cc4
-rw-r--r--chromium/ui/views/controls/styled_label.cc6
-rw-r--r--chromium/ui/views/controls/styled_label.h2
-rw-r--r--chromium/ui/views/controls/styled_label_unittest.cc74
-rw-r--r--chromium/ui/views/controls/tabbed_pane/tabbed_pane.cc8
-rw-r--r--chromium/ui/views/controls/tabbed_pane/tabbed_pane_unittest.cc1
-rw-r--r--chromium/ui/views/controls/table/table_view.cc24
-rw-r--r--chromium/ui/views/controls/textfield/textfield.cc115
-rw-r--r--chromium/ui/views/controls/textfield/textfield.h17
-rw-r--r--chromium/ui/views/controls/textfield/textfield_model.cc91
-rw-r--r--chromium/ui/views/controls/textfield/textfield_model.h8
-rw-r--r--chromium/ui/views/controls/textfield/textfield_model_unittest.cc55
-rw-r--r--chromium/ui/views/controls/textfield/textfield_unittest.cc192
-rw-r--r--chromium/ui/views/controls/tree/tree_view.cc13
-rw-r--r--chromium/ui/views/controls/views_text_services_context_menu.cc5
-rw-r--r--chromium/ui/views/controls/views_text_services_context_menu_base.cc53
-rw-r--r--chromium/ui/views/controls/views_text_services_context_menu_base.h41
-rw-r--r--chromium/ui/views/controls/views_text_services_context_menu_mac.mm62
-rw-r--r--chromium/ui/views/controls/webview/web_dialog_view.cc23
-rw-r--r--chromium/ui/views/controls/webview/web_dialog_view.h2
-rw-r--r--chromium/ui/views/controls/webview/webview.cc37
-rw-r--r--chromium/ui/views/controls/webview/webview.h17
-rw-r--r--chromium/ui/views/controls/webview/webview_unittest.cc44
-rw-r--r--chromium/ui/views/debug_utils.cc97
-rw-r--r--chromium/ui/views/debug_utils.h9
-rw-r--r--chromium/ui/views/examples/button_sticker_sheet.cc5
-rw-r--r--chromium/ui/views/examples/dialog_example.cc2
-rw-r--r--chromium/ui/views/examples/examples_main.cc2
-rw-r--r--chromium/ui/views/examples/textfield_example.cc7
-rw-r--r--chromium/ui/views/examples/textfield_example.h1
-rw-r--r--chromium/ui/views/focus/focus_manager.cc77
-rw-r--r--chromium/ui/views/focus/focus_manager_delegate.h5
-rw-r--r--chromium/ui/views/focus/focus_manager_unittest.cc134
-rw-r--r--chromium/ui/views/focus/focus_search.cc178
-rw-r--r--chromium/ui/views/focus/focus_search.h67
-rw-r--r--chromium/ui/views/layout/layout_provider.cc23
-rw-r--r--chromium/ui/views/layout/layout_provider.h25
-rw-r--r--chromium/ui/views/linux_ui/linux_ui.h3
-rw-r--r--chromium/ui/views/mus/BUILD.gn3
-rw-r--r--chromium/ui/views/mus/DEPS2
-rw-r--r--chromium/ui/views/mus/aura_init.cc23
-rw-r--r--chromium/ui/views/mus/aura_init.h12
-rw-r--r--chromium/ui/views/mus/clipboard_mus.h3
-rw-r--r--chromium/ui/views/mus/clipboard_unittest.cc1
-rw-r--r--chromium/ui/views/mus/desktop_window_tree_host_mus.cc19
-rw-r--r--chromium/ui/views/mus/desktop_window_tree_host_mus.h3
-rw-r--r--chromium/ui/views/mus/desktop_window_tree_host_mus_unittest.cc30
-rw-r--r--chromium/ui/views/mus/mus_client.cc64
-rw-r--r--chromium/ui/views/mus/mus_client.h61
-rw-r--r--chromium/ui/views/mus/pointer_watcher_event_router.cc19
-rw-r--r--chromium/ui/views/mus/pointer_watcher_event_router.h4
-rw-r--r--chromium/ui/views/mus/pointer_watcher_event_router_unittest.cc63
-rw-r--r--chromium/ui/views/mus/screen_mus.cc14
-rw-r--r--chromium/ui/views/mus/screen_mus.h14
-rw-r--r--chromium/ui/views/mus/screen_mus_delegate.h2
-rw-r--r--chromium/ui/views/mus/test_utils.h31
-rw-r--r--chromium/ui/views/mus/views_mus_test_suite.cc8
-rw-r--r--chromium/ui/views/style/platform_style.cc4
-rw-r--r--chromium/ui/views/style/platform_style.h9
-rw-r--r--chromium/ui/views/style/platform_style_mac.mm3
-rw-r--r--chromium/ui/views/style/typography_provider.cc11
-rw-r--r--chromium/ui/views/view.cc131
-rw-r--r--chromium/ui/views/view.h21
-rw-r--r--chromium/ui/views/view_observer.h6
-rw-r--r--chromium/ui/views/view_properties.cc7
-rw-r--r--chromium/ui/views/view_properties.h9
-rw-r--r--chromium/ui/views/views_delegate.cc6
-rw-r--r--chromium/ui/views/views_delegate.h11
-rw-r--r--chromium/ui/views/views_test_suite.cc2
-rw-r--r--chromium/ui/views/widget/desktop_aura/desktop_drag_drop_client_aurax11.cc1
-rw-r--r--chromium/ui/views/widget/desktop_aura/desktop_native_widget_aura.cc17
-rw-r--r--chromium/ui/views/widget/desktop_aura/desktop_native_widget_aura.h1
-rw-r--r--chromium/ui/views/widget/desktop_aura/desktop_native_widget_aura_unittest.cc61
-rw-r--r--chromium/ui/views/widget/desktop_aura/desktop_screen.cc23
-rw-r--r--chromium/ui/views/widget/desktop_aura/desktop_screen.h2
-rw-r--r--chromium/ui/views/widget/desktop_aura/desktop_screen_ozone.cc61
-rw-r--r--chromium/ui/views/widget/desktop_aura/desktop_screen_ozone.h39
-rw-r--r--chromium/ui/views/widget/desktop_aura/desktop_screen_x11.cc16
-rw-r--r--chromium/ui/views/widget/desktop_aura/desktop_window_tree_host_ozone.cc16
-rw-r--r--chromium/ui/views/widget/desktop_aura/desktop_window_tree_host_platform.cc77
-rw-r--r--chromium/ui/views/widget/desktop_aura/desktop_window_tree_host_platform.h4
-rw-r--r--chromium/ui/views/widget/desktop_aura/desktop_window_tree_host_win.cc25
-rw-r--r--chromium/ui/views/widget/desktop_aura/desktop_window_tree_host_win.h10
-rw-r--r--chromium/ui/views/widget/desktop_aura/desktop_window_tree_host_x11.cc118
-rw-r--r--chromium/ui/views/widget/desktop_aura/desktop_window_tree_host_x11.h22
-rw-r--r--chromium/ui/views/widget/desktop_aura/window_event_filter.h1
-rw-r--r--chromium/ui/views/widget/desktop_aura/x11_desktop_handler.cc1
-rw-r--r--chromium/ui/views/widget/desktop_aura/x11_desktop_window_move_client.cc1
-rw-r--r--chromium/ui/views/widget/desktop_aura/x11_desktop_window_move_client.h1
-rw-r--r--chromium/ui/views/widget/desktop_aura/x11_window_event_filter.h1
-rw-r--r--chromium/ui/views/widget/desktop_widget_unittest.cc100
-rw-r--r--chromium/ui/views/widget/native_widget_aura.cc20
-rw-r--r--chromium/ui/views/widget/native_widget_aura.h1
-rw-r--r--chromium/ui/views/widget/native_widget_mac.h15
-rw-r--r--chromium/ui/views/widget/native_widget_mac.mm40
-rw-r--r--chromium/ui/views/widget/native_widget_mac_unittest.mm5
-rw-r--r--chromium/ui/views/widget/native_widget_private.cc25
-rw-r--r--chromium/ui/views/widget/native_widget_private.h5
-rw-r--r--chromium/ui/views/widget/root_view.cc1
-rw-r--r--chromium/ui/views/widget/widget.cc17
-rw-r--r--chromium/ui/views/widget/widget.h5
-rw-r--r--chromium/ui/views/widget/widget_interactive_uitest.cc2
-rw-r--r--chromium/ui/views/widget/widget_unittest.cc5
-rw-r--r--chromium/ui/views/widget/widget_utils_mac.h16
-rw-r--r--chromium/ui/views/widget/widget_utils_mac.mm17
-rw-r--r--chromium/ui/views/win/hwnd_message_handler.cc152
-rw-r--r--chromium/ui/views/win/hwnd_message_handler.h64
-rw-r--r--chromium/ui/views/win/pen_event_processor.cc15
-rw-r--r--chromium/ui/views/win/pen_event_processor_unittest.cc63
-rw-r--r--chromium/ui/views/window/dialog_delegate.cc10
-rw-r--r--chromium/ui/views/window/dialog_delegate.h3
184 files changed, 4037 insertions, 1948 deletions
diff --git a/chromium/ui/views/BUILD.gn b/chromium/ui/views/BUILD.gn
index 32f534f12e3..c9ecb17ed42 100644
--- a/chromium/ui/views/BUILD.gn
+++ b/chromium/ui/views/BUILD.gn
@@ -115,7 +115,6 @@ jumbo_component("views") {
"controls/combobox/combobox_listener.h",
"controls/focus_ring.h",
"controls/focusable_border.h",
- "controls/glow_hover_controller.h",
"controls/image_view.h",
"controls/label.h",
"controls/link.h",
@@ -245,6 +244,7 @@ jumbo_component("views") {
"widget/widget_deletion_observer.h",
"widget/widget_observer.h",
"widget/widget_removals_observer.h",
+ "widget/widget_utils_mac.h",
"window/client_view.h",
"window/custom_frame_view.h",
"window/dialog_client_view.h",
@@ -313,7 +313,6 @@ jumbo_component("views") {
"controls/combobox/combobox.cc",
"controls/focus_ring.cc",
"controls/focusable_border.cc",
- "controls/glow_hover_controller.cc",
"controls/image_view.cc",
"controls/label.cc",
"controls/link.cc",
@@ -369,6 +368,8 @@ jumbo_component("views") {
"controls/tree/tree_view_controller.cc",
"controls/tree/tree_view_drawing_provider.cc",
"controls/views_text_services_context_menu.cc",
+ "controls/views_text_services_context_menu_base.cc",
+ "controls/views_text_services_context_menu_base.h",
"controls/views_text_services_context_menu_mac.mm",
"debug_utils.cc",
"drag_utils.cc",
@@ -418,6 +419,7 @@ jumbo_component("views") {
"views_touch_selection_controller_factory_mac.cc",
"widget/drop_helper.cc",
"widget/native_widget_mac.mm",
+ "widget/native_widget_private.cc",
"widget/root_view.cc",
"widget/root_view_targeter.cc",
"widget/tooltip_manager.cc",
@@ -425,6 +427,7 @@ jumbo_component("views") {
"widget/widget_aura_utils.cc",
"widget/widget_delegate.cc",
"widget/widget_deletion_observer.cc",
+ "widget/widget_utils_mac.mm",
"window/client_view.cc",
"window/custom_frame_view.cc",
"window/dialog_client_view.cc",
@@ -629,6 +632,7 @@ jumbo_component("views") {
"widget/desktop_aura/desktop_focus_rules.cc",
"widget/desktop_aura/desktop_native_cursor_manager.cc",
"widget/desktop_aura/desktop_native_widget_aura.cc",
+ "widget/desktop_aura/desktop_screen.cc",
"widget/desktop_aura/desktop_screen_position_client.cc",
"widget/focus_manager_event_handler.cc",
"widget/native_widget_aura.cc",
@@ -686,7 +690,8 @@ jumbo_component("views") {
"widget/desktop_aura/desktop_window_tree_host_win.h",
]
} else if (use_ozone) {
- sources += [ "widget/desktop_aura/desktop_window_tree_host_ozone.cc" ]
+ public += [ "widget/desktop_aura/desktop_screen_ozone.h" ]
+ sources += [ "widget/desktop_aura/desktop_screen_ozone.cc" ]
}
if (is_linux) {
sources += [
@@ -694,15 +699,16 @@ jumbo_component("views") {
"widget/desktop_aura/window_event_filter.cc",
"widget/desktop_aura/window_event_filter.h",
]
+ if (!use_x11) {
+ public +=
+ [ "widget/desktop_aura/desktop_window_tree_host_platform.h" ]
+ sources +=
+ [ "widget/desktop_aura/desktop_window_tree_host_platform.cc" ]
+ }
}
}
}
- if (is_linux) {
- sources += [ "widget/desktop_aura/desktop_window_tree_host_platform.cc" ]
- public += [ "widget/desktop_aura/desktop_window_tree_host_platform.h" ]
- }
-
if (is_mac) {
sources -= [ "controls/views_text_services_context_menu.cc" ]
deps += [
diff --git a/chromium/ui/views/OWNERS b/chromium/ui/views/OWNERS
index c4ad8565841..4aca57c69ca 100644
--- a/chromium/ui/views/OWNERS
+++ b/chromium/ui/views/OWNERS
@@ -5,4 +5,9 @@ sadrul@chromium.org
sky@chromium.org
tapted@chromium.org
+# Prefer ellyjones@ for any changes to these files.
+per-file *_mac.*=ellyjones@chromium.org
+per-file *_cocoa.*=ellyjones@chromium.org
+per-file *.mm=ellyjones@chromium.org
+
# COMPONENT: Internals>Views
diff --git a/chromium/ui/views/accessibility/ax_system_caret_win_interactive_uitest.cc b/chromium/ui/views/accessibility/ax_system_caret_win_interactive_uitest.cc
index 27a8229d2f1..d3821559c0a 100644
--- a/chromium/ui/views/accessibility/ax_system_caret_win_interactive_uitest.cc
+++ b/chromium/ui/views/accessibility/ax_system_caret_win_interactive_uitest.cc
@@ -40,7 +40,7 @@ class AXSystemCaretWinTest : public test::WidgetTest {
gl::GLSurfaceTestSupport::InitializeOneOff();
ui::RegisterPathProvider();
base::FilePath ui_test_pak_path;
- ASSERT_TRUE(PathService::Get(ui::UI_TEST_PAK, &ui_test_pak_path));
+ ASSERT_TRUE(base::PathService::Get(ui::UI_TEST_PAK, &ui_test_pak_path));
ui::ResourceBundle::InitSharedInstanceWithPakPath(ui_test_pak_path);
test::WidgetTest::SetUp();
diff --git a/chromium/ui/views/accessibility/native_view_accessibility_auralinux.cc b/chromium/ui/views/accessibility/native_view_accessibility_auralinux.cc
index 1d155c6fa74..a098c1b5877 100644
--- a/chromium/ui/views/accessibility/native_view_accessibility_auralinux.cc
+++ b/chromium/ui/views/accessibility/native_view_accessibility_auralinux.cc
@@ -123,6 +123,26 @@ class AuraLinuxApplication
return gfx::kNullAcceleratedWidget;
}
+ int GetTableRowCount() const override { return 0; }
+
+ int GetTableColCount() const override { return 0; }
+
+ std::vector<int32_t> GetColHeaderNodeIds(int32_t col_index) const override {
+ return std::vector<int32_t>();
+ }
+
+ std::vector<int32_t> GetRowHeaderNodeIds(int32_t row_index) const override {
+ return std::vector<int32_t>();
+ }
+
+ int32_t GetCellId(int32_t row_index, int32_t col_index) const override {
+ return -1;
+ }
+
+ int32_t CellIdToIndex(int32_t cell_id) const override { return -1; }
+
+ int32_t CellIndexToId(int32_t cell_index) const override { return -1; }
+
bool AccessibilityPerformAction(const ui::AXActionData& data) override {
return false;
}
diff --git a/chromium/ui/views/accessibility/native_view_accessibility_base.cc b/chromium/ui/views/accessibility/native_view_accessibility_base.cc
index e895c9d3418..d22d3778b41 100644
--- a/chromium/ui/views/accessibility/native_view_accessibility_base.cc
+++ b/chromium/ui/views/accessibility/native_view_accessibility_base.cc
@@ -291,8 +291,9 @@ void NativeViewAccessibilityBase::OnAutofillShown() {
}
void NativeViewAccessibilityBase::OnAutofillHidden() {
- DCHECK(fake_focus_view_id_ == GetUniqueId().Get())
- << "Cannot clear fake focus on an object that did not have fake focus.";
+ DCHECK(fake_focus_view_id_) << "No autofill fake focus set.";
+ DCHECK_EQ(fake_focus_view_id_, GetUniqueId().Get())
+ << "Cannot clear autofill fake focus on an object that did not have it.";
fake_focus_view_id_ = 0;
ui::AXPlatformNode::OnAutofillHidden();
}
@@ -322,6 +323,37 @@ NativeViewAccessibilityBase::GetTargetForNativeAccessibilityEvent() {
return gfx::kNullAcceleratedWidget;
}
+int NativeViewAccessibilityBase::GetTableRowCount() const {
+ return 0;
+}
+
+int NativeViewAccessibilityBase::GetTableColCount() const {
+ return 0;
+}
+
+std::vector<int32_t> NativeViewAccessibilityBase::GetColHeaderNodeIds(
+ int32_t col_index) const {
+ return std::vector<int32_t>();
+}
+
+std::vector<int32_t> NativeViewAccessibilityBase::GetRowHeaderNodeIds(
+ int32_t row_index) const {
+ return std::vector<int32_t>();
+}
+
+int32_t NativeViewAccessibilityBase::GetCellId(int32_t row_index,
+ int32_t col_index) const {
+ return 0;
+}
+
+int32_t NativeViewAccessibilityBase::CellIdToIndex(int32_t cell_id) const {
+ return -1;
+}
+
+int32_t NativeViewAccessibilityBase::CellIndexToId(int32_t cell_index) const {
+ return 0;
+}
+
bool NativeViewAccessibilityBase::AccessibilityPerformAction(
const ui::AXActionData& data) {
return view()->HandleAccessibleAction(data);
diff --git a/chromium/ui/views/accessibility/native_view_accessibility_base.h b/chromium/ui/views/accessibility/native_view_accessibility_base.h
index cb3d15a8eec..11f0d7e778b 100644
--- a/chromium/ui/views/accessibility/native_view_accessibility_base.h
+++ b/chromium/ui/views/accessibility/native_view_accessibility_base.h
@@ -54,6 +54,13 @@ class VIEWS_EXPORT NativeViewAccessibilityBase
ui::AXPlatformNode* GetFromNodeID(int32_t id) override;
int GetIndexInParent() const override;
gfx::AcceleratedWidget GetTargetForNativeAccessibilityEvent() override;
+ int GetTableRowCount() const override;
+ int GetTableColCount() const override;
+ std::vector<int32_t> GetColHeaderNodeIds(int32_t col_index) const override;
+ std::vector<int32_t> GetRowHeaderNodeIds(int32_t row_index) const override;
+ int32_t GetCellId(int32_t row_index, int32_t col_index) const override;
+ int32_t CellIdToIndex(int32_t cell_id) const override;
+ int32_t CellIndexToId(int32_t cell_index) const override;
bool AccessibilityPerformAction(const ui::AXActionData& data) override;
bool ShouldIgnoreHoveredStateForTesting() override;
bool IsOffscreen() const override;
diff --git a/chromium/ui/views/accessible_pane_view.cc b/chromium/ui/views/accessible_pane_view.cc
index 87cb386a30c..cfd86c97ac1 100644
--- a/chromium/ui/views/accessible_pane_view.cc
+++ b/chromium/ui/views/accessible_pane_view.cc
@@ -4,7 +4,6 @@
#include "ui/views/accessible_pane_view.h"
-#include "base/message_loop/message_loop.h"
#include "ui/accessibility/ax_node_data.h"
#include "ui/views/focus/focus_search.h"
#include "ui/views/view_tracker.h"
@@ -25,8 +24,9 @@ class AccessiblePaneViewFocusSearch : public FocusSearch {
protected:
View* GetParent(View* v) override {
- return accessible_pane_view_->ContainsForFocusSearch(root(), v) ?
- accessible_pane_view_->GetParentForFocusSearch(v) : NULL;
+ return accessible_pane_view_->ContainsForFocusSearch(root(), v)
+ ? accessible_pane_view_->GetParentForFocusSearch(v)
+ : nullptr;
}
// Returns true if |v| is contained within the hierarchy rooted at |root|.
@@ -43,7 +43,7 @@ class AccessiblePaneViewFocusSearch : public FocusSearch {
AccessiblePaneView::AccessiblePaneView()
: pane_has_focus_(false),
allow_deactivate_on_esc_(false),
- focus_manager_(NULL),
+ focus_manager_(nullptr),
home_key_(ui::VKEY_HOME, ui::EF_NONE),
end_key_(ui::VKEY_END, ui::EF_NONE),
escape_key_(ui::VKEY_ESCAPE, ui::EF_NONE),
@@ -109,7 +109,7 @@ bool AccessiblePaneView::SetPaneFocusAndFocusDefault() {
}
views::View* AccessiblePaneView::GetDefaultFocusableChild() {
- return NULL;
+ return nullptr;
}
View* AccessiblePaneView::GetParentForFocusSearch(View* v) {
@@ -135,7 +135,10 @@ views::View* AccessiblePaneView::GetFirstFocusableChild() {
FocusTraversable* dummy_focus_traversable;
views::View* dummy_focus_traversable_view;
return focus_search_->FindNextFocusableView(
- NULL, false, views::FocusSearch::DOWN, false,
+ nullptr, FocusSearch::SearchDirection::kForwards,
+ FocusSearch::TraversalDirection::kDown,
+ FocusSearch::StartingViewPolicy::kSkipStartingView,
+ FocusSearch::AnchoredDialogPolicy::kCanGoIntoAnchoredDialog,
&dummy_focus_traversable, &dummy_focus_traversable_view);
}
@@ -143,7 +146,10 @@ views::View* AccessiblePaneView::GetLastFocusableChild() {
FocusTraversable* dummy_focus_traversable;
views::View* dummy_focus_traversable_view;
return focus_search_->FindNextFocusableView(
- this, true, views::FocusSearch::DOWN, false,
+ this, FocusSearch::SearchDirection::kBackwards,
+ FocusSearch::TraversalDirection::kDown,
+ FocusSearch::StartingViewPolicy::kSkipStartingView,
+ FocusSearch::AnchoredDialogPolicy::kCanGoIntoAnchoredDialog,
&dummy_focus_traversable, &dummy_focus_traversable_view);
}
@@ -154,7 +160,7 @@ views::FocusTraversable* AccessiblePaneView::GetPaneFocusTraversable() {
if (pane_has_focus_)
return this;
else
- return NULL;
+ return nullptr;
}
bool AccessiblePaneView::AcceleratorPressed(
@@ -247,12 +253,12 @@ views::FocusSearch* AccessiblePaneView::GetFocusSearch() {
views::FocusTraversable* AccessiblePaneView::GetFocusTraversableParent() {
DCHECK(pane_has_focus_);
- return NULL;
+ return nullptr;
}
views::View* AccessiblePaneView::GetFocusTraversableParentView() {
DCHECK(pane_has_focus_);
- return NULL;
+ return nullptr;
}
} // namespace views
diff --git a/chromium/ui/views/animation/ink_drop_host_view.cc b/chromium/ui/views/animation/ink_drop_host_view.cc
index 922600f1375..1d024628715 100644
--- a/chromium/ui/views/animation/ink_drop_host_view.cc
+++ b/chromium/ui/views/animation/ink_drop_host_view.cc
@@ -4,6 +4,7 @@
#include "ui/views/animation/ink_drop_host_view.h"
+#include "build/build_config.h"
#include "ui/events/event.h"
#include "ui/events/scoped_target_handler.h"
#include "ui/gfx/color_palette.h"
@@ -21,16 +22,16 @@ namespace {
// The scale factor to compute the large size of the default
// SquareInkDropRipple.
-const float kLargeInkDropScale = 1.333f;
+constexpr float kLargeInkDropScale = 1.333f;
// Default opacity of the ink drop when it is visible.
-const float kInkDropVisibleOpacity = 0.175f;
+constexpr float kInkDropVisibleOpacity = 0.175f;
-} // namespace
+// Default corner radii used for the SquareInkDropRipple.
+constexpr int kInkDropSmallCornerRadius = 2;
+constexpr int kInkDropLargeCornerRadius = 4;
-// static
-constexpr int InkDropHostView::kInkDropSmallCornerRadius;
-constexpr int InkDropHostView::kInkDropLargeCornerRadius;
+} // namespace
// An EventHandler that is guaranteed to be invoked and is not prone to
// InkDropHostView descendents who do not call
@@ -119,6 +120,8 @@ InkDropHostView::InkDropHostView()
ink_drop_(nullptr),
ink_drop_visible_opacity_(
PlatformStyle::kUseRipples ? kInkDropVisibleOpacity : 0),
+ ink_drop_small_corner_radius_(kInkDropSmallCornerRadius),
+ ink_drop_large_corner_radius_(kInkDropLargeCornerRadius),
old_paint_to_layer_(false),
destroying_(false) {}
@@ -175,8 +178,8 @@ std::unique_ptr<InkDropRipple> InkDropHostView::CreateDefaultInkDropRipple(
const gfx::Point& center_point,
const gfx::Size& size) const {
std::unique_ptr<InkDropRipple> ripple(new SquareInkDropRipple(
- CalculateLargeInkDropSize(size), kInkDropLargeCornerRadius, size,
- kInkDropSmallCornerRadius, center_point, GetInkDropBaseColor(),
+ CalculateLargeInkDropSize(size), ink_drop_large_corner_radius_, size,
+ ink_drop_small_corner_radius_, center_point, GetInkDropBaseColor(),
ink_drop_visible_opacity()));
return ripple;
}
@@ -184,8 +187,9 @@ std::unique_ptr<InkDropRipple> InkDropHostView::CreateDefaultInkDropRipple(
std::unique_ptr<InkDropHighlight>
InkDropHostView::CreateDefaultInkDropHighlight(const gfx::PointF& center_point,
const gfx::Size& size) const {
- std::unique_ptr<InkDropHighlight> highlight(new InkDropHighlight(
- size, kInkDropSmallCornerRadius, center_point, GetInkDropBaseColor()));
+ std::unique_ptr<InkDropHighlight> highlight(
+ new InkDropHighlight(size, ink_drop_small_corner_radius_, center_point,
+ GetInkDropBaseColor()));
highlight->set_explode_size(gfx::SizeF(CalculateLargeInkDropSize(size)));
return highlight;
}
@@ -297,12 +301,9 @@ InkDrop* InkDropHostView::GetInkDrop() {
}
void InkDropHostView::InstallInkDropMask(ui::Layer* ink_drop_layer) {
-// Layer masks don't work on Windows. See crbug.com/713359
-#if !defined(OS_WIN)
ink_drop_mask_ = CreateInkDropMask();
if (ink_drop_mask_)
ink_drop_layer->SetMaskLayer(ink_drop_mask_->layer());
-#endif
}
void InkDropHostView::ResetInkDropMask() {
diff --git a/chromium/ui/views/animation/ink_drop_host_view.h b/chromium/ui/views/animation/ink_drop_host_view.h
index 81d018254a5..14929b2564b 100644
--- a/chromium/ui/views/animation/ink_drop_host_view.h
+++ b/chromium/ui/views/animation/ink_drop_host_view.h
@@ -64,6 +64,17 @@ class VIEWS_EXPORT InkDropHostView : public View, public InkDropHost {
}
float ink_drop_visible_opacity() const { return ink_drop_visible_opacity_; }
+ void set_ink_drop_corner_radii(int small_radius, int large_radius) {
+ ink_drop_small_corner_radius_ = small_radius;
+ ink_drop_large_corner_radius_ = large_radius;
+ }
+ int ink_drop_small_corner_radius() const {
+ return ink_drop_small_corner_radius_;
+ }
+ int ink_drop_large_corner_radius() const {
+ return ink_drop_large_corner_radius_;
+ }
+
// Animates |ink_drop_| to the desired |ink_drop_state|. Caches |event| as the
// last_ripple_triggering_event().
//
@@ -74,9 +85,6 @@ class VIEWS_EXPORT InkDropHostView : public View, public InkDropHost {
void AnimateInkDrop(InkDropState state, const ui::LocatedEvent* event);
protected:
- static constexpr int kInkDropSmallCornerRadius = 2;
- static constexpr int kInkDropLargeCornerRadius = 4;
-
// Size used for the default SquareInkDropRipple.
static constexpr int kDefaultInkDropSize = 24;
@@ -162,6 +170,10 @@ class VIEWS_EXPORT InkDropHostView : public View, public InkDropHost {
float ink_drop_visible_opacity_;
+ // Radii used for the SquareInkDropRipple.
+ int ink_drop_small_corner_radius_;
+ int ink_drop_large_corner_radius_;
+
// Determines whether the view was already painting to layer before adding ink
// drop layer.
bool old_paint_to_layer_;
diff --git a/chromium/ui/views/bubble/bubble_border.cc b/chromium/ui/views/bubble/bubble_border.cc
index 44e7b93fddc..6961a92ef6f 100644
--- a/chromium/ui/views/bubble/bubble_border.cc
+++ b/chromium/ui/views/bubble/bubble_border.cc
@@ -184,10 +184,9 @@ BubbleBorder::~BubbleBorder() {}
// static
gfx::Insets BubbleBorder::GetBorderAndShadowInsets(
base::Optional<int> elevation) {
- if (elevation.has_value()) {
- return -gfx::ShadowValue::GetMargin(GetShadowValues(elevation)) +
- gfx::Insets(kBorderThicknessDip);
- }
+ // Borders with custom shadow elevations do not draw the 1px border.
+ if (elevation.has_value())
+ return -gfx::ShadowValue::GetMargin(GetShadowValues(elevation));
constexpr gfx::Insets blur(kShadowBlur + kBorderThicknessDip);
constexpr gfx::Insets offset(-kShadowVerticalOffset, 0, kShadowVerticalOffset,
@@ -218,11 +217,14 @@ gfx::Rect BubbleBorder::GetBounds(const gfx::Rect& anchor_rect,
// Apply the border part of the inset before calculating coordinates because
// the border should align with the anchor's border. For the purposes of
// positioning, the border is rounded up to a dip, which may mean we have
- // misalignment in scale factors greater than 1.
+ // misalignment in scale factors greater than 1. Borders with custom shadow
+ // elevations do not draw the 1px border.
// TODO(estade): when it becomes possible to provide px bounds instead of
// dip bounds, fix this.
const gfx::Insets border_insets =
- shadow_ == NO_ASSETS ? gfx::Insets() : gfx::Insets(kBorderThicknessDip);
+ shadow_ == NO_ASSETS || md_shadow_elevation_.has_value()
+ ? gfx::Insets()
+ : gfx::Insets(kBorderThicknessDip);
const gfx::Insets shadow_insets = GetInsets() - border_insets;
contents_bounds.Inset(-border_insets);
if (arrow_ == TOP_RIGHT) {
diff --git a/chromium/ui/views/bubble/bubble_border.h b/chromium/ui/views/bubble/bubble_border.h
index 400366fd808..778d93070d8 100644
--- a/chromium/ui/views/bubble/bubble_border.h
+++ b/chromium/ui/views/bubble/bubble_border.h
@@ -188,11 +188,14 @@ class VIEWS_EXPORT BubbleBorder : public Border {
void (cc::PaintCanvas::*draw)(const T&, const cc::PaintFlags&),
gfx::Canvas* canvas,
base::Optional<int> shadow_elevation = base::nullopt) {
- // Provide a 1 px border outside the bounds.
- const int kBorderStrokeThicknessPx = 1;
- const SkScalar one_pixel =
- SkFloatToScalar(kBorderStrokeThicknessPx / canvas->image_scale());
- rect.outset(one_pixel, one_pixel);
+ // Borders with custom shadow elevations do not draw the 1px border.
+ if (!shadow_elevation.has_value()) {
+ // Provide a 1 px border outside the bounds.
+ const int kBorderStrokeThicknessPx = 1;
+ const SkScalar one_pixel =
+ SkFloatToScalar(kBorderStrokeThicknessPx / canvas->image_scale());
+ rect.outset(one_pixel, one_pixel);
+ }
(canvas->sk_canvas()->*draw)(rect,
GetBorderAndShadowFlags(shadow_elevation));
diff --git a/chromium/ui/views/bubble/bubble_dialog_delegate.cc b/chromium/ui/views/bubble/bubble_dialog_delegate.cc
index 5856ea9b8b1..eb5a9fd9b42 100644
--- a/chromium/ui/views/bubble/bubble_dialog_delegate.cc
+++ b/chromium/ui/views/bubble/bubble_dialog_delegate.cc
@@ -16,6 +16,7 @@
#include "ui/views/bubble/bubble_frame_view.h"
#include "ui/views/layout/layout_manager.h"
#include "ui/views/layout/layout_provider.h"
+#include "ui/views/view_properties.h"
#include "ui/views/view_tracker.h"
#include "ui/views/views_delegate.h"
#include "ui/views/widget/widget.h"
@@ -26,10 +27,30 @@
#include "ui/base/win/shell.h"
#endif
+#if defined(OS_MACOSX)
+#include "ui/views/widget/widget_utils_mac.h"
+#endif
+
namespace views {
namespace {
+// The frame view for bubble dialog widgets. These are not user-sizable so have
+// simplified logic for minimum and maximum sizes to avoid repeated calls to
+// CalculatePreferredSize().
+class BubbleDialogFrameView : public BubbleFrameView {
+ public:
+ explicit BubbleDialogFrameView(const gfx::Insets& title_margins)
+ : BubbleFrameView(title_margins, gfx::Insets()) {}
+
+ // View:
+ gfx::Size GetMinimumSize() const override { return gfx::Size(); }
+ gfx::Size GetMaximumSize() const override { return gfx::Size(); }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(BubbleDialogFrameView);
+};
+
// Create a widget to host the bubble.
Widget* CreateBubbleWidget(BubbleDialogDelegateView* bubble) {
Widget* bubble_widget = new Widget();
@@ -113,7 +134,7 @@ ClientView* BubbleDialogDelegateView::CreateClientView(Widget* widget) {
NonClientFrameView* BubbleDialogDelegateView::CreateNonClientFrameView(
Widget* widget) {
- BubbleFrameView* frame = new BubbleFrameView(title_margins_, gfx::Insets());
+ BubbleFrameView* frame = new BubbleDialogFrameView(title_margins_);
frame->set_footnote_margins(
LayoutProvider::Get()->GetInsetsMetric(INSETS_DIALOG_SUBSECTION));
@@ -218,6 +239,16 @@ void BubbleDialogDelegateView::OnAnchorBoundsChanged() {
SizeToContents();
}
+void BubbleDialogDelegateView::EnableFocusTraversalFromAnchorView() {
+ DCHECK(GetWidget());
+ DCHECK(GetAnchorView());
+ GetWidget()->SetFocusTraversableParent(
+ anchor_widget()->GetFocusTraversable());
+ GetWidget()->SetFocusTraversableParentView(GetAnchorView());
+ GetAnchorView()->SetProperty(kAnchoredDialogKey,
+ static_cast<BubbleDialogDelegateView*>(this));
+}
+
BubbleDialogDelegateView::BubbleDialogDelegateView()
: BubbleDialogDelegateView(nullptr, BubbleBorder::TOP_LEFT) {}
@@ -266,6 +297,17 @@ ax::mojom::Role BubbleDialogDelegateView::GetAccessibleWindowRole() const {
return ax::mojom::Role::kAlertDialog;
}
+gfx::Size BubbleDialogDelegateView::GetMinimumSize() const {
+ // Note that although BubbleDialogFrameView will never invoke this, a subclass
+ // may override CreateNonClientFrameView() to provide a NonClientFrameView
+ // that does. See http://crbug.com/844359.
+ return gfx::Size();
+}
+
+gfx::Size BubbleDialogDelegateView::GetMaximumSize() const {
+ return gfx::Size();
+}
+
void BubbleDialogDelegateView::OnNativeThemeChanged(
const ui::NativeTheme* theme) {
UpdateColorsFromTheme(theme);
@@ -274,6 +316,9 @@ void BubbleDialogDelegateView::OnNativeThemeChanged(
void BubbleDialogDelegateView::Init() {}
void BubbleDialogDelegateView::SetAnchorView(View* anchor_view) {
+ if (GetAnchorView())
+ GetAnchorView()->ClearProperty(kAnchoredDialogKey);
+
// When the anchor view gets set the associated anchor widget might
// change as well.
if (!anchor_view || anchor_widget() != anchor_view->GetWidget()) {
@@ -305,7 +350,16 @@ void BubbleDialogDelegateView::SetAnchorRect(const gfx::Rect& rect) {
}
void BubbleDialogDelegateView::SizeToContents() {
- GetWidget()->SetBounds(GetBubbleBounds());
+ gfx::Rect bubble_bounds = GetBubbleBounds();
+#if defined(OS_MACOSX)
+ // GetBubbleBounds() doesn't take the Mac NativeWindow's style mask into
+ // account, so we need to adjust the size.
+ gfx::Size actual_size =
+ GetWindowSizeForClientSize(GetWidget(), bubble_bounds.size());
+ bubble_bounds.set_size(actual_size);
+#endif
+
+ GetWidget()->SetBounds(bubble_bounds);
}
BubbleFrameView* BubbleDialogDelegateView::GetBubbleFrameView() const {
diff --git a/chromium/ui/views/bubble/bubble_dialog_delegate.h b/chromium/ui/views/bubble/bubble_dialog_delegate.h
index fa45ae39055..c564cb85dcf 100644
--- a/chromium/ui/views/bubble/bubble_dialog_delegate.h
+++ b/chromium/ui/views/bubble/bubble_dialog_delegate.h
@@ -47,7 +47,7 @@ class VIEWS_EXPORT BubbleDialogDelegateView : public DialogDelegateView,
// Create and initialize the bubble Widget(s) with proper bounds.
static Widget* CreateBubble(BubbleDialogDelegateView* bubble_delegate);
- // WidgetDelegateView:
+ // DialogDelegateView:
BubbleDialogDelegateView* AsBubbleDialogDelegate() override;
bool ShouldShowCloseButton() const override;
ClientView* CreateClientView(Widget* widget) override;
@@ -126,6 +126,11 @@ class VIEWS_EXPORT BubbleDialogDelegateView : public DialogDelegateView,
// bounds change as a result of the widget's bounds changing.
void OnAnchorBoundsChanged();
+ // If this is called, enables focus to traverse from the anchor view
+ // to inside this dialog and back out. This may become the default in
+ // the future.
+ void EnableFocusTraversalFromAnchorView();
+
protected:
BubbleDialogDelegateView();
// |shadow| usually doesn't need to be explicitly set, just uses the default
@@ -139,10 +144,16 @@ class VIEWS_EXPORT BubbleDialogDelegateView : public DialogDelegateView,
// Get bubble bounds from the anchor rect and client view's preferred size.
virtual gfx::Rect GetBubbleBounds();
- // DialogDelegateView overrides:
+ // DialogDelegateView:
ax::mojom::Role GetAccessibleWindowRole() const override;
- // View overrides:
+ // Disallow overrides of GetMinimumSize and GetMaximumSize(). These would only
+ // be called by the FrameView, but the BubbleFrameView ignores these. Bubbles
+ // are not user-sizable and always size to their preferred size (plus any
+ // border / frame).
+ gfx::Size GetMinimumSize() const final;
+ gfx::Size GetMaximumSize() const final;
+
void OnNativeThemeChanged(const ui::NativeTheme* theme) override;
// Perform view initialization on the contents for bubble sizing.
diff --git a/chromium/ui/views/bubble/bubble_frame_view.cc b/chromium/ui/views/bubble/bubble_frame_view.cc
index fd0748f3639..5021792e327 100644
--- a/chromium/ui/views/bubble/bubble_frame_view.cc
+++ b/chromium/ui/views/bubble/bubble_frame_view.cc
@@ -134,7 +134,7 @@ Button* BubbleFrameView::CreateCloseButton(ButtonListener* listener) {
ImageButton* close_button = nullptr;
if (ui::MaterialDesignController::IsSecondaryUiMaterial()) {
close_button = CreateVectorImageButton(listener);
- SetImageFromVectorIcon(close_button, vector_icons::kClose16Icon);
+ SetImageFromVectorIcon(close_button, vector_icons::kCloseRoundedIcon);
} else {
ui::ResourceBundle* rb = &ui::ResourceBundle::GetSharedInstance();
close_button = new ImageButton(listener);
diff --git a/chromium/ui/views/bubble/bubble_frame_view_unittest.cc b/chromium/ui/views/bubble/bubble_frame_view_unittest.cc
index edabed53af6..9b16c5a3247 100644
--- a/chromium/ui/views/bubble/bubble_frame_view_unittest.cc
+++ b/chromium/ui/views/bubble/bubble_frame_view_unittest.cc
@@ -615,7 +615,6 @@ class TestBubbleDialogDelegateView : public BubbleDialogDelegateView {
destroyed_ = true;
}
int GetDialogButtons() const override { return ui::DIALOG_BUTTON_OK; }
- gfx::Size GetMinimumSize() const override { return gfx::Size(); }
gfx::Size CalculatePreferredSize() const override {
return gfx::Size(200, 200);
}
diff --git a/chromium/ui/views/bubble/tray_bubble_view.cc b/chromium/ui/views/bubble/tray_bubble_view.cc
index 1a5f43ef3df..80ae22b84af 100644
--- a/chromium/ui/views/bubble/tray_bubble_view.cc
+++ b/chromium/ui/views/bubble/tray_bubble_view.cc
@@ -381,15 +381,10 @@ base::string16 TrayBubbleView::GetAccessibleWindowTitle() const {
}
gfx::Size TrayBubbleView::CalculatePreferredSize() const {
+ DCHECK_LE(preferred_width_, params_.max_width);
return gfx::Size(preferred_width_, GetHeightForWidth(preferred_width_));
}
-gfx::Size TrayBubbleView::GetMaximumSize() const {
- gfx::Size size = GetPreferredSize();
- size.set_width(params_.max_width);
- return size;
-}
-
int TrayBubbleView::GetHeightForWidth(int width) const {
int height = GetInsets().height();
width = std::max(width - GetInsets().width(), 0);
diff --git a/chromium/ui/views/bubble/tray_bubble_view.h b/chromium/ui/views/bubble/tray_bubble_view.h
index 92f3ed00638..bd78fbc3509 100644
--- a/chromium/ui/views/bubble/tray_bubble_view.h
+++ b/chromium/ui/views/bubble/tray_bubble_view.h
@@ -150,7 +150,6 @@ class VIEWS_EXPORT TrayBubbleView : public BubbleDialogDelegateView,
// Overridden from views::View.
gfx::Size CalculatePreferredSize() const override;
- gfx::Size GetMaximumSize() const override;
int GetHeightForWidth(int width) const override;
void OnMouseEntered(const ui::MouseEvent& event) override;
void OnMouseExited(const ui::MouseEvent& event) override;
diff --git a/chromium/ui/views/cocoa/bridged_content_view.h b/chromium/ui/views/cocoa/bridged_content_view.h
index bb53eab7765..5bfbe297e5c 100644
--- a/chromium/ui/views/cocoa/bridged_content_view.h
+++ b/chromium/ui/views/cocoa/bridged_content_view.h
@@ -44,13 +44,6 @@ class View;
// The last tooltip text, used to limit updates.
base::string16 lastTooltipText_;
-
- // Whether to draw an almost-transparent background with rounded corners so
- // that OSX correctly blurs the background showing through.
- BOOL drawMenuBackgroundForBlur_;
-
- // The cached window mask. Only used for non-rectangular windows on 10.9.
- base::scoped_nsobject<NSBezierPath> windowMask_;
}
@property(readonly, nonatomic) views::View* hostedView;
@@ -72,9 +65,6 @@ class View;
// contentRect (also this NSView's frame), as given by a ui::LocatedEvent.
- (void)updateTooltipIfRequiredAt:(const gfx::Point&)locationInContent;
-// Update windowMask_ depending on the current view bounds.
-- (void)updateWindowMask;
-
// Notifies the associated FocusManager whether full keyboard access is enabled
// or not.
- (void)updateFullKeyboardAccess;
diff --git a/chromium/ui/views/cocoa/bridged_content_view.mm b/chromium/ui/views/cocoa/bridged_content_view.mm
index c55fb600d20..17f7339f7d8 100644
--- a/chromium/ui/views/cocoa/bridged_content_view.mm
+++ b/chromium/ui/views/cocoa/bridged_content_view.mm
@@ -48,17 +48,6 @@ namespace {
NSString* const kFullKeyboardAccessChangedNotification =
@"com.apple.KeyboardUIModeDidChange";
-// Returns true if all four corners of |rect| are contained inside |path|.
-bool IsRectInsidePath(NSRect rect, NSBezierPath* path) {
- return [path containsPoint:rect.origin] &&
- [path containsPoint:NSMakePoint(rect.origin.x + rect.size.width,
- rect.origin.y)] &&
- [path containsPoint:NSMakePoint(rect.origin.x,
- rect.origin.y + rect.size.height)] &&
- [path containsPoint:NSMakePoint(rect.origin.x + rect.size.width,
- rect.origin.y + rect.size.height)];
-}
-
// Convert a |point| in |source_window|'s AppKit coordinate system (origin at
// the bottom left of the window) to |target_window|'s content rect, with the
// origin at the top left of the content area.
@@ -369,30 +358,6 @@ ui::TextEditCommand GetTextEditCommandForMenuAction(SEL action) {
}
}
-- (void)updateWindowMask {
- DCHECK(![self inLiveResize]);
- DCHECK(base::mac::IsOS10_9());
- DCHECK(hostedView_);
-
- views::Widget* widget = hostedView_->GetWidget();
- if (!widget->non_client_view())
- return;
-
- const NSRect frameRect = [self bounds];
- gfx::Path mask;
- widget->non_client_view()->GetWindowMask(gfx::Size(frameRect.size), &mask);
- if (mask.isEmpty())
- return;
-
- windowMask_.reset([gfx::CreateNSBezierPathFromSkPath(mask) retain]);
-
- // Convert to AppKit coordinate system.
- NSAffineTransform* flipTransform = [NSAffineTransform transform];
- [flipTransform translateXBy:0.0 yBy:frameRect.size.height];
- [flipTransform scaleXBy:1.0 yBy:-1.0];
- [windowMask_ transformUsingAffineTransform:flipTransform];
-}
-
- (void)updateFullKeyboardAccess {
if (!hostedView_)
return;
@@ -675,67 +640,6 @@ ui::TextEditCommand GetTextEditCommandForMenuAction(SEL action) {
hostedView_->SetSize(gfx::Size(newSize.width, newSize.height));
}
-- (void)viewDidEndLiveResize {
- [super viewDidEndLiveResize];
-
- // 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::IsOS10_9()) {
- [self updateWindowMask];
- [self setNeedsDisplay:YES];
- }
-}
-
-- (void)drawRect:(NSRect)dirtyRect {
- // Note that BridgedNativeWidget uses -[NSWindow setAutodisplay:NO] to
- // suppress calls to this when the window is known to be hidden.
- if (!hostedView_)
- return;
-
- if (drawMenuBackgroundForBlur_) {
- const CGFloat radius = views::MenuConfig::instance().corner_radius;
- [skia::SkColorToSRGBNSColor(0x01000000) set];
- [[NSBezierPath bezierPathWithRoundedRect:[self bounds]
- xRadius:radius
- yRadius:radius] fill];
- }
-
- // On OS versions earlier than Yosemite, to generate a drop shadow, we set an
- // opaque background. This causes windows with non rectangular shapes to have
- // square corners. To get around this, fill the path outside the window
- // boundary with clearColor and tell Cococa to regenerate drop shadow. See
- // crbug.com/543671.
- if (windowMask_ && ![self inLiveResize] &&
- !IsRectInsidePath(dirtyRect, windowMask_)) {
- DCHECK(base::mac::IsOS10_9());
- gfx::ScopedNSGraphicsContextSaveGState state;
-
- // The outer rectangular path corresponding to the window.
- NSBezierPath* outerPath = [NSBezierPath bezierPathWithRect:[self bounds]];
-
- [outerPath appendBezierPath:windowMask_];
- [outerPath setWindingRule:NSEvenOddWindingRule];
- [[NSGraphicsContext currentContext]
- setCompositingOperation:NSCompositeCopy];
- [[NSColor clearColor] set];
-
- // Fill the region between windowMask_ and its outer rectangular path
- // with clear color. This causes the window to have the shape described
- // by windowMask_.
- [outerPath fill];
- // Regerate drop shadow around the window boundary.
- [[self window] invalidateShadow];
- }
-
- // If there's a layer, painting occurs in BridgedNativeWidget::OnPaintLayer().
- if (hostedView_->GetWidget()->GetLayer())
- return;
-
- // TODO(tapted): Add a NOTREACHED() here. At the moment, low-level
- // BridgedNativeWidget unit tests may not have a ui::Layer.
-}
-
- (BOOL)isOpaque {
if (!hostedView_)
return NO;
diff --git a/chromium/ui/views/cocoa/bridged_native_widget.h b/chromium/ui/views/cocoa/bridged_native_widget.h
index 9af5c2db7db..ec83dfb3089 100644
--- a/chromium/ui/views/cocoa/bridged_native_widget.h
+++ b/chromium/ui/views/cocoa/bridged_native_widget.h
@@ -14,6 +14,7 @@
#include "base/macros.h"
#include "components/viz/common/surfaces/parent_local_surface_id_allocator.h"
#import "ui/accelerated_widget_mac/accelerated_widget_mac.h"
+#include "ui/accelerated_widget_mac/display_ca_layer_tree.h"
#include "ui/base/ime/input_method_delegate.h"
#include "ui/compositor/layer_owner.h"
#import "ui/views/cocoa/bridged_native_widget_owner.h"
@@ -271,9 +272,7 @@ class VIEWS_EXPORT BridgedNativeWidget
// Overridden from ui::AcceleratedWidgetMac:
NSView* AcceleratedWidgetGetNSView() const override;
- void AcceleratedWidgetGetVSyncParameters(
- base::TimeTicks* timebase, base::TimeDelta* interval) const override;
- void AcceleratedWidgetSwapCompleted() override;
+ void AcceleratedWidgetCALayerParamsUpdated() override;
// Overridden from BridgedNativeWidgetOwner:
NSWindow* GetNSWindow() override;
@@ -302,6 +301,7 @@ class VIEWS_EXPORT BridgedNativeWidget
base::scoped_nsobject<NSView> compositor_superview_;
std::unique_ptr<ui::AcceleratedWidgetMac> compositor_widget_;
+ std::unique_ptr<ui::DisplayCALayerTree> display_ca_layer_tree_;
std::unique_ptr<ui::Compositor> compositor_;
viz::ParentLocalSurfaceIdAllocator parent_local_surface_id_allocator_;
diff --git a/chromium/ui/views/cocoa/bridged_native_widget.mm b/chromium/ui/views/cocoa/bridged_native_widget.mm
index 64ea16b86ef..c37fd628634 100644
--- a/chromium/ui/views/cocoa/bridged_native_widget.mm
+++ b/chromium/ui/views/cocoa/bridged_native_widget.mm
@@ -467,7 +467,13 @@ void BridgedNativeWidget::SetVisibilityState(WindowVisibilityState new_state) {
// See http://crbug.com/667602. Alternatives: call -setAlphaValue:0 and
// -setIgnoresMouseEvents:YES on the NSWindow, or dismiss the sheet before
// hiding.
- DCHECK(![window_ attachedSheet]);
+ //
+ // TODO(ellyjones): Sort this entire situation out. This DCHECK doesn't
+ // trigger in shipped builds, but it does trigger when the browser exits
+ // "abnormally" (not via one of the UI paths to exiting), such as in browser
+ // tests, so this breaks a slew of browser tests in MacViews mode. See also
+ // https://crbug.com/834926.
+ // DCHECK(![window_ attachedSheet]);
[window_ orderOut:nil];
DCHECK(!window_visible_);
@@ -528,8 +534,9 @@ void BridgedNativeWidget::SetVisibilityState(WindowVisibilityState new_state) {
// duration of the animation, but would keep it smooth. The window also
// hasn't yet received a frame from the compositor at this stage, so it is
// fully transparent until the GPU sends a frame swap IPC. For the blocking
- // option, the animation needs to wait until AcceleratedWidgetSwapCompleted
- // has been called at least once, otherwise it will animate nothing.
+ // option, the animation needs to wait until
+ // AcceleratedWidgetCALayerParamsUpdated has been called at least once,
+ // otherwise it will animate nothing.
[show_animation_ setAnimationBlockingMode:NSAnimationNonblocking];
[show_animation_ startAnimation];
}
@@ -616,7 +623,7 @@ void BridgedNativeWidget::OnWindowWillClose() {
Widget* widget = native_widget_mac_->GetWidget();
if (DialogDelegate* dialog = widget->widget_delegate()->AsDialogDelegate())
dialog->RemoveObserver(this);
- widget->OnNativeWidgetDestroying();
+ native_widget_mac_->WindowDestroying();
// Ensure BridgedNativeWidget does not have capture, otherwise
// OnMouseCaptureLost() may reference a deleted |native_widget_mac_| when
@@ -636,7 +643,7 @@ void BridgedNativeWidget::OnWindowWillClose() {
DCHECK(!show_animation_);
[window_ setDelegate:nil];
- native_widget_mac_->OnWindowDestroyed();
+ native_widget_mac_->WindowDestroyed();
// Note: |this| is deleted here.
}
@@ -723,14 +730,6 @@ void BridgedNativeWidget::OnSizeChanged() {
if ([window_ inLiveResize])
MaybeWaitForFrame(new_size);
}
-
- // 10.9 is unable to generate a window shadow from the composited CALayer, so
- // use Quartz.
- // 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::IsOS10_9() && ![window_ inLiveResize])
- [bridged_view_ updateWindowMask];
}
void BridgedNativeWidget::OnPositionChanged() {
@@ -1038,19 +1037,19 @@ NSView* BridgedNativeWidget::AcceleratedWidgetGetNSView() const {
return compositor_superview_;
}
-void BridgedNativeWidget::AcceleratedWidgetGetVSyncParameters(
- base::TimeTicks* timebase, base::TimeDelta* interval) const {
- // TODO(tapted): Add vsync support.
- *timebase = base::TimeTicks();
- *interval = base::TimeDelta();
-}
-
-void BridgedNativeWidget::AcceleratedWidgetSwapCompleted() {
+void BridgedNativeWidget::AcceleratedWidgetCALayerParamsUpdated() {
// Ignore frames arriving "late" for an old size. A frame at the new size
// should arrive soon.
if (!compositor_widget_->HasFrameOfSize(GetClientAreaSize()))
return;
+ // Update the DisplayCALayerTree with the most recent CALayerParams, to make
+ // the content display on-screen.
+ const gfx::CALayerParams* ca_layer_params =
+ compositor_widget_->GetCALayerParams();
+ if (ca_layer_params)
+ display_ca_layer_tree_->UpdateCALayerTree(*ca_layer_params);
+
if (initial_visibility_suppressed_) {
initial_visibility_suppressed_ = false;
[window_ setAlphaValue:1.0];
@@ -1241,11 +1240,11 @@ void BridgedNativeWidget::AddCompositorSuperview() {
[compositor_superview_ setWantsBestResolutionOpenGLSurface:YES];
}
+ // Set the layer first to create a layer-hosting view (not layer-backed), and
+ // set the compositor output to go to that layer.
base::scoped_nsobject<CALayer> background_layer([[CALayer alloc] init]);
- [background_layer
- setAutoresizingMask:kCALayerWidthSizable | kCALayerHeightSizable];
-
- // Set the layer first to create a layer-hosting view (not layer-backed).
+ display_ca_layer_tree_ =
+ std::make_unique<ui::DisplayCALayerTree>(background_layer.get());
[compositor_superview_ setLayer:background_layer];
[compositor_superview_ setWantsLayer:YES];
diff --git a/chromium/ui/views/controls/button/button.cc b/chromium/ui/views/controls/button/button.cc
index 8a2e4dff278..5fa2f6f1ebb 100644
--- a/chromium/ui/views/controls/button/button.cc
+++ b/chromium/ui/views/controls/button/button.cc
@@ -22,6 +22,7 @@
#include "ui/views/controls/button/menu_button.h"
#include "ui/views/controls/button/radio_button.h"
#include "ui/views/controls/button/toggle_button.h"
+#include "ui/views/controls/focus_ring.h"
#include "ui/views/painter.h"
#include "ui/views/style/platform_style.h"
#include "ui/views/widget/widget.h"
@@ -145,6 +146,13 @@ void Button::SetAnimationDuration(int duration) {
hover_animation_.SetSlideDuration(duration);
}
+void Button::SetInstallFocusRingOnFocus(bool install) {
+ if (install)
+ focus_ring_ = FocusRing::Install(this);
+ else
+ focus_ring_.reset();
+}
+
void Button::SetHotTracked(bool is_hot_tracked) {
if (state_ != STATE_DISABLED)
SetState(is_hot_tracked ? STATE_HOVERED : STATE_NORMAL);
@@ -443,7 +451,7 @@ void Button::OnBlur() {
std::unique_ptr<InkDrop> Button::CreateInkDrop() {
std::unique_ptr<views::InkDropImpl> ink_drop = CreateDefaultInkDropImpl();
- ink_drop->SetShowHighlightOnFocus(true);
+ ink_drop->SetShowHighlightOnFocus(!focus_ring_);
ink_drop->SetAutoHighlightModeForPlatform();
return std::move(ink_drop);
}
diff --git a/chromium/ui/views/controls/button/button.h b/chromium/ui/views/controls/button/button.h
index 3942d790bb4..6f0ce20627b 100644
--- a/chromium/ui/views/controls/button/button.h
+++ b/chromium/ui/views/controls/button/button.h
@@ -13,6 +13,7 @@
#include "ui/native_theme/native_theme.h"
#include "ui/views/animation/ink_drop_host_view.h"
#include "ui/views/animation/ink_drop_state.h"
+#include "ui/views/controls/focus_ring.h"
#include "ui/views/painter.h"
namespace views {
@@ -143,6 +144,7 @@ class VIEWS_EXPORT Button : public InkDropHostView,
void set_has_ink_drop_action_on_click(bool has_ink_drop_action_on_click) {
has_ink_drop_action_on_click_ = has_ink_drop_action_on_click;
}
+ void SetInstallFocusRingOnFocus(bool install_focus_ring_on_focus);
void SetHotTracked(bool is_hot_tracked);
bool IsHotTracked() const;
@@ -291,6 +293,9 @@ class VIEWS_EXPORT Button : public InkDropHostView,
// The color of the ripple and hover.
SkColor ink_drop_base_color_;
+ // The focus ring for this Button.
+ std::unique_ptr<FocusRing> focus_ring_;
+
std::unique_ptr<Painter> focus_painter_;
DISALLOW_COPY_AND_ASSIGN(Button);
diff --git a/chromium/ui/views/controls/button/checkbox.cc b/chromium/ui/views/controls/button/checkbox.cc
index ecdcda3d503..cfe84643274 100644
--- a/chromium/ui/views/controls/button/checkbox.cc
+++ b/chromium/ui/views/controls/button/checkbox.cc
@@ -30,47 +30,6 @@
namespace views {
-constexpr int kFocusRingThicknessDip = 2;
-
-// View used to paint the focus ring around the Checkbox icon.
-// The icon is painted separately.
-class IconFocusRing : public View {
- public:
- explicit IconFocusRing(Checkbox* checkbox);
- ~IconFocusRing() override = default;
-
- private:
- // View:
- void Layout() override;
- void OnPaint(gfx::Canvas* canvas) override;
-
- Checkbox* checkbox_;
-
- DISALLOW_COPY_AND_ASSIGN(IconFocusRing);
-};
-
-IconFocusRing::IconFocusRing(Checkbox* checkbox) : checkbox_(checkbox) {
- FocusRing::InitFocusRing(this);
-}
-
-void IconFocusRing::Layout() {
- gfx::Rect focus_bounds = checkbox_->image()->bounds();
- focus_bounds.Inset(-kFocusRingThicknessDip, -kFocusRingThicknessDip);
- SetBoundsRect(focus_bounds);
-}
-
-void IconFocusRing::OnPaint(gfx::Canvas* canvas) {
- cc::PaintFlags focus_flags;
- focus_flags.setAntiAlias(true);
- focus_flags.setColor(
- SkColorSetA(GetNativeTheme()->GetSystemColor(
- ui::NativeTheme::kColorId_FocusedBorderColor),
- 0x66));
- focus_flags.setStyle(cc::PaintFlags::kStroke_Style);
- focus_flags.setStrokeWidth(2);
- checkbox_->PaintFocusRing(this, canvas, focus_flags);
-}
-
// static
const char Checkbox::kViewClassName[] = "Checkbox";
@@ -88,9 +47,7 @@ Checkbox::Checkbox(const base::string16& label, bool force_md)
set_request_focus_on_press(false);
SetInkDropMode(InkDropMode::ON);
set_has_ink_drop_action_on_click(true);
- focus_ring_ = new IconFocusRing(this);
- focus_ring_->SetVisible(false);
- AddChildView(focus_ring_);
+ focus_ring_ = FocusRing::Install(this);
} else {
std::unique_ptr<LabelButtonBorder> button_border(new LabelButtonBorder());
// Inset the trailing side by a couple pixels for the focus border.
@@ -200,16 +157,12 @@ void Checkbox::OnFocus() {
LabelButton::OnFocus();
if (!UseMd())
UpdateImage();
- else
- focus_ring_->SetVisible(true);
}
void Checkbox::OnBlur() {
LabelButton::OnBlur();
if (!UseMd())
UpdateImage();
- else
- focus_ring_->SetVisible(false);
}
void Checkbox::OnNativeThemeChanged(const ui::NativeTheme* theme) {
@@ -262,6 +215,20 @@ std::unique_ptr<LabelButtonBorder> Checkbox::CreateDefaultBorder() const {
return border;
}
+void Checkbox::Layout() {
+ LabelButton::Layout();
+ if (focus_ring_ && !image()->bounds().IsEmpty())
+ focus_ring_->SetPath(GetFocusRingPath());
+}
+
+SkPath Checkbox::GetFocusRingPath() const {
+ SkPath path;
+ gfx::Rect bounds = image()->bounds();
+ bounds.Inset(1, 1);
+ path.addRect(RectToSkRect(bounds));
+ return path;
+}
+
void Checkbox::SetCustomImage(bool checked,
bool focused,
ButtonState for_state,
@@ -272,14 +239,6 @@ void Checkbox::SetCustomImage(bool checked,
UpdateImage();
}
-void Checkbox::PaintFocusRing(View* view,
- gfx::Canvas* canvas,
- const cc::PaintFlags& flags) {
- gfx::RectF bounds(view->GetLocalBounds());
- bounds.Inset(kFocusRingThicknessDip, kFocusRingThicknessDip);
- canvas->DrawRoundRect(bounds, kFocusRingThicknessDip, flags);
-}
-
const gfx::VectorIcon& Checkbox::GetVectorIcon() const {
return checked() ? kCheckboxActiveIcon : kCheckboxNormalIcon;
}
diff --git a/chromium/ui/views/controls/button/checkbox.h b/chromium/ui/views/controls/button/checkbox.h
index ca8da3cb2af..de681d69271 100644
--- a/chromium/ui/views/controls/button/checkbox.h
+++ b/chromium/ui/views/controls/button/checkbox.h
@@ -12,6 +12,7 @@
#include "base/strings/string16.h"
#include "cc/paint/paint_flags.h"
#include "ui/views/controls/button/label_button.h"
+#include "ui/views/controls/focus_ring.h"
namespace gfx {
struct VectorIcon;
@@ -63,6 +64,7 @@ class VIEWS_EXPORT Checkbox : public LabelButton {
SkColor GetInkDropBaseColor() const override;
gfx::ImageSkia GetImage(ButtonState for_state) const override;
std::unique_ptr<LabelButtonBorder> CreateDefaultBorder() const override;
+ void Layout() override;
// Set the image shown for each button state depending on whether it is
// [checked] or [focused].
@@ -71,14 +73,12 @@ class VIEWS_EXPORT Checkbox : public LabelButton {
ButtonState for_state,
const gfx::ImageSkia& image);
- // Paints a focus indicator for the view. Overridden in RadioButton.
- virtual void PaintFocusRing(View* view,
- gfx::Canvas* canvas,
- const cc::PaintFlags& flags);
-
// Gets the vector icon to use based on the current state of |checked_|.
virtual const gfx::VectorIcon& GetVectorIcon() const;
+ // Returns the path to draw the focus ring around for this Checkbox.
+ virtual SkPath GetFocusRingPath() const;
+
private:
friend class IconFocusRing;
@@ -97,15 +97,15 @@ class VIEWS_EXPORT Checkbox : public LabelButton {
// True if the checkbox is checked.
bool checked_;
- // FocusRing used in MD mode
- View* focus_ring_ = nullptr;
-
// The images for each button node_data.
gfx::ImageSkia images_[2][2][STATE_COUNT];
// The unique id for the associated label's accessible object.
int32_t label_ax_id_;
+ // The focus ring to use for this Checkbox.
+ std::unique_ptr<FocusRing> focus_ring_;
+
bool use_md_;
DISALLOW_COPY_AND_ASSIGN(Checkbox);
diff --git a/chromium/ui/views/controls/button/image_button.cc b/chromium/ui/views/controls/button/image_button.cc
index 6d945e4579c..677d4dc3d84 100644
--- a/chromium/ui/views/controls/button/image_button.cc
+++ b/chromium/ui/views/controls/button/image_button.cc
@@ -83,6 +83,13 @@ void ImageButton::SetImageAlignment(HorizontalAlignment h_align,
SchedulePaint();
}
+void ImageButton::SetBackgroundImageAlignment(HorizontalAlignment h_align,
+ VerticalAlignment v_align) {
+ h_background_alignment_ = h_align;
+ v_background_alignment_ = v_align;
+ SchedulePaint();
+}
+
void ImageButton::SetMinimumImageSize(const gfx::Size& size) {
if (minimum_image_size_ == size)
return;
@@ -138,10 +145,21 @@ void ImageButton::PaintButtonContents(gfx::Canvas* canvas) {
canvas->Scale(-1, 1);
}
- gfx::Point position = ComputeImagePaintPosition(img);
- if (!background_image_.isNull())
- canvas->DrawImageInt(background_image_, position.x(), position.y());
+ if (!background_image_.isNull()) {
+ // If the background image alignment was not set, use the image
+ // alignment.
+ HorizontalAlignment h_alignment =
+ h_background_alignment_.value_or(h_alignment_);
+ VerticalAlignment v_alignment =
+ v_background_alignment_.value_or(v_alignment_);
+ gfx::Point background_position = ComputeImagePaintPosition(
+ background_image_, h_alignment, v_alignment);
+ canvas->DrawImageInt(background_image_, background_position.x(),
+ background_position.y());
+ }
+ gfx::Point position =
+ ComputeImagePaintPosition(img, h_alignment_, v_alignment_);
canvas->DrawImageInt(img, position.x(), position.y());
}
}
@@ -166,11 +184,13 @@ gfx::ImageSkia ImageButton::GetImageToPaint() {
////////////////////////////////////////////////////////////////////////////////
// ImageButton, private:
-gfx::Point ImageButton::ComputeImagePaintPosition(const gfx::ImageSkia& image) {
+const gfx::Point ImageButton::ComputeImagePaintPosition(
+ const gfx::ImageSkia& image,
+ HorizontalAlignment h_alignment,
+ VerticalAlignment v_alignment) {
int x = 0, y = 0;
gfx::Rect rect = GetContentsBounds();
- HorizontalAlignment h_alignment = h_alignment_;
if (draw_image_mirrored_) {
if (h_alignment == ALIGN_RIGHT)
h_alignment = ALIGN_LEFT;
@@ -185,7 +205,7 @@ gfx::Point ImageButton::ComputeImagePaintPosition(const gfx::ImageSkia& image) {
if (v_alignment_ == ALIGN_MIDDLE)
y = (rect.height() - image.height()) / 2;
- else if (v_alignment_ == ALIGN_BOTTOM)
+ else if (v_alignment == ALIGN_BOTTOM)
y = rect.height() - image.height();
x += rect.x();
diff --git a/chromium/ui/views/controls/button/image_button.h b/chromium/ui/views/controls/button/image_button.h
index fb73e6c1e06..bc69e6e350b 100644
--- a/chromium/ui/views/controls/button/image_button.h
+++ b/chromium/ui/views/controls/button/image_button.h
@@ -60,6 +60,10 @@ class VIEWS_EXPORT ImageButton : public Button {
void SetImageAlignment(HorizontalAlignment h_align,
VerticalAlignment v_align);
+ // Sets how the background is laid out within the button's bounds.
+ void SetBackgroundImageAlignment(HorizontalAlignment h_align,
+ VerticalAlignment v_align);
+
// The minimum size of the contents (not including the border). The contents
// will be at least this size, but may be larger if the image itself is
// larger.
@@ -97,17 +101,25 @@ class VIEWS_EXPORT ImageButton : public Button {
FRIEND_TEST_ALL_PREFIXES(ImageButtonTest, ImagePositionWithBorder);
FRIEND_TEST_ALL_PREFIXES(ImageButtonTest, LeftAlignedMirrored);
FRIEND_TEST_ALL_PREFIXES(ImageButtonTest, RightAlignedMirrored);
+ FRIEND_TEST_ALL_PREFIXES(ImageButtonTest, ImagePositionWithBackground);
FRIEND_TEST_ALL_PREFIXES(ImageButtonFactoryTest, CreateVectorImageButton);
// Returns the correct position of the image for painting.
- gfx::Point ComputeImagePaintPosition(const gfx::ImageSkia& image);
+ const gfx::Point ComputeImagePaintPosition(const gfx::ImageSkia& image,
+ HorizontalAlignment h_alignment,
+ VerticalAlignment v_alignment);
// Image alignment.
HorizontalAlignment h_alignment_;
VerticalAlignment v_alignment_;
gfx::Size minimum_image_size_;
+ // Background alignment. If these are not set, the background image uses the
+ // image alignment.
+ base::Optional<HorizontalAlignment> h_background_alignment_;
+ base::Optional<VerticalAlignment> v_background_alignment_;
+
// Whether we draw our resources horizontally flipped. This can happen in the
// linux titlebar, where image resources were designed to be flipped so a
// small curved corner in the close button designed to fit into the frame
diff --git a/chromium/ui/views/controls/button/image_button_factory_unittest.cc b/chromium/ui/views/controls/button/image_button_factory_unittest.cc
index 52728ff3516..6a2e776d07d 100644
--- a/chromium/ui/views/controls/button/image_button_factory_unittest.cc
+++ b/chromium/ui/views/controls/button/image_button_factory_unittest.cc
@@ -26,14 +26,14 @@ TEST_F(ImageButtonFactoryTest, CreateVectorImageButton) {
TEST_F(ImageButtonFactoryTest, SetImageFromVectorIcon) {
ImageButton* button = CreateVectorImageButton(nullptr);
- SetImageFromVectorIcon(button, vector_icons::kClose16Icon, SK_ColorRED);
+ SetImageFromVectorIcon(button, vector_icons::kCloseRoundedIcon, SK_ColorRED);
EXPECT_FALSE(button->GetImage(Button::STATE_NORMAL).isNull());
EXPECT_FALSE(button->GetImage(Button::STATE_DISABLED).isNull());
EXPECT_EQ(color_utils::DeriveDefaultIconColor(SK_ColorRED),
button->GetInkDropBaseColor());
// Default to GoogleGrey900.
- SetImageFromVectorIcon(button, vector_icons::kClose16Icon);
+ SetImageFromVectorIcon(button, vector_icons::kCloseRoundedIcon);
EXPECT_EQ(color_utils::DeriveDefaultIconColor(gfx::kGoogleGrey900),
button->GetInkDropBaseColor());
delete button;
diff --git a/chromium/ui/views/controls/button/image_button_unittest.cc b/chromium/ui/views/controls/button/image_button_unittest.cc
index baf9a75d18b..4c595a5d7ef 100644
--- a/chromium/ui/views/controls/button/image_button_unittest.cc
+++ b/chromium/ui/views/controls/button/image_button_unittest.cc
@@ -39,6 +39,13 @@ class Parent : public views::View {
namespace views {
+namespace {
+const ImageButton::HorizontalAlignment kDefaultHorizontalAlignment =
+ ImageButton::ALIGN_LEFT;
+const ImageButton::VerticalAlignment kDefaultVerticalAlignment =
+ ImageButton::ALIGN_TOP;
+} // namespace
+
typedef ViewsTestBase ImageButtonTest;
TEST_F(ImageButtonTest, Basics) {
@@ -122,24 +129,39 @@ TEST_F(ImageButtonTest, ImagePositionWithBorder) {
// The image should be painted at the top-left corner.
EXPECT_EQ(gfx::Point().ToString(),
- button.ComputeImagePaintPosition(image).ToString());
+ button
+ .ComputeImagePaintPosition(image, kDefaultHorizontalAlignment,
+ kDefaultVerticalAlignment)
+ .ToString());
button.SetBorder(views::CreateEmptyBorder(10, 5, 0, 0));
EXPECT_EQ(gfx::Point(5, 10).ToString(),
- button.ComputeImagePaintPosition(image).ToString());
+ button
+ .ComputeImagePaintPosition(image, kDefaultHorizontalAlignment,
+ kDefaultVerticalAlignment)
+ .ToString());
button.SetBorder(NullBorder());
button.SetBounds(0, 0, 50, 50);
EXPECT_EQ(gfx::Point().ToString(),
- button.ComputeImagePaintPosition(image).ToString());
+ button
+ .ComputeImagePaintPosition(image, kDefaultHorizontalAlignment,
+ kDefaultVerticalAlignment)
+ .ToString());
button.SetImageAlignment(ImageButton::ALIGN_CENTER,
ImageButton::ALIGN_MIDDLE);
EXPECT_EQ(gfx::Point(15, 10).ToString(),
- button.ComputeImagePaintPosition(image).ToString());
+ button
+ .ComputeImagePaintPosition(image, ImageButton::ALIGN_CENTER,
+ ImageButton::ALIGN_MIDDLE)
+ .ToString());
button.SetBorder(views::CreateEmptyBorder(10, 10, 0, 0));
EXPECT_EQ(gfx::Point(20, 15).ToString(),
- button.ComputeImagePaintPosition(image).ToString());
+ button
+ .ComputeImagePaintPosition(image, ImageButton::ALIGN_CENTER,
+ ImageButton::ALIGN_MIDDLE)
+ .ToString());
// The entire button's size should take the border into account.
EXPECT_EQ(gfx::Size(30, 40).ToString(), button.GetPreferredSize().ToString());
@@ -161,7 +183,10 @@ TEST_F(ImageButtonTest, LeftAlignedMirrored) {
// Because the coordinates are flipped, we should expect this to draw as if
// it were ALIGN_RIGHT.
EXPECT_EQ(gfx::Point(30, 0).ToString(),
- button.ComputeImagePaintPosition(image).ToString());
+ button
+ .ComputeImagePaintPosition(image, ImageButton::ALIGN_LEFT,
+ ImageButton::ALIGN_BOTTOM)
+ .ToString());
}
TEST_F(ImageButtonTest, RightAlignedMirrored) {
@@ -176,7 +201,10 @@ TEST_F(ImageButtonTest, RightAlignedMirrored) {
// Because the coordinates are flipped, we should expect this to draw as if
// it were ALIGN_LEFT.
EXPECT_EQ(gfx::Point(0, 0).ToString(),
- button.ComputeImagePaintPosition(image).ToString());
+ button
+ .ComputeImagePaintPosition(image, ImageButton::ALIGN_RIGHT,
+ ImageButton::ALIGN_BOTTOM)
+ .ToString());
}
TEST_F(ImageButtonTest, PreferredSizeInvalidation) {
diff --git a/chromium/ui/views/controls/button/label_button.cc b/chromium/ui/views/controls/button/label_button.cc
index d56f4cb2f73..60907bd19cd 100644
--- a/chromium/ui/views/controls/button/label_button.cc
+++ b/chromium/ui/views/controls/button/label_button.cc
@@ -398,7 +398,7 @@ std::unique_ptr<views::InkDropHighlight> LabelButton::CreateInkDropHighlight()
const {
return ShouldUseFloodFillInkDrop()
? std::make_unique<views::InkDropHighlight>(
- size(), kInkDropSmallCornerRadius,
+ size(), ink_drop_small_corner_radius(),
gfx::RectF(GetLocalBounds()).CenterPoint(),
GetInkDropBaseColor())
: CreateDefaultInkDropHighlight(
diff --git a/chromium/ui/views/controls/button/md_text_button.cc b/chromium/ui/views/controls/button/md_text_button.cc
index 60f6ca0ffb3..e3c0f6e9dc2 100644
--- a/chromium/ui/views/controls/button/md_text_button.cc
+++ b/chromium/ui/views/controls/button/md_text_button.cc
@@ -77,6 +77,7 @@ MdTextButton* MdTextButton::Create(ButtonListener* listener,
MdTextButton* button = new MdTextButton(listener, button_context);
button->SetText(text);
button->SetFocusForPlatform();
+
return button;
}
@@ -95,6 +96,11 @@ void MdTextButton::SetBgColorOverride(const base::Optional<SkColor>& color) {
UpdateColors();
}
+void MdTextButton::set_corner_radius(float radius) {
+ corner_radius_ = radius;
+ set_ink_drop_corner_radii(corner_radius_, corner_radius_);
+}
+
void MdTextButton::OnPaintBackground(gfx::Canvas* canvas) {
LabelButton::OnPaintBackground(canvas);
if (hover_animation().is_animating() || state() == STATE_HOVERED) {
@@ -104,16 +110,6 @@ void MdTextButton::OnPaintBackground(gfx::Canvas* canvas) {
}
}
-void MdTextButton::OnFocus() {
- LabelButton::OnFocus();
- FocusRing::Install(this);
-}
-
-void MdTextButton::OnBlur() {
- LabelButton::OnBlur();
- FocusRing::Uninstall(this);
-}
-
void MdTextButton::OnNativeThemeChanged(const ui::NativeTheme* theme) {
LabelButton::OnNativeThemeChanged(theme);
UpdateColors();
@@ -179,16 +175,17 @@ void MdTextButton::UpdateStyleToIndicateDefaultStatus() {
MdTextButton::MdTextButton(ButtonListener* listener, int button_context)
: LabelButton(listener, base::string16(), button_context),
- is_prominent_(false),
- corner_radius_(kInkDropSmallCornerRadius) {
+ is_prominent_(false) {
SetInkDropMode(InkDropMode::ON);
set_has_ink_drop_action_on_click(true);
+ set_corner_radius(LayoutProvider::Get()->GetCornerRadiusMetric(EMPHASIS_LOW));
SetHorizontalAlignment(gfx::ALIGN_CENTER);
SetFocusForPlatform();
const int minimum_width = LayoutProvider::Get()->GetDistanceMetric(
DISTANCE_DIALOG_BUTTON_MINIMUM_WIDTH);
SetMinSize(gfx::Size(minimum_width, 0));
SetFocusPainter(nullptr);
+ SetInstallFocusRingOnFocus(true);
label()->SetAutoColorReadabilityEnabled(false);
set_request_focus_on_press(false);
@@ -226,8 +223,9 @@ void MdTextButton::UpdatePadding() {
// GetControlHeightForFont(). It can't because that only returns a correct
// result with --secondary-ui-md, and MdTextButtons appear in top chrome
// without that.
- const int kBaseHeight = 28;
- int target_height = std::max(kBaseHeight + size_delta * 2,
+ const int base_height =
+ ui::MaterialDesignController::IsNewerMaterialUi() ? 32 : 28;
+ int target_height = std::max(base_height + size_delta * 2,
label()->font_list().GetFontSize() * 2);
int label_height = label()->GetPreferredSize().height();
diff --git a/chromium/ui/views/controls/button/md_text_button.h b/chromium/ui/views/controls/button/md_text_button.h
index 25f65ce5e87..eafa8a817ba 100644
--- a/chromium/ui/views/controls/button/md_text_button.h
+++ b/chromium/ui/views/controls/button/md_text_button.h
@@ -9,6 +9,7 @@
#include "base/optional.h"
#include "ui/views/controls/button/label_button.h"
+#include "ui/views/controls/focus_ring.h"
#include "ui/views/style/typography.h"
namespace views {
@@ -36,14 +37,12 @@ class VIEWS_EXPORT MdTextButton : public LabelButton {
// Override the default corner radius of the round rect used for the
// background and ink drop effects.
- void set_corner_radius(float radius) { corner_radius_ = radius; }
+ void set_corner_radius(float radius);
// View:
void OnPaintBackground(gfx::Canvas* canvas) override;
// LabelButton:
- void OnFocus() override;
- void OnBlur() override;
void OnNativeThemeChanged(const ui::NativeTheme* theme) override;
std::unique_ptr<InkDrop> CreateInkDrop() override;
std::unique_ptr<views::InkDropRipple> CreateInkDropRipple() const override;
diff --git a/chromium/ui/views/controls/button/menu_button.cc b/chromium/ui/views/controls/button/menu_button.cc
index b6996aab742..7f38894e57c 100644
--- a/chromium/ui/views/controls/button/menu_button.cc
+++ b/chromium/ui/views/controls/button/menu_button.cc
@@ -287,7 +287,7 @@ bool MenuButton::OnKeyReleased(const ui::KeyEvent& event) {
void MenuButton::GetAccessibleNodeData(ui::AXNodeData* node_data) {
Button::GetAccessibleNodeData(node_data);
node_data->role = ax::mojom::Role::kPopUpButton;
- node_data->AddState(ax::mojom::State::kHaspopup);
+ node_data->SetHasPopup(ax::mojom::HasPopup::kMenu);
if (enabled())
node_data->SetDefaultActionVerb(ax::mojom::DefaultActionVerb::kOpen);
}
@@ -385,7 +385,8 @@ void MenuButton::DecrementPressedLocked() {
if (should_disable_after_press_) {
desired_state = STATE_DISABLED;
should_disable_after_press_ = false;
- } else if (ShouldEnterHoveredState()) {
+ } else if (GetWidget() && !GetWidget()->dragged_view() &&
+ ShouldEnterHoveredState()) {
desired_state = STATE_HOVERED;
GetInkDrop()->SetHovered(true);
}
diff --git a/chromium/ui/views/controls/button/radio_button.cc b/chromium/ui/views/controls/button/radio_button.cc
index e8499db3311..efadd78b3e9 100644
--- a/chromium/ui/views/controls/button/radio_button.cc
+++ b/chromium/ui/views/controls/button/radio_button.cc
@@ -157,15 +157,14 @@ void RadioButton::SetChecked(bool checked) {
Checkbox::SetChecked(checked);
}
-void RadioButton::PaintFocusRing(View* view,
- gfx::Canvas* canvas,
- const cc::PaintFlags& flags) {
- canvas->DrawCircle(gfx::RectF(view->GetLocalBounds()).CenterPoint(),
- image()->width() / 2, flags);
-}
-
const gfx::VectorIcon& RadioButton::GetVectorIcon() const {
return checked() ? kRadioButtonActiveIcon : kRadioButtonNormalIcon;
}
+SkPath RadioButton::GetFocusRingPath() const {
+ SkPath path;
+ path.addOval(gfx::RectToSkRect(image()->bounds()));
+ return path;
+}
+
} // namespace views
diff --git a/chromium/ui/views/controls/button/radio_button.h b/chromium/ui/views/controls/button/radio_button.h
index b91ed088182..02cf91dcf8d 100644
--- a/chromium/ui/views/controls/button/radio_button.h
+++ b/chromium/ui/views/controls/button/radio_button.h
@@ -8,6 +8,7 @@
#include "base/macros.h"
#include "base/strings/string16.h"
#include "ui/views/controls/button/checkbox.h"
+#include "ui/views/controls/focus_ring.h"
namespace views {
@@ -38,10 +39,8 @@ class VIEWS_EXPORT RadioButton : public Checkbox {
// Overridden from Checkbox:
void SetChecked(bool checked) override;
- void PaintFocusRing(View* view,
- gfx::Canvas* canvas,
- const cc::PaintFlags& flags) override;
const gfx::VectorIcon& GetVectorIcon() const override;
+ SkPath GetFocusRingPath() const override;
private:
DISALLOW_COPY_AND_ASSIGN(RadioButton);
diff --git a/chromium/ui/views/controls/combobox/combobox.cc b/chromium/ui/views/controls/combobox/combobox.cc
index 67f9af2eaee..2cb22fea6dc 100644
--- a/chromium/ui/views/controls/combobox/combobox.cc
+++ b/chromium/ui/views/controls/combobox/combobox.cc
@@ -462,6 +462,9 @@ Combobox::Combobox(ui::ComboboxModel* model, Style style)
arrow_image_ = *ui::ResourceBundle::GetSharedInstance().GetImageSkiaNamed(
IDR_MENU_DROPARROW);
}
+
+ if (UseMd())
+ focus_ring_ = FocusRing::Install(this);
}
Combobox::~Combobox() {
@@ -533,11 +536,9 @@ void Combobox::SetInvalid(bool invalid) {
invalid_ = invalid;
- if (HasFocus() && UseMd()) {
- FocusRing::Install(this, invalid_
- ? ui::NativeTheme::kColorId_AlertSeverityHigh
- : ui::NativeTheme::kColorId_NumColors);
- }
+ if (focus_ring_)
+ focus_ring_->SetInvalid(invalid);
+
UpdateBorder();
SchedulePaint();
}
@@ -746,11 +747,6 @@ void Combobox::OnFocus() {
View::OnFocus();
// Border renders differently when focused.
SchedulePaint();
- if (UseMd()) {
- FocusRing::Install(this, invalid_
- ? ui::NativeTheme::kColorId_AlertSeverityHigh
- : ui::NativeTheme::kColorId_NumColors);
- }
}
void Combobox::OnBlur() {
@@ -761,8 +757,6 @@ void Combobox::OnBlur() {
selector_->OnViewBlur();
// Border renders differently when focused.
SchedulePaint();
- if (UseMd())
- FocusRing::Uninstall(this);
}
void Combobox::GetAccessibleNodeData(ui::AXNodeData* node_data) {
diff --git a/chromium/ui/views/controls/combobox/combobox.h b/chromium/ui/views/controls/combobox/combobox.h
index 7977c36feda..d6132543edd 100644
--- a/chromium/ui/views/controls/combobox/combobox.h
+++ b/chromium/ui/views/controls/combobox/combobox.h
@@ -11,6 +11,7 @@
#include "base/time/time.h"
#include "ui/base/models/combobox_model.h"
#include "ui/views/controls/button/button.h"
+#include "ui/views/controls/focus_ring.h"
#include "ui/views/controls/prefix_delegate.h"
namespace gfx {
@@ -113,7 +114,7 @@ class VIEWS_EXPORT Combobox : public View,
void SetSelectedRow(int row) override;
base::string16 GetTextForRow(int row) override;
- // Overriden from ButtonListener:
+ // Overridden from ButtonListener:
void ButtonPressed(Button* sender, const ui::Event& event) override;
protected:
@@ -165,6 +166,9 @@ class VIEWS_EXPORT Combobox : public View,
// Returns the width of the combobox's arrow container.
int GetArrowContainerWidth() const;
+ // Returns the color to use for the combobox's focus ring.
+ SkColor GetFocusRingColor() const;
+
// Optionally used to tie the lifetime of the model to this combobox. See
// constructor.
std::unique_ptr<ui::ComboboxModel> owned_model_;
@@ -232,6 +236,9 @@ class VIEWS_EXPORT Combobox : public View,
// true, the parent view must relayout in ChildPreferredSizeChanged().
bool size_to_largest_label_;
+ // The focus ring for this Combobox.
+ std::unique_ptr<FocusRing> focus_ring_;
+
// Used for making calbacks.
base::WeakPtrFactory<Combobox> weak_ptr_factory_;
diff --git a/chromium/ui/views/controls/focus_ring.cc b/chromium/ui/views/controls/focus_ring.cc
index 1cf8348a308..c17bc868e2f 100644
--- a/chromium/ui/views/controls/focus_ring.cc
+++ b/chromium/ui/views/controls/focus_ring.cc
@@ -5,70 +5,52 @@
#include "ui/views/controls/focus_ring.h"
#include "ui/gfx/canvas.h"
-
-namespace views {
+#include "ui/views/controls/focusable_border.h"
+#include "ui/views/style/platform_style.h"
namespace {
-// The default stroke width of the focus border in dp.
-constexpr float kFocusHaloThicknessDp = 2.f;
-
-FocusRing* GetFocusRing(View* parent) {
- for (int i = 0; i < parent->child_count(); ++i) {
- if (parent->child_at(i)->GetClassName() == FocusRing::kViewClassName)
- return static_cast<FocusRing*>(parent->child_at(i));
- }
- return nullptr;
+ui::NativeTheme::ColorId ColorIdForValidity(bool valid) {
+ return valid ? ui::NativeTheme::kColorId_FocusedBorderColor
+ : ui::NativeTheme::kColorId_AlertSeverityHigh;
}
} // namespace
+namespace views {
+
const char FocusRing::kViewClassName[] = "FocusRing";
// static
-FocusRing* FocusRing::Install(View* parent,
- SkColor color,
- float corner_radius) {
- FocusRing* ring = GetFocusRing(parent);
- if (!ring) {
- ring = new FocusRing(color, corner_radius);
- parent->AddChildView(ring);
- } else {
- ring->color_ = color;
- ring->corner_radius_ = corner_radius;
- }
+std::unique_ptr<FocusRing> FocusRing::Install(View* parent) {
+ auto ring = base::WrapUnique<FocusRing>(new FocusRing(parent));
+ ring->set_owned_by_client();
+ parent->AddChildView(ring.get());
+ parent->AddObserver(ring.get());
ring->Layout();
ring->SchedulePaint();
return ring;
}
// static
-FocusRing* FocusRing::Install(View* parent,
- ui::NativeTheme::ColorId override_color_id) {
- SkColor ring_color = parent->GetNativeTheme()->GetSystemColor(
- override_color_id == ui::NativeTheme::kColorId_NumColors
- ? ui::NativeTheme::kColorId_FocusedBorderColor
- : override_color_id);
- FocusRing* ring = Install(parent, ring_color);
- DCHECK(ring);
- ring->override_color_id_ = override_color_id;
- if (!ring->view_observer_.IsObserving(parent))
- ring->view_observer_.Add(parent);
- return ring;
+bool FocusRing::IsPathUseable(const SkPath& path) {
+ return path.isRect(nullptr) || path.isOval(nullptr) || path.isRRect(nullptr);
}
-// static
-void FocusRing::Uninstall(View* parent) {
- delete GetFocusRing(parent);
+void FocusRing::SetPath(const SkPath& path) {
+ DCHECK(IsPathUseable(path));
+ path_ = path;
+ SchedulePaint();
}
-// static
-void FocusRing::InitFocusRing(View* view) {
- // A layer is necessary to paint beyond the parent's bounds.
- view->SetPaintToLayer();
- view->layer()->SetFillsBoundsOpaquely(false);
- // Don't allow the view to process events.
- view->set_can_process_events_within_subtree(false);
+void FocusRing::SetInvalid(bool invalid) {
+ invalid_ = invalid;
+ SchedulePaint();
+}
+
+void FocusRing::SetHasFocusPredicate(const ViewPredicate& predicate) {
+ has_focus_predicate_ = predicate;
+ SchedulePaint();
}
const char* FocusRing::GetClassName() const {
@@ -79,37 +61,87 @@ 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));
+ focus_bounds.Inset(gfx::Insets(PlatformStyle::kFocusHaloInset));
SetBoundsRect(focus_bounds);
}
void FocusRing::OnPaint(gfx::Canvas* canvas) {
- cc::PaintFlags flags;
- flags.setAntiAlias(true);
- flags.setColor(SkColorSetA(color_, 0x66));
- flags.setStyle(cc::PaintFlags::kStroke_Style);
- flags.setStrokeWidth(kFocusHaloThicknessDp);
- gfx::RectF rect(GetLocalBounds());
- rect.Inset(gfx::InsetsF(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, increase
- // the rounding slightly by adding half the ring thickness.
- canvas->DrawRoundRect(rect, corner_radius_ + kFocusHaloThicknessDp / 2.f,
- flags);
+ if (!has_focus_predicate_(parent()))
+ return;
+
+ SkColor base_color =
+ GetNativeTheme()->GetSystemColor(ColorIdForValidity(!invalid_));
+
+ cc::PaintFlags paint;
+ paint.setAntiAlias(true);
+ paint.setColor(SkColorSetA(base_color, 0x66));
+ paint.setStyle(cc::PaintFlags::kStroke_Style);
+ paint.setStrokeWidth(PlatformStyle::kFocusHaloThickness);
+
+ SkPath path = path_;
+ if (path.isEmpty())
+ path.addRect(RectToSkRect(parent()->GetLocalBounds()));
+
+ DCHECK(IsPathUseable(path));
+ SkRect bounds;
+ SkRRect rbounds;
+ if (path.isRect(&bounds)) {
+ canvas->sk_canvas()->drawRRect(RingRectFromPathRect(bounds), paint);
+ } else if (path.isOval(&bounds)) {
+ gfx::RectF rect = gfx::SkRectToRectF(bounds);
+ View::ConvertRectToTarget(view_, this, &rect);
+ canvas->sk_canvas()->drawRRect(SkRRect::MakeOval(gfx::RectFToSkRect(rect)),
+ paint);
+ } else if (path.isRRect(&rbounds)) {
+ canvas->sk_canvas()->drawRRect(RingRectFromPathRect(rbounds), paint);
+ }
}
-void FocusRing::OnViewNativeThemeChanged(View* observed_view) {
- if (override_color_id_) {
- color_ = observed_view->GetNativeTheme()->GetSystemColor(
- override_color_id_.value());
- }
+void FocusRing::OnViewFocused(View* view) {
+ SchedulePaint();
}
-FocusRing::FocusRing(SkColor color, float corner_radius)
- : color_(color), corner_radius_(corner_radius), view_observer_(this) {
- InitFocusRing(this);
+void FocusRing::OnViewBlurred(View* view) {
+ SchedulePaint();
}
-FocusRing::~FocusRing() {}
+FocusRing::FocusRing(View* parent) : view_(parent) {
+ // A layer is necessary to paint beyond the parent's bounds.
+ SetPaintToLayer();
+ layer()->SetFillsBoundsOpaquely(false);
+ // Don't allow the view to process events.
+ set_can_process_events_within_subtree(false);
+
+ has_focus_predicate_ = [](View* p) -> bool { return p->HasFocus(); };
+}
+
+FocusRing::~FocusRing() {
+ if (parent())
+ parent()->RemoveObserver(this);
+}
+
+SkRRect FocusRing::RingRectFromPathRect(const SkRect& rect) const {
+ double thickness = PlatformStyle::kFocusHaloThickness / 2.f;
+ double corner_radius = FocusableBorder::kCornerRadiusDp + thickness;
+ return RingRectFromPathRect(
+ SkRRect::MakeRectXY(rect, corner_radius, corner_radius));
+}
+
+SkRRect FocusRing::RingRectFromPathRect(const SkRRect& rrect) const {
+ double thickness = PlatformStyle::kFocusHaloThickness / 2.f;
+ gfx::RectF r = gfx::SkRectToRectF(rrect.rect());
+ View::ConvertRectToTarget(view_, this, &r);
+
+ SkRRect skr =
+ rrect.makeOffset(r.x() - rrect.rect().x(), r.y() - rrect.rect().y());
+
+ // 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, increase
+ // the rounding slightly by adding half the ring thickness.
+ skr.inset(PlatformStyle::kFocusHaloInset, PlatformStyle::kFocusHaloInset);
+ skr.inset(thickness, thickness);
+
+ return skr;
+}
} // namespace views
diff --git a/chromium/ui/views/controls/focus_ring.h b/chromium/ui/views/controls/focus_ring.h
index df1f1b330ca..1b4d99ca2b1 100644
--- a/chromium/ui/views/controls/focus_ring.h
+++ b/chromium/ui/views/controls/focus_ring.h
@@ -17,31 +17,55 @@ 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.
+//
+// Using FocusRing looks something like this:
+//
+// class MyView : public View {
+// ...
+// private:
+// std::unique_ptr<FocusRing> focus_ring_;
+// };
+//
+// MyView::MyView() {
+// focus_ring_ = FocusRing::Install(this);
+// ...
+// }
+//
+// If MyView should show a rounded rectangular focus ring when it has focus and
+// hide the ring when it loses focus, no other configuration is necessary. In
+// other cases, it might be necessary to use the Set*() functions on FocusRing;
+// these take care of repainting it when the state changes.
class VIEWS_EXPORT FocusRing : public View, public ViewObserver {
public:
static const char kViewClassName[];
- // Create a FocusRing and adds it to |parent|, or updates the one that already
- // exists with the given |color| and |corner_radius|.
- // TODO(crbug.com/831926): Prefer using the below Install() method - this one
- // should eventually be removed.
- static FocusRing* Install(
- View* parent,
- SkColor color,
- float corner_radius = FocusableBorder::kCornerRadiusDp);
+ using ViewPredicate = std::function<bool(View* view)>;
- // Similar to FocusRing::Install(View, SkColor, float), but
- // |override_color_id| will be used in place of the default coloration
- // when provided.
- static FocusRing* Install(View* parent,
- ui::NativeTheme::ColorId override_color_id =
- ui::NativeTheme::kColorId_NumColors);
+ ~FocusRing() override;
+
+ // Create a FocusRing and adds it to |parent|. The returned focus ring is
+ // owned by the client (the code calling FocusRing::Install), *not* by
+ // |parent|.
+ static std::unique_ptr<FocusRing> Install(View* parent);
+
+ // Returns whether this class can draw a focus ring from |path|. Not all paths
+ // are useable since not all paths can be easily outset.
+ static bool IsPathUseable(const SkPath& path);
- // Removes the FocusRing from |parent|.
- static void Uninstall(View* parent);
+ // Sets the path to draw this FocusRing around. This path is in the parent
+ // view's coordinate system, *not* in the FocusRing's coordinate system.
+ void SetPath(const SkPath& path);
- // Configure |view| for painting focus ring highlights.
- static void InitFocusRing(View* view);
+ // Sets whether the FocusRing should show an invalid state for the View it
+ // encloses.
+ void SetInvalid(bool invalid);
+
+ // Sets the predicate function used to tell when the parent has focus. The
+ // parent is passed into this predicate; it should return whether the parent
+ // should be treated as focused. This is useful when, for example, the parent
+ // wraps an inner view and the inner view is the one that actually receives
+ // focus, but the FocusRing sits on the parent instead of the inner view.
+ void SetHasFocusPredicate(const ViewPredicate& predicate);
// View:
const char* GetClassName() const override;
@@ -49,17 +73,33 @@ class VIEWS_EXPORT FocusRing : public View, public ViewObserver {
void OnPaint(gfx::Canvas* canvas) override;
// ViewObserver:
- void OnViewNativeThemeChanged(View* observed_view) override;
-
- protected:
- FocusRing(SkColor color, float corner_radius);
- ~FocusRing() override;
+ void OnViewFocused(View* view) override;
+ void OnViewBlurred(View* view) override;
private:
- SkColor color_;
- float corner_radius_;
- base::Optional<ui::NativeTheme::ColorId> override_color_id_;
- ScopedObserver<View, FocusRing> view_observer_;
+ explicit FocusRing(View* parent);
+
+ // Translates the provided SkRect or SkRRect, which is in the parent's
+ // coordinate system, into this view's coordinate system, then insets it
+ // appropriately to produce the focus ring "halo" effect. If the supplied rect
+ // is an SkRect, it will have the default focus ring corner radius applied as
+ // well.
+ SkRRect RingRectFromPathRect(const SkRect& rect) const;
+ SkRRect RingRectFromPathRect(const SkRRect& rect) const;
+
+ // The View this focus ring is installed on.
+ View* view_ = nullptr;
+
+ // The path to draw this focus ring around. IsPathUseable(path_) is always
+ // true.
+ SkPath path_;
+
+ // Whether the enclosed View is in an invalid state, which controls whether
+ // the focus ring shows an invalid appearance (usually a different color).
+ bool invalid_ = false;
+
+ // The predicate used to determine whether the parent has focus.
+ ViewPredicate has_focus_predicate_;
DISALLOW_COPY_AND_ASSIGN(FocusRing);
};
diff --git a/chromium/ui/views/controls/glow_hover_controller.cc b/chromium/ui/views/controls/glow_hover_controller.cc
deleted file mode 100644
index ef08308f7e7..00000000000
--- a/chromium/ui/views/controls/glow_hover_controller.cc
+++ /dev/null
@@ -1,88 +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/glow_hover_controller.h"
-
-#include "ui/views/view.h"
-
-namespace views {
-
-// Amount to scale the opacity.
-static const double kSubtleOpacityScale = 0.45;
-static const double kPronouncedOpacityScale = 1.0;
-
-// How long the hover state takes.
-static const int kTrackHoverDurationMs = 400;
-
-GlowHoverController::GlowHoverController(views::View* view)
- : view_(view),
- animation_(this),
- opacity_scale_(kSubtleOpacityScale) {
- animation_.set_delegate(this);
-}
-
-GlowHoverController::~GlowHoverController() {
-}
-
-void GlowHoverController::SetAnimationContainer(
- gfx::AnimationContainer* container) {
- animation_.SetContainer(container);
-}
-
-void GlowHoverController::SetLocation(const gfx::Point& location) {
- location_ = location;
- if (ShouldDraw())
- view_->SchedulePaint();
-}
-
-void GlowHoverController::Show(Style style) {
- switch (style) {
- case SUBTLE:
- opacity_scale_ = kSubtleOpacityScale;
- animation_.SetSlideDuration(kTrackHoverDurationMs);
- animation_.SetTweenType(gfx::Tween::EASE_OUT);
- animation_.Show();
- break;
- case PRONOUNCED:
- opacity_scale_ = kPronouncedOpacityScale;
- // Force the end state to show immediately.
- animation_.Show();
- animation_.End();
- break;
- }
-}
-
-void GlowHoverController::Hide() {
- animation_.SetTweenType(gfx::Tween::EASE_IN);
- animation_.Hide();
-}
-
-void GlowHoverController::HideImmediately() {
- if (ShouldDraw())
- view_->SchedulePaint();
- animation_.Reset();
-}
-
-double GlowHoverController::GetAnimationValue() const {
- return animation_.GetCurrentValue();
-}
-
-SkAlpha GlowHoverController::GetAlpha() const {
- return static_cast<SkAlpha>(animation_.CurrentValueBetween(
- 0, gfx::ToRoundedInt(255 * opacity_scale_)));
-}
-
-bool GlowHoverController::ShouldDraw() const {
- return animation_.IsShowing() || animation_.is_animating();
-}
-
-void GlowHoverController::AnimationEnded(const gfx::Animation* animation) {
- view_->SchedulePaint();
-}
-
-void GlowHoverController::AnimationProgressed(const gfx::Animation* animation) {
- view_->SchedulePaint();
-}
-
-} // namespace views
diff --git a/chromium/ui/views/controls/glow_hover_controller.h b/chromium/ui/views/controls/glow_hover_controller.h
deleted file mode 100644
index 49db0a8c4d9..00000000000
--- a/chromium/ui/views/controls/glow_hover_controller.h
+++ /dev/null
@@ -1,86 +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.
-
-#ifndef UI_VIEWS_CONTROLS_GLOW_HOVER_CONTROLLER_H_
-#define UI_VIEWS_CONTROLS_GLOW_HOVER_CONTROLLER_H_
-
-#include "base/macros.h"
-#include "ui/gfx/animation/animation_delegate.h"
-#include "ui/gfx/animation/slide_animation.h"
-#include "ui/views/views_export.h"
-
-namespace gfx {
-class Point;
-}
-
-namespace views {
-
-class View;
-
-// GlowHoverController is responsible for drawing a hover effect as is used by
-// the tabstrip. Typical usage:
-// OnMouseEntered() -> invoke Show().
-// OnMouseMoved() -> invoke SetLocation().
-// OnMouseExited() -> invoke Hide().
-// OnPaint() -> if ShouldDraw() returns true invoke Draw().
-// Internally GlowHoverController uses an animation to animate the glow and
-// invokes SchedulePaint() back on the View as necessary.
-class VIEWS_EXPORT GlowHoverController : public gfx::AnimationDelegate {
- public:
- enum Style {
- SUBTLE,
- PRONOUNCED
- };
-
- explicit GlowHoverController(views::View* view);
- ~GlowHoverController() override;
-
- // Sets the AnimationContainer used by the animation.
- void SetAnimationContainer(gfx::AnimationContainer* container);
-
- // Sets the location of the hover, relative to the View passed to the
- // constructor.
- void SetLocation(const gfx::Point& location);
-
- const gfx::Point& location() const { return location_; }
-
- // Initiates showing the hover.
- void Show(Style style);
-
- // Hides the hover.
- void Hide();
-
- // Hides the hover immediately.
- void HideImmediately();
-
- // Returns the value of the animation.
- double GetAnimationValue() const;
-
- SkAlpha GetAlpha() const;
-
- // Returns true if there is something to be drawn. Use this instead of
- // invoking Draw() if creating |mask_image| is expensive.
- bool ShouldDraw() const;
-
- // gfx::AnimationDelegate overrides:
- void AnimationEnded(const gfx::Animation* animation) override;
- void AnimationProgressed(const gfx::Animation* animation) override;
-
- private:
- // View we're drawing to.
- views::View* view_;
-
- // Opacity of the glow ramps up over time.
- gfx::SlideAnimation animation_;
-
- // Location of the glow, relative to view.
- gfx::Point location_;
- double opacity_scale_;
-
- DISALLOW_COPY_AND_ASSIGN(GlowHoverController);
-};
-
-} // namespace views
-
-#endif // UI_VIEWS_CONTROLS_GLOW_HOVER_CONTROLLER_H_
diff --git a/chromium/ui/views/controls/image_view.cc b/chromium/ui/views/controls/image_view.cc
index 059a83652f7..5b7f65a5522 100644
--- a/chromium/ui/views/controls/image_view.cc
+++ b/chromium/ui/views/controls/image_view.cc
@@ -29,10 +29,10 @@ void* GetBitmapPixels(const gfx::ImageSkia& img, float image_scale) {
const char ImageView::kViewClassName[] = "ImageView";
ImageView::ImageView()
- : horiz_alignment_(CENTER),
- vert_alignment_(CENTER),
+ : horizontal_alignment_(CENTER),
+ vertical_alignment_(CENTER),
last_paint_scale_(0.f),
- last_painted_bitmap_pixels_(NULL) {}
+ last_painted_bitmap_pixels_(nullptr) {}
ImageView::~ImageView() {}
@@ -40,7 +40,7 @@ void ImageView::SetImage(const gfx::ImageSkia& img) {
if (IsImageEqual(img))
return;
- last_painted_bitmap_pixels_ = NULL;
+ last_painted_bitmap_pixels_ = nullptr;
gfx::Size pref_size(GetPreferredSize());
image_ = img;
if (pref_size != GetPreferredSize())
@@ -93,31 +93,39 @@ gfx::Size ImageView::GetImageSize() const {
gfx::Point ImageView::ComputeImageOrigin(const gfx::Size& image_size) const {
gfx::Insets insets = GetInsets();
- int x;
+ int x = 0;
// In order to properly handle alignment of images in RTL locales, we need
// to flip the meaning of trailing and leading. For example, if the
// horizontal alignment is set to trailing, then we'll use left alignment for
// the image instead of right alignment if the UI layout is RTL.
- Alignment actual_horiz_alignment = horiz_alignment_;
- if (base::i18n::IsRTL() && (horiz_alignment_ != CENTER))
- actual_horiz_alignment = (horiz_alignment_ == LEADING) ? TRAILING : LEADING;
- switch (actual_horiz_alignment) {
- case LEADING: x = insets.left(); break;
- case TRAILING: x = width() - insets.right() - image_size.width(); break;
+ Alignment actual_horizontal_alignment = horizontal_alignment_;
+ if (base::i18n::IsRTL() && (horizontal_alignment_ != CENTER)) {
+ actual_horizontal_alignment =
+ (horizontal_alignment_ == LEADING) ? TRAILING : LEADING;
+ }
+ switch (actual_horizontal_alignment) {
+ case LEADING:
+ x = insets.left();
+ break;
+ case TRAILING:
+ x = width() - insets.right() - image_size.width();
+ break;
case CENTER:
x = (width() - insets.width() - image_size.width()) / 2 + insets.left();
break;
- default: NOTREACHED(); x = 0; break;
}
- int y;
- switch (vert_alignment_) {
- case LEADING: y = insets.top(); break;
- case TRAILING: y = height() - insets.bottom() - image_size.height(); break;
+ int y = 0;
+ switch (vertical_alignment_) {
+ case LEADING:
+ y = insets.top();
+ break;
+ case TRAILING:
+ y = height() - insets.bottom() - image_size.height();
+ break;
case CENTER:
y = (height() - insets.height() - image_size.height()) / 2 + insets.top();
break;
- default: NOTREACHED(); y = 0; break;
}
return gfx::Point(x, y);
@@ -137,26 +145,26 @@ const char* ImageView::GetClassName() const {
return kViewClassName;
}
-void ImageView::SetHorizontalAlignment(Alignment ha) {
- if (ha != horiz_alignment_) {
- horiz_alignment_ = ha;
+void ImageView::SetHorizontalAlignment(Alignment alignment) {
+ if (alignment != horizontal_alignment_) {
+ horizontal_alignment_ = alignment;
SchedulePaint();
}
}
ImageView::Alignment ImageView::GetHorizontalAlignment() const {
- return horiz_alignment_;
+ return horizontal_alignment_;
}
-void ImageView::SetVerticalAlignment(Alignment va) {
- if (va != vert_alignment_) {
- vert_alignment_ = va;
+void ImageView::SetVerticalAlignment(Alignment alignment) {
+ if (alignment != vertical_alignment_) {
+ vertical_alignment_ = alignment;
SchedulePaint();
}
}
ImageView::Alignment ImageView::GetVerticalAlignment() const {
- return vert_alignment_;
+ return vertical_alignment_;
}
void ImageView::SetTooltipText(const base::string16& tooltip) {
@@ -184,7 +192,7 @@ gfx::Size ImageView::CalculatePreferredSize() const {
views::PaintInfo::ScaleType ImageView::GetPaintScaleType() const {
// ImageView contains an image which is rastered at the device scale factor.
- // By default, the paint commands are recorded at a scale factor slighlty
+ // By default, the paint commands are recorded at a scale factor slightly
// different from the device scale factor. Re-rastering the image at this
// paint recording scale will result in a distorted image. Paint recording
// scale might also not be uniform along the x & y axis, thus resulting in
@@ -197,7 +205,7 @@ views::PaintInfo::ScaleType ImageView::GetPaintScaleType() const {
void ImageView::OnPaintImage(gfx::Canvas* canvas) {
last_paint_scale_ = canvas->image_scale();
- last_painted_bitmap_pixels_ = NULL;
+ last_painted_bitmap_pixels_ = nullptr;
if (image_.isNull())
return;
diff --git a/chromium/ui/views/controls/image_view.h b/chromium/ui/views/controls/image_view.h
index 178c50e5087..c19a3c5ea8e 100644
--- a/chromium/ui/views/controls/image_view.h
+++ b/chromium/ui/views/controls/image_view.h
@@ -106,10 +106,10 @@ class VIEWS_EXPORT ImageView : public View {
gfx::ImageSkia image_;
// Horizontal alignment.
- Alignment horiz_alignment_;
+ Alignment horizontal_alignment_;
// Vertical alignment.
- Alignment vert_alignment_;
+ Alignment vertical_alignment_;
// The current tooltip text.
base::string16 tooltip_text_;
diff --git a/chromium/ui/views/controls/menu/menu_config.cc b/chromium/ui/views/controls/menu/menu_config.cc
index 62fbbca1d1d..c398009a47d 100644
--- a/chromium/ui/views/controls/menu/menu_config.cc
+++ b/chromium/ui/views/controls/menu/menu_config.cc
@@ -5,7 +5,9 @@
#include "ui/views/controls/menu/menu_config.h"
#include "base/macros.h"
+#include "ui/views/controls/menu/menu_controller.h"
#include "ui/views/controls/menu/menu_image_util.h"
+#include "ui/views/controls/menu/menu_item_view.h"
#include "ui/views/round_rect_painter.h"
namespace views {
@@ -18,28 +20,36 @@ MenuConfig::MenuConfig()
item_bottom_margin(3),
item_no_icon_top_margin(4),
item_no_icon_bottom_margin(4),
- fixed_text_item_height(0),
- fixed_menu_width(0),
+ minimum_text_item_height(0),
+ minimum_container_item_height(0),
+ minimum_menu_width(0),
item_left_margin(10),
touchable_item_left_margin(16),
label_to_arrow_padding(10),
arrow_to_edge_padding(5),
icon_to_label_padding(10),
- touchable_icon_to_label_padding(22),
+ touchable_icon_to_label_padding(16),
touchable_icon_size(20),
- touchable_icon_color(SkColorSetA(SK_ColorBLACK, 0xDE)),
+ touchable_icon_color(SkColorSetRGB(0x5F, 0x63, 0x60)),
check_width(kMenuCheckSize),
check_height(kMenuCheckSize),
arrow_width(kSubmenuArrowSize),
separator_height(11),
+ double_separator_height(18),
separator_upper_height(3),
separator_lower_height(4),
separator_spacing_height(3),
separator_thickness(1),
+ double_separator_thickness(2),
show_mnemonics(false),
+ use_mnemonics(true),
scroll_arrow_height(3),
label_to_minor_text_padding(10),
item_min_height(0),
+ actionable_submenu_arrow_to_edge_padding(14),
+ actionable_submenu_width(37),
+ actionable_submenu_vertical_separator_height(18),
+ actionable_submenu_vertical_separator_width(1),
show_accelerators(true),
always_use_icon_to_label_padding(false),
align_arrow_and_shortcut(false),
@@ -49,17 +59,44 @@ MenuConfig::MenuConfig()
check_selected_combobox_item(false),
show_delay(400),
corner_radius(0),
+ auxiliary_corner_radius(0),
touchable_corner_radius(8),
touchable_anchor_offset(8),
touchable_menu_height(36),
touchable_menu_width(256),
touchable_menu_shadow_elevation(12),
- vertical_touchable_menu_item_padding(8) {
+ vertical_touchable_menu_item_padding(8),
+ padded_separator_left_margin(64),
+ arrow_key_selection_wraps(true),
+ show_context_menu_accelerators(true) {
Init();
}
MenuConfig::~MenuConfig() {}
+int MenuConfig::CornerRadiusForMenu(const MenuController* controller) const {
+ if (controller && controller->use_touchable_layout())
+ return touchable_corner_radius;
+ if (controller && (controller->is_combobox() || controller->IsContextMenu()))
+ return auxiliary_corner_radius;
+ return corner_radius;
+}
+
+bool MenuConfig::ShouldShowAcceleratorText(const MenuItemView* item,
+ base::string16* text) const {
+ if (!show_accelerators || !item->GetDelegate() || !item->GetCommand())
+ return false;
+ ui::Accelerator accelerator;
+ if (!item->GetDelegate()->GetAccelerator(item->GetCommand(), &accelerator))
+ return false;
+ if (item->GetMenuController() && item->GetMenuController()->IsContextMenu() &&
+ !show_context_menu_accelerators) {
+ return false;
+ }
+ *text = accelerator.GetShortcutText();
+ return true;
+}
+
// static
const MenuConfig& MenuConfig::instance() {
CR_DEFINE_STATIC_LOCAL(MenuConfig, instance, ());
diff --git a/chromium/ui/views/controls/menu/menu_config.h b/chromium/ui/views/controls/menu/menu_config.h
index 3fbb794646a..c07195b1be6 100644
--- a/chromium/ui/views/controls/menu/menu_config.h
+++ b/chromium/ui/views/controls/menu/menu_config.h
@@ -11,6 +11,9 @@
namespace views {
+class MenuController;
+class MenuItemView;
+
// Layout type information for menu items. Use the instance() method to obtain
// the MenuConfig for the current platform.
struct VIEWS_EXPORT MenuConfig {
@@ -19,6 +22,16 @@ struct VIEWS_EXPORT MenuConfig {
static const MenuConfig& instance();
+ // Helper methods to simplify access to MenuConfig:
+ // Returns the appropriate corner radius for the menu controlled by
+ // |controller|, or the default corner radius if |controller| is nullptr.
+ int CornerRadiusForMenu(const MenuController* controller) const;
+
+ // Returns whether |item_view| should show accelerator text. If so, returns
+ // the text to show.
+ bool ShouldShowAcceleratorText(const MenuItemView* item_view,
+ base::string16* text) const;
+
// Font list used by menus.
gfx::FontList font_list;
@@ -44,12 +57,12 @@ struct VIEWS_EXPORT MenuConfig {
int item_no_icon_top_margin;
int item_no_icon_bottom_margin;
- // Fixed dimensions used for entire items. If these are nonzero, they override
- // the vertical margin constants given above - the item's text and icon are
- // vertically centered within these heights.
- int fixed_text_item_height;
- int fixed_container_item_height;
- int fixed_menu_width;
+ // Minimum dimensions used for entire items. If these are nonzero, they
+ // override the vertical margin constants given above - the item's text and
+ // icon are vertically centered within these heights.
+ int minimum_text_item_height;
+ int minimum_container_item_height;
+ int minimum_menu_width;
// Margins between the left of the item and the icon.
int item_left_margin;
@@ -87,6 +100,9 @@ struct VIEWS_EXPORT MenuConfig {
// Height of a normal separator (ui::NORMAL_SEPARATOR).
int separator_height;
+ // Height of a double separator (ui::DOUBLE_SEPARATOR).
+ int double_separator_height;
+
// Height of a ui::UPPER_SEPARATOR.
int separator_upper_height;
@@ -99,9 +115,15 @@ struct VIEWS_EXPORT MenuConfig {
// Thickness of the drawn separator line in pixels.
int separator_thickness;
+ // Thickness of the drawn separator line in pixels for double separator.
+ int double_separator_thickness;
+
// Are mnemonics shown?
bool show_mnemonics;
+ // Are mnemonics used to activate items?
+ bool use_mnemonics;
+
// Height of the scroll arrow.
int scroll_arrow_height;
@@ -112,6 +134,18 @@ struct VIEWS_EXPORT MenuConfig {
// Minimum height of menu item.
int item_min_height;
+ // Edge padding for an actionable submenu arrow.
+ int actionable_submenu_arrow_to_edge_padding;
+
+ // Width of the submenu in an actionable submenu.
+ int actionable_submenu_width;
+
+ // The height of the vertical separator used in an actionable submenu.
+ int actionable_submenu_vertical_separator_height;
+
+ // The width of the vertical separator used in an actionable submenu.
+ int actionable_submenu_vertical_separator_width;
+
// Whether the keyboard accelerators are visible.
bool show_accelerators;
@@ -140,6 +174,10 @@ struct VIEWS_EXPORT MenuConfig {
// Radius of the rounded corners of the menu border. Must be >= 0.
int corner_radius;
+ // Radius of "auxiliary" rounded corners - comboboxes and context menus.
+ // Must be >= 0.
+ int auxiliary_corner_radius;
+
// Radius of the rounded corners of the touchable menu border
int touchable_corner_radius;
@@ -158,6 +196,15 @@ struct VIEWS_EXPORT MenuConfig {
// Vertical padding for touchable menus.
int vertical_touchable_menu_item_padding;
+ // Left margin of padded separator (ui::PADDED_SEPARATOR).
+ int padded_separator_left_margin;
+
+ // Whether arrow keys should wrap around the end of the menu when selecting.
+ bool arrow_key_selection_wraps;
+
+ // Whether to show accelerators in context menus.
+ bool show_context_menu_accelerators;
+
private:
// Configures a MenuConfig as appropriate for the current platform.
void Init();
diff --git a/chromium/ui/views/controls/menu/menu_config_mac.mm b/chromium/ui/views/controls/menu/menu_config_mac.mm
index e950582f43f..7854af9d373 100644
--- a/chromium/ui/views/controls/menu/menu_config_mac.mm
+++ b/chromium/ui/views/controls/menu/menu_config_mac.mm
@@ -17,9 +17,9 @@ void InitMaterialMenuConfig(views::MenuConfig* config) {
config->menu_vertical_border_size = 8;
config->menu_horizontal_border_size = 0;
config->submenu_horizontal_inset = 0;
- config->fixed_text_item_height = 32;
- config->fixed_container_item_height = 48;
- config->fixed_menu_width = 320;
+ config->minimum_text_item_height = 32;
+ config->minimum_container_item_height = 48;
+ config->minimum_menu_width = 320;
config->item_left_margin = 8;
config->label_to_arrow_padding = 0;
config->arrow_to_edge_padding = 16;
@@ -28,12 +28,16 @@ void InitMaterialMenuConfig(views::MenuConfig* config) {
config->check_height = 16;
config->arrow_width = 8;
config->separator_height = 17;
+ config->separator_lower_height = 9;
+ config->separator_upper_height = 9;
+ config->separator_spacing_height = 9;
config->separator_thickness = 1;
config->label_to_minor_text_padding = 8;
config->align_arrow_and_shortcut = true;
config->use_outer_border = false;
config->icons_in_label = true;
config->corner_radius = 8;
+ config->auxiliary_corner_radius = 4;
}
} // namespace
@@ -43,6 +47,9 @@ namespace views {
void MenuConfig::Init() {
font_list = gfx::FontList(gfx::Font([NSFont menuFontOfSize:0.0]));
check_selected_combobox_item = true;
+ arrow_key_selection_wraps = false;
+ use_mnemonics = false;
+ show_context_menu_accelerators = false;
if (ui::MaterialDesignController::IsSecondaryUiMaterial())
InitMaterialMenuConfig(this);
}
diff --git a/chromium/ui/views/controls/menu/menu_controller.cc b/chromium/ui/views/controls/menu/menu_controller.cc
index 46713d7170a..00590f09d91 100644
--- a/chromium/ui/views/controls/menu/menu_controller.cc
+++ b/chromium/ui/views/controls/menu/menu_controller.cc
@@ -165,7 +165,7 @@ View* GetNextFocusableView(View* ancestor, View* start_at, bool forward) {
return NULL;
}
-#if defined(OS_WIN) || defined(OS_CHROMEOS)
+#if defined(OS_WIN)
// Determines the correct coordinates and window to repost |event| to, if it is
// a mouse or touch event.
static void RepostEventImpl(const ui::LocatedEvent* event,
@@ -252,7 +252,7 @@ static void RepostEventImpl(const ui::LocatedEvent* event,
PostMessage(target_window, event_type, target, window_coords);
return;
}
-#endif
+#endif // defined(OS_WIN)
#if defined(USE_AURA)
if (!window)
@@ -276,7 +276,7 @@ static void RepostEventImpl(const ui::LocatedEvent* event,
root->GetHost()->dispatcher()->RepostEvent(located_event.get());
#endif // defined(USE_AURA)
}
-#endif // defined(OS_WIN) || defined(OS_CHROMEOS)
+#endif // defined(OS_WIN)
} // namespace
@@ -442,9 +442,8 @@ void MenuController::Run(Widget* parent,
// 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.
- DCHECK(blocking_run_);
+ // Nesting (context menus) is not used for drag and drop.
+ DCHECK(!for_drop_);
state_.hot_button = hot_button_;
hot_button_ = nullptr;
@@ -477,7 +476,10 @@ void MenuController::Run(Widget* parent,
// Set the selection, which opens the initial menu.
SetSelection(root, SELECTION_OPEN_SUBMENU | SELECTION_UPDATE_IMMEDIATELY);
- if (!blocking_run_) {
+ if (button)
+ pressed_lock_ = std::make_unique<MenuButton::PressedLock>(button);
+
+ if (for_drop_) {
if (!is_nested_drag) {
// Start the timer to hide the menu. This is needed as we get no
// notification when the drag has finished.
@@ -486,9 +488,6 @@ void MenuController::Run(Widget* parent,
return;
}
- if (button)
- pressed_lock_.reset(new MenuButton::PressedLock(button));
-
// Make sure Chrome doesn't attempt to shut down while the menu is showing.
if (ViewsDelegate::GetInstance())
ViewsDelegate::GetInstance()->AddRef();
@@ -516,7 +515,7 @@ void MenuController::Cancel(ExitType type) {
// Hide windows immediately.
SetSelection(NULL, SELECTION_UPDATE_IMMEDIATELY | SELECTION_EXIT);
- if (!blocking_run_) {
+ if (for_drop_) {
// If we didn't block the caller we need to notify the menu, which
// triggers deleting us.
DCHECK(selected);
@@ -550,6 +549,10 @@ void MenuController::AddNestedDelegate(
delegate_ = delegate;
}
+bool MenuController::IsContextMenu() const {
+ return state_.context_menu;
+}
+
bool MenuController::OnMousePressed(SubmenuView* source,
const ui::MouseEvent& event) {
// We should either have no current_mouse_event_target_, or should have a
@@ -607,7 +610,7 @@ bool MenuController::OnMouseDragged(SubmenuView* source,
MenuPart part = GetMenuPart(source, event.location());
UpdateScrolling(part);
- if (!blocking_run_)
+ if (for_drop_)
return false;
if (possible_drag_) {
@@ -664,12 +667,11 @@ void MenuController::OnMouseReleased(SubmenuView* source,
return;
}
- if (!blocking_run_)
+ if (for_drop_)
return;
DCHECK(state_.item);
possible_drag_ = false;
- DCHECK(blocking_run_);
MenuPart part = GetMenuPart(source, event.location());
if (event.IsRightMouseButton() && part.type == MenuPart::MENU_ITEM) {
MenuItemView* menu = part.menu;
@@ -694,7 +696,7 @@ void MenuController::OnMouseReleased(SubmenuView* source,
// for selected folder menu items. If it's only a left click, show the
// contents of the folder.
if (!part.is_scroll() && part.menu &&
- !(part.menu->HasSubmenu() &&
+ !(part.should_submenu_show && part.menu->HasSubmenu() &&
(event.flags() & ui::EF_LEFT_MOUSE_BUTTON))) {
if (active_mouse_view_tracker_->view()) {
SendMouseReleaseToActiveView(source, event);
@@ -819,7 +821,7 @@ void MenuController::OnGestureEvent(SubmenuView* source,
}
} else if (event->type() == ui::ET_GESTURE_TAP) {
if (!part.is_scroll() && part.menu &&
- !(part.menu->HasSubmenu())) {
+ !(part.should_submenu_show && part.menu->HasSubmenu())) {
if (part.menu->GetDelegate()->IsTriggerableEvent(
part.menu, *event)) {
item_selected_by_touch_ = true;
@@ -1004,7 +1006,7 @@ int MenuController::OnPerformDrop(SubmenuView* source,
if (drop_target->id() == MenuItemView::kEmptyMenuItemViewID)
drop_target = drop_target->GetParentMenuItem();
- if (!IsBlockingRun()) {
+ if (for_drop_) {
delegate_->OnMenuClosed(
internal::MenuControllerDelegate::DONT_NOTIFY_DELEGATE,
item->GetRootMenuItem(), accept_event_flags_);
@@ -1165,7 +1167,14 @@ void MenuController::SetSelection(MenuItemView* menu_item,
size_t current_size = current_path.size();
size_t new_size = new_path.size();
- bool pending_item_changed = pending_state_.item != menu_item;
+ // ACTIONABLE_SUBMENUs can change without changing the pending item, this
+ // occurs when selection moves from the COMMAND area to the SUBMENU area of
+ // the ACTIONABLE_SUBMENU.
+ const bool pending_item_changed =
+ pending_state_.item != menu_item ||
+ pending_state_.submenu_open !=
+ !!(selection_types & SELECTION_OPEN_SUBMENU);
+
if (pending_item_changed && pending_state_.item)
SetHotTrackedButton(nullptr);
@@ -1174,7 +1183,8 @@ void MenuController::SetSelection(MenuItemView* menu_item,
current_path.empty() ? NULL : current_path.front()->GetDelegate();
for (size_t i = paths_differ_at; i < current_size; ++i) {
if (current_delegate &&
- current_path[i]->GetType() == MenuItemView::SUBMENU) {
+ (current_path[i]->GetType() == MenuItemView::SUBMENU ||
+ current_path[i]->GetType() == MenuItemView::ACTIONABLE_SUBMENU)) {
current_delegate->WillHideMenu(current_path[i]);
}
current_path[i]->SetSelected(false);
@@ -1184,6 +1194,14 @@ void MenuController::SetSelection(MenuItemView* menu_item,
for (size_t i = paths_differ_at; i < new_size; ++i) {
new_path[i]->ScrollRectToVisible(new_path[i]->GetLocalBounds());
new_path[i]->SetSelected(true);
+ if (new_path[i]->GetType() == MenuItemView::ACTIONABLE_SUBMENU) {
+ new_path[i]->SetSelectionOfActionableSubmenu(
+ (selection_types & SELECTION_OPEN_SUBMENU) != 0);
+ }
+ }
+ if (menu_item && menu_item->GetType() == MenuItemView::ACTIONABLE_SUBMENU) {
+ menu_item->SetSelectionOfActionableSubmenu(
+ (selection_types & SELECTION_OPEN_SUBMENU) != 0);
}
if (menu_item && menu_item->GetDelegate())
@@ -1206,16 +1224,17 @@ void MenuController::SetSelection(MenuItemView* menu_item,
StartShowTimer();
// Notify an accessibility focus event on all menu items except for the root.
- if (menu_item &&
- (MenuDepth(menu_item) != 1 ||
- menu_item->GetType() != MenuItemView::SUBMENU)) {
+ if (menu_item && (MenuDepth(menu_item) != 1 ||
+ menu_item->GetType() != MenuItemView::SUBMENU ||
+ (menu_item->GetType() == MenuItemView::ACTIONABLE_SUBMENU &&
+ (selection_types & SELECTION_OPEN_SUBMENU) == 0))) {
menu_item->NotifyAccessibilityEvent(ax::mojom::Event::kSelection, true);
}
}
void MenuController::SetSelectionOnPointerDown(SubmenuView* source,
const ui::LocatedEvent* event) {
- if (!blocking_run_)
+ if (for_drop_)
return;
DCHECK(!active_mouse_view_tracker_->view());
@@ -1255,7 +1274,7 @@ void MenuController::SetSelectionOnPointerDown(SubmenuView* source,
possible_drag_ = true;
press_pt_ = event->location();
}
- if (part.menu->HasSubmenu())
+ if (part.menu->HasSubmenu() && part.should_submenu_show)
selection_types |= SELECTION_OPEN_SUBMENU;
}
SetSelection(part.menu, selection_types);
@@ -1298,7 +1317,7 @@ void MenuController::StartDrag(SubmenuView* source,
void MenuController::OnKeyDown(ui::KeyboardCode key_code) {
// Do not process while performing drag-and-drop
- if (!blocking_run_)
+ if (for_drop_)
return;
switch (key_code) {
@@ -1405,9 +1424,9 @@ void MenuController::OnKeyDown(ui::KeyboardCode key_code) {
}
}
-MenuController::MenuController(bool blocking,
+MenuController::MenuController(bool for_drop,
internal::MenuControllerDelegate* delegate)
- : blocking_run_(blocking),
+ : for_drop_(for_drop),
active_mouse_view_tracker_(std::make_unique<ViewTracker>()),
delegate_(delegate) {
delegate_stack_.push_back(delegate_);
@@ -1445,11 +1464,6 @@ void MenuController::UpdateInitialLocation(const gfx::Rect& bounds,
bool context_menu) {
pending_state_.context_menu = context_menu;
pending_state_.initial_bounds = bounds;
- if (bounds.height() > 1) {
- // Inset the bounds slightly, otherwise drag coordinates don't line up
- // nicely and menus close prematurely.
- pending_state_.initial_bounds.Inset(0, 1);
- }
// Reverse anchor position for RTL languages.
if (base::i18n::IsRTL() &&
@@ -1491,7 +1505,7 @@ void MenuController::Accept(MenuItemView* item, int event_flags) {
}
void MenuController::ReallyAccept(MenuItemView* item, int event_flags) {
- DCHECK(IsBlockingRun());
+ DCHECK(!for_drop_);
result_ = item;
#if defined(OS_MACOSX)
// Reset the closure animation since it's now finished - this also unblocks
@@ -1562,9 +1576,8 @@ bool MenuController::ShowSiblingMenu(SubmenuView* source,
// It is currently not possible to show a submenu recursively in a bubble.
DCHECK(!MenuItemView::IsBubble(anchor));
- // Subtract 1 from the height to make the popup flush with the button border.
UpdateInitialLocation(gfx::Rect(screen_menu_loc.x(), screen_menu_loc.y(),
- button->width(), button->height() - 1),
+ button->width(), button->height()),
anchor, state_.context_menu);
alt_menu->PrepareForRun(
false, has_mnemonics,
@@ -1699,11 +1712,19 @@ bool MenuController::GetMenuPartByScreenCoordinateImpl(
part->menu = GetMenuItemAt(menu, menu_loc.x(), menu_loc.y());
part->type = MenuPart::MENU_ITEM;
part->submenu = menu;
+ part->should_submenu_show =
+ part->submenu && part->menu &&
+ (part->menu->GetType() == MenuItemView::SUBMENU ||
+ IsLocationOverSubmenuAreaOfActionableSubmenu(part->menu, screen_loc));
if (!part->menu)
part->parent = menu->GetMenuItem();
return true;
}
+ // Return false for points on touchable menu shadows, to search parent menus.
+ if (use_touchable_layout_)
+ return false;
+
// While the mouse isn't over a menu item or the scroll buttons of menu, it
// is contained by menu and so we return true. If we didn't return true other
// menus would be searched, even though they are likely obscured by us.
@@ -1735,7 +1756,20 @@ bool MenuController::DoesSubmenuContainLocation(SubmenuView* submenu,
gfx::Point view_loc = screen_loc;
View::ConvertPointFromScreen(submenu, &view_loc);
gfx::Rect vis_rect = submenu->GetVisibleBounds();
- return vis_rect.Contains(view_loc.x(), view_loc.y());
+ return vis_rect.Contains(view_loc);
+}
+
+bool MenuController::IsLocationOverSubmenuAreaOfActionableSubmenu(
+ MenuItemView* item,
+ const gfx::Point& screen_loc) const {
+ if (!item || item->GetType() != MenuItemView::ACTIONABLE_SUBMENU)
+ return false;
+
+ gfx::Point view_loc = screen_loc;
+ View::ConvertPointFromScreen(item, &view_loc);
+ if (base::i18n::IsRTL())
+ view_loc.set_x(item->GetMirroredXInView(view_loc.x()));
+ return item->GetSubmenuAreaOfActionableSubmenu().Contains(view_loc);
}
void MenuController::CommitPendingSelection() {
@@ -1847,7 +1881,7 @@ void MenuController::OpenMenuImpl(MenuItemView* item, bool show) {
CalculateBubbleMenuBounds(item, prefer_leading, &resulting_direction) :
CalculateMenuBounds(item, prefer_leading, &resulting_direction);
state_.open_leading.push_back(resulting_direction);
- bool do_capture = (!did_capture_ && blocking_run_);
+ bool do_capture = (!did_capture_ && !for_drop_);
showing_submenu_ = true;
if (show) {
// Menus are the only place using kGroupingPropertyKey, so any value (other
@@ -2127,7 +2161,6 @@ gfx::Rect MenuController::CalculateBubbleMenuBounds(MenuItemView* item,
bool prefer_leading,
bool* is_leading) {
DCHECK(item);
- DCHECK(!item->GetParentMenuItem());
// Assume we can honor prefer_leading.
*is_leading = prefer_leading;
@@ -2136,103 +2169,153 @@ gfx::Rect MenuController::CalculateBubbleMenuBounds(MenuItemView* item,
DCHECK(submenu);
gfx::Size pref = submenu->GetScrollViewContainer()->GetPreferredSize();
- const gfx::Rect& owner_bounds = pending_state_.initial_bounds;
-
- // First the size gets reduced to the possible space.
- if (!state_.monitor_bounds.IsEmpty()) {
- int max_width = state_.monitor_bounds.width();
- int max_height = state_.monitor_bounds.height();
- // In case of bubbles, the maximum width is limited by the space
- // between the display corner and the target area + the tip size.
- if (state_.anchor == MENU_ANCHOR_BUBBLE_LEFT) {
- max_width = owner_bounds.x() - state_.monitor_bounds.x() +
- kBubbleTipSizeLeftRight;
- } else if (state_.anchor == MENU_ANCHOR_BUBBLE_RIGHT) {
- max_width = state_.monitor_bounds.right() - owner_bounds.right() +
- kBubbleTipSizeLeftRight;
- } else if (state_.anchor == MENU_ANCHOR_BUBBLE_ABOVE) {
- max_height = owner_bounds.y() - state_.monitor_bounds.y() +
- kBubbleTipSizeTopBottom;
- } else if (state_.anchor == MENU_ANCHOR_BUBBLE_BELOW) {
- max_height = state_.monitor_bounds.bottom() - owner_bounds.bottom() +
- kBubbleTipSizeTopBottom;
- }
- // The space for the menu to cover should never get empty.
- DCHECK_GE(max_width, kBubbleTipSizeLeftRight);
- DCHECK_GE(max_height, kBubbleTipSizeTopBottom);
- pref.set_width(std::min(pref.width(), max_width));
- pref.set_height(std::min(pref.height(), max_height));
- }
- // Also make sure that the menu does not go too wide.
- pref.set_width(std::min(pref.width(),
- item->GetDelegate()->GetMaxWidthForMenu(item)));
-
+ int x = 0;
+ int y = 0;
const MenuConfig& menu_config = MenuConfig::instance();
// Shadow insets are built into MenuScrollView's preferred size so it must be
// compensated for when determining the bounds of touchable menus.
- gfx::Insets shadow_insets = BubbleBorder::GetBorderAndShadowInsets(
- menu_config.touchable_menu_shadow_elevation);
+ const gfx::Insets border_and_shadow_insets =
+ BubbleBorder::GetBorderAndShadowInsets(
+ menu_config.touchable_menu_shadow_elevation);
- int x, y;
- if (state_.anchor == MENU_ANCHOR_BUBBLE_ABOVE ||
- state_.anchor == MENU_ANCHOR_BUBBLE_BELOW) {
- if (state_.anchor == MENU_ANCHOR_BUBBLE_ABOVE)
- y = owner_bounds.y() - pref.height() + kBubbleTipSizeTopBottom;
- else
- y = owner_bounds.bottom() - kBubbleTipSizeTopBottom;
-
- x = owner_bounds.CenterPoint().x() - pref.width() / 2;
- int x_old = x;
- if (x < state_.monitor_bounds.x()) {
- x = state_.monitor_bounds.x();
- } else if (x + pref.width() > state_.monitor_bounds.right()) {
- x = state_.monitor_bounds.right() - pref.width();
+ if (!item->GetParentMenuItem()) {
+ // This is a top-level menu, position it relative to the anchor bounds.
+ const gfx::Rect& owner_bounds = pending_state_.initial_bounds;
+
+ // First the size gets reduced to the possible space.
+ if (!state_.monitor_bounds.IsEmpty()) {
+ int max_width = state_.monitor_bounds.width();
+ int max_height = state_.monitor_bounds.height();
+ // In case of bubbles, the maximum width is limited by the space
+ // between the display corner and the target area + the tip size.
+ if (state_.anchor == MENU_ANCHOR_BUBBLE_LEFT) {
+ max_width = owner_bounds.x() - state_.monitor_bounds.x() +
+ kBubbleTipSizeLeftRight;
+ } else if (state_.anchor == MENU_ANCHOR_BUBBLE_RIGHT) {
+ max_width = state_.monitor_bounds.right() - owner_bounds.right() +
+ kBubbleTipSizeLeftRight;
+ } else if (state_.anchor == MENU_ANCHOR_BUBBLE_ABOVE) {
+ max_height = owner_bounds.y() - state_.monitor_bounds.y() +
+ kBubbleTipSizeTopBottom;
+ } else if (state_.anchor == MENU_ANCHOR_BUBBLE_BELOW) {
+ max_height = state_.monitor_bounds.bottom() - owner_bounds.bottom() +
+ kBubbleTipSizeTopBottom;
+ }
+ // The menu should always have a non-empty available area.
+ DCHECK_GE(max_width, kBubbleTipSizeLeftRight);
+ DCHECK_GE(max_height, kBubbleTipSizeTopBottom);
+ pref.set_width(std::min(pref.width(), max_width));
+ pref.set_height(std::min(pref.height(), max_height));
}
- submenu->GetScrollViewContainer()->SetBubbleArrowOffset(
- pref.width() / 2 - x + x_old);
- } else if (state_.anchor == MENU_ANCHOR_BUBBLE_TOUCHABLE_ABOVE) {
- // Align the left edges of the menu and anchor, and the bottom of the menu
- // with the top of the anchor.
- x = owner_bounds.origin().x() - shadow_insets.left();
- y = owner_bounds.origin().y() - pref.height() + shadow_insets.bottom() -
- menu_config.touchable_anchor_offset;
- // Align the right of the container with the right of the app icon.
- if (x + pref.width() > state_.monitor_bounds.width())
- x = owner_bounds.right() - pref.width() + shadow_insets.right();
- // Align the top of the menu with the bottom of the anchor.
- if (y < 0) {
- y = owner_bounds.bottom() - shadow_insets.top() +
+ // Respect the delegate's maximum width.
+ pref.set_width(
+ std::min(pref.width(), item->GetDelegate()->GetMaxWidthForMenu(item)));
+
+ if (state_.anchor == MENU_ANCHOR_BUBBLE_ABOVE ||
+ state_.anchor == MENU_ANCHOR_BUBBLE_BELOW) {
+ if (state_.anchor == MENU_ANCHOR_BUBBLE_ABOVE)
+ y = owner_bounds.y() - pref.height() + kBubbleTipSizeTopBottom;
+ else
+ y = owner_bounds.bottom() - kBubbleTipSizeTopBottom;
+
+ x = owner_bounds.CenterPoint().x() - pref.width() / 2;
+ int x_old = x;
+ if (x < state_.monitor_bounds.x())
+ x = state_.monitor_bounds.x();
+ else if (x + pref.width() > state_.monitor_bounds.right())
+ x = state_.monitor_bounds.right() - pref.width();
+ submenu->GetScrollViewContainer()->SetBubbleArrowOffset(pref.width() / 2 -
+ x + x_old);
+ } else if (state_.anchor == MENU_ANCHOR_BUBBLE_TOUCHABLE_ABOVE) {
+ // Align the left edges of the menu and anchor, and the bottom of the menu
+ // with the top of the anchor.
+ x = owner_bounds.origin().x() - border_and_shadow_insets.left();
+ y = owner_bounds.origin().y() - pref.height() +
+ border_and_shadow_insets.bottom() -
menu_config.touchable_anchor_offset;
- }
- } else if (state_.anchor == MENU_ANCHOR_BUBBLE_TOUCHABLE_LEFT) {
- // Align the right of the menu with the left of the anchor, and the top of
- // the menu with the top of the anchor.
- x = owner_bounds.origin().x() - pref.width() + shadow_insets.right() -
- menu_config.touchable_anchor_offset;
- y = owner_bounds.origin().y() - shadow_insets.top();
- // Align the left of the menu with the right of the anchor.
- if (x < 0) {
- x = owner_bounds.right() + shadow_insets.left() +
+ // Align the right of the container with the right of the anchor.
+ if (x + pref.width() > state_.monitor_bounds.width()) {
+ x = owner_bounds.right() - pref.width() +
+ border_and_shadow_insets.right();
+ }
+ // Align the top of the menu with the bottom of the anchor.
+ if (y < 0) {
+ y = owner_bounds.bottom() - border_and_shadow_insets.top() +
+ menu_config.touchable_anchor_offset;
+ }
+ } else if (state_.anchor == MENU_ANCHOR_BUBBLE_TOUCHABLE_LEFT) {
+ // Align the right of the menu with the left of the anchor, and the top of
+ // the menu with the top of the anchor.
+ x = owner_bounds.origin().x() - pref.width() +
+ border_and_shadow_insets.right() -
menu_config.touchable_anchor_offset;
+ y = owner_bounds.origin().y() - border_and_shadow_insets.top();
+ // Align the left of the menu with the right of the anchor.
+ if (x < 0) {
+ x = owner_bounds.right() - border_and_shadow_insets.left() +
+ menu_config.touchable_anchor_offset;
+ }
+ // Align the bottom of the menu to the bottom of the anchor.
+ if (y + pref.height() > state_.monitor_bounds.height()) {
+ y = owner_bounds.bottom() - pref.height() +
+ border_and_shadow_insets.bottom();
+ }
+ } else {
+ if (state_.anchor == MENU_ANCHOR_BUBBLE_RIGHT)
+ x = owner_bounds.right() - kBubbleTipSizeLeftRight;
+ else
+ x = owner_bounds.x() - pref.width() + kBubbleTipSizeLeftRight;
+
+ y = owner_bounds.CenterPoint().y() - pref.height() / 2;
+ int y_old = y;
+ if (y < state_.monitor_bounds.y())
+ y = state_.monitor_bounds.y();
+ else if (y + pref.height() > state_.monitor_bounds.bottom())
+ y = state_.monitor_bounds.bottom() - pref.height();
+ submenu->GetScrollViewContainer()->SetBubbleArrowOffset(
+ pref.height() / 2 - y + y_old);
}
- // Align the bottom of the menu to the bottom of the anchor.
- if (y + pref.height() > state_.monitor_bounds.height())
- y = owner_bounds.bottom() - pref.height() + shadow_insets.bottom();
} else {
- if (state_.anchor == MENU_ANCHOR_BUBBLE_RIGHT)
- x = owner_bounds.right() - kBubbleTipSizeLeftRight;
- else
- x = owner_bounds.x() - pref.width() + kBubbleTipSizeLeftRight;
+ if (!use_touchable_layout_) {
+ NOTIMPLEMENTED()
+ << "Nested bubble menus are only implemented for touchable menus.";
+ }
- y = owner_bounds.CenterPoint().y() - pref.height() / 2;
- int y_old = y;
- if (y < state_.monitor_bounds.y()) {
- y = state_.monitor_bounds.y();
- } else if (y + pref.height() > state_.monitor_bounds.bottom()) {
- y = state_.monitor_bounds.bottom() - pref.height();
+ // This is a sub-menu, position it relative to the parent menu.
+ const gfx::Rect item_bounds = item->GetBoundsInScreen();
+ // If the layout is RTL, then a 'leading' menu is positioned to the left of
+ // the parent menu item and not to the right.
+ const bool layout_is_rtl = base::i18n::IsRTL();
+ const bool create_on_the_right = (prefer_leading && !layout_is_rtl) ||
+ (!prefer_leading && layout_is_rtl);
+ if (create_on_the_right) {
+ x = item_bounds.right() - border_and_shadow_insets.left();
+ if (state_.monitor_bounds.width() != 0 &&
+ (x + menu_config.touchable_menu_width -
+ border_and_shadow_insets.right() >
+ state_.monitor_bounds.right())) {
+ *is_leading = prefer_leading;
+ x = item_bounds.x() - menu_config.touchable_menu_width -
+ border_and_shadow_insets.right();
+ }
+ } else {
+ x = item_bounds.x() - menu_config.touchable_menu_width -
+ border_and_shadow_insets.right();
+ if (state_.monitor_bounds.width() != 0 && x < state_.monitor_bounds.x()) {
+ *is_leading = !prefer_leading;
+ x = item_bounds.x() + menu_config.touchable_menu_width -
+ border_and_shadow_insets.left();
+ }
}
- submenu->GetScrollViewContainer()->SetBubbleArrowOffset(
- pref.height() / 2 - y + y_old);
+ y = item_bounds.y() - border_and_shadow_insets.top() -
+ menu_config.vertical_touchable_menu_item_padding;
+ if (y + pref.height() - border_and_shadow_insets.bottom() >
+ state_.monitor_bounds.bottom()) {
+ y = state_.monitor_bounds.bottom() - pref.height() +
+ border_and_shadow_insets.top();
+ }
+ if (y < state_.monitor_bounds.y())
+ y = state_.monitor_bounds.y() - border_and_shadow_insets.top();
}
return gfx::Rect(x, y, pref.width(), pref.height());
}
@@ -2309,14 +2392,20 @@ MenuItemView* MenuController::FindNextSelectableMenuItem(
// Loop through the menu items skipping any invisible menus. The loop stops
// when we wrap or find a visible and enabled child.
do {
+ if (!MenuConfig::instance().arrow_key_selection_wraps) {
+ if (index == 0 && direction == INCREMENT_SELECTION_UP)
+ return nullptr;
+ if (index == parent_count - 1 && direction == INCREMENT_SELECTION_DOWN)
+ return nullptr;
+ }
index = (index + delta + parent_count) % parent_count;
if (index == stop_index && !include_all_items)
- return NULL;
+ return nullptr;
MenuItemView* child = parent->GetSubmenu()->GetMenuItemAt(index);
if (child->visible() && child->enabled())
return child;
} while (index != stop_index);
- return NULL;
+ return nullptr;
}
void MenuController::OpenSubmenuChangeSelectionIfCan() {
@@ -2398,8 +2487,8 @@ void MenuController::AcceptOrSelect(MenuItemView* parent,
}
void MenuController::SelectByChar(base::char16 character) {
- // Do not process while performing drag-and-drop
- if (!blocking_run_)
+ // Do not process while performing drag-and-drop.
+ if (for_drop_)
return;
if (!character)
return;
@@ -2483,14 +2572,6 @@ void MenuController::RepostEventAndCancel(SubmenuView* source,
exit_type = EXIT_OUTERMOST;
}
Cancel(exit_type);
-
-#if defined(OS_CHROMEOS)
- // We're going to exit the menu and want to repost the event so that is
- // is handled normally after the context menu has exited. We call
- // RepostEvent after Cancel so that event capture has been released so
- // that finding the event target is unaffected by the current capture.
- RepostEventImpl(event, screen_loc, native_view, window);
-#endif
}
void MenuController::SetDropMenuItem(
@@ -2723,14 +2804,15 @@ void MenuController::HandleMouseLocation(SubmenuView* source,
UpdateScrolling(part);
- if (!blocking_run_)
+ if (for_drop_)
return;
if (part.type == MenuPart::NONE && ShowSiblingMenu(source, mouse_location))
return;
if (part.type == MenuPart::MENU_ITEM && part.menu) {
- SetSelection(part.menu, SELECTION_OPEN_SUBMENU);
+ SetSelection(part.menu, part.should_submenu_show ? SELECTION_OPEN_SUBMENU
+ : SELECTION_DEFAULT);
} else if (!part.is_scroll() && pending_state_.item &&
pending_state_.item->GetParentMenuItem() &&
!pending_state_.item->SubmenuIsShowing()) {
diff --git a/chromium/ui/views/controls/menu/menu_controller.h b/chromium/ui/views/controls/menu/menu_controller.h
index b2080448720..67b8849bca5 100644
--- a/chromium/ui/views/controls/menu/menu_controller.h
+++ b/chromium/ui/views/controls/menu/menu_controller.h
@@ -95,8 +95,7 @@ class VIEWS_EXPORT MenuController
bool context_menu,
bool is_nested_drag);
- // Whether or not Run blocks.
- bool IsBlockingRun() const { return blocking_run_; }
+ bool for_drop() const { return for_drop_; }
bool in_nested_run() const { return !menu_stack_.empty(); }
@@ -144,6 +143,8 @@ class VIEWS_EXPORT MenuController
void set_is_combobox(bool is_combobox) { is_combobox_ = is_combobox; }
bool is_combobox() const { return is_combobox_; }
+ bool IsContextMenu() const;
+
// Various events, forwarded from the submenu.
//
// NOTE: the coordinates of the events are in that of the
@@ -294,13 +295,11 @@ class VIEWS_EXPORT MenuController
SCROLL_DOWN
};
- MenuPart() : type(NONE), menu(NULL), parent(NULL), submenu(NULL) {}
-
// Convenience for testing type == SCROLL_DOWN or type == SCROLL_UP.
bool is_scroll() const { return type == SCROLL_DOWN || type == SCROLL_UP; }
// Type of part.
- Type type;
+ Type type = NONE;
// If type is MENU_ITEM, this is the menu item the mouse is over, otherwise
// this is NULL.
@@ -308,14 +307,17 @@ class VIEWS_EXPORT MenuController
// but is over a menu (for example, the mouse is over a separator or
// empty menu), this is NULL and parent is the menu the mouse was
// clicked on.
- MenuItemView* menu;
+ MenuItemView* menu = nullptr;
// If type is MENU_ITEM but the mouse is not over a menu item this is the
// parent of the menu item the user clicked on. Otherwise this is NULL.
- MenuItemView* parent;
+ MenuItemView* parent = nullptr;
// This is the submenu the mouse is over.
- SubmenuView* submenu;
+ SubmenuView* submenu = nullptr;
+
+ // Whether the controller should apply SELECTION_OPEN_SUBMENU to this item.
+ bool should_submenu_show = false;
};
// Sets the selection to |menu_item|. A value of NULL unselects
@@ -334,10 +336,8 @@ class VIEWS_EXPORT MenuController
// Key processing.
void OnKeyDown(ui::KeyboardCode key_code);
- // Creates a MenuController. If |blocking| is true a nested run loop is
- // started in |Run|.
- MenuController(bool blocking,
- internal::MenuControllerDelegate* delegate);
+ // Creates a MenuController. See |for_drop_| member for details on |for_drop|.
+ MenuController(bool for_drop, internal::MenuControllerDelegate* delegate);
~MenuController() override;
@@ -419,6 +419,11 @@ class VIEWS_EXPORT MenuController
bool DoesSubmenuContainLocation(SubmenuView* submenu,
const gfx::Point& screen_loc);
+ // Returns whether the location is over the ACTIONABLE_SUBMENU's submenu area.
+ bool IsLocationOverSubmenuAreaOfActionableSubmenu(
+ MenuItemView* item,
+ const gfx::Point& screen_loc) const;
+
// Opens/Closes the necessary menus such that state_ matches that of
// pending_state_. This is invoked if submenus are not opened immediately,
// but after a delay.
@@ -576,11 +581,10 @@ class VIEWS_EXPORT MenuController
// The active instance.
static MenuController* active_instance_;
- // If true, Run blocks. If false, Run doesn't block and this is used for
- // drag and drop. Note that the semantics for drag and drop are slightly
- // different: cancel timer is kicked off any time the drag moves outside the
- // menu, mouse events do nothing...
- bool blocking_run_;
+ // If true the menu is shown for a drag and drop. Note that the semantics for
+ // drag and drop are slightly different: cancel timer is kicked off any time
+ // the drag moves outside the menu, mouse events do nothing...
+ const bool for_drop_;
// If true, we're showing.
bool showing_ = false;
diff --git a/chromium/ui/views/controls/menu/menu_controller_unittest.cc b/chromium/ui/views/controls/menu/menu_controller_unittest.cc
index 22fa8335af1..2d1c138bdc0 100644
--- a/chromium/ui/views/controls/menu/menu_controller_unittest.cc
+++ b/chromium/ui/views/controls/menu/menu_controller_unittest.cc
@@ -294,6 +294,11 @@ class MenuControllerTest : public ViewsTestBase {
event_generator_->PressKey(key_code, 0);
}
+ void DispatchKey(ui::KeyboardCode key_code) {
+ ui::KeyEvent event(ui::EventType::ET_KEY_PRESSED, key_code, 0);
+ menu_controller_->OnWillDispatchKeyEvent(&event);
+ }
+
#if defined(USE_AURA)
// Verifies that a non-nested menu fully closes when receiving an escape key.
void TestAsyncEscapeKey() {
@@ -330,8 +335,9 @@ class MenuControllerTest : public ViewsTestBase {
void TestMenuControllerReplacementDuringDrag() {
DestroyMenuController();
menu_item()->GetSubmenu()->Close();
+ const bool for_drop = false;
menu_controller_ =
- new MenuController(true, menu_controller_delegate_.get());
+ new MenuController(for_drop, menu_controller_delegate_.get());
menu_controller_->owner_ = owner_.get();
menu_controller_->showing_ = true;
}
@@ -485,7 +491,7 @@ class MenuControllerTest : public ViewsTestBase {
}
MenuController* menu_controller() { return menu_controller_; }
const MenuItemView* pending_state_item() const {
- return menu_controller_->pending_state_.item;
+ return menu_controller_->pending_state_.item;
}
MenuController::ExitType menu_exit_type() const {
return menu_controller_->exit_type_;
@@ -533,6 +539,10 @@ class MenuControllerTest : public ViewsTestBase {
int CountOwnerOnGestureEvent() const { return owner_->gesture_count(); }
+ bool SelectionWraps() {
+ return MenuConfig::instance().arrow_key_selection_wraps;
+ }
+
private:
void Init() {
owner_ = std::make_unique<GestureTestWidget>();
@@ -558,8 +568,9 @@ class MenuControllerTest : public ViewsTestBase {
void SetupMenuController() {
menu_controller_delegate_.reset(new TestMenuControllerDelegate);
+ const bool for_drop = false;
menu_controller_ =
- new MenuController(true, menu_controller_delegate_.get());
+ new MenuController(for_drop, menu_controller_delegate_.get());
menu_controller_->owner_ = owner_.get();
menu_controller_->showing_ = true;
menu_controller_->SetSelection(
@@ -644,8 +655,12 @@ TEST_F(MenuControllerTest, InitialSelectedItem) {
// The last selectable item should be item "Four".
MenuItemView* last_selectable =
FindInitialSelectableMenuItemUp(menu_item());
- ASSERT_NE(nullptr, last_selectable);
- EXPECT_EQ(4, last_selectable->GetCommand());
+ if (SelectionWraps()) {
+ ASSERT_NE(nullptr, last_selectable);
+ EXPECT_EQ(4, last_selectable->GetCommand());
+ } else {
+ ASSERT_EQ(nullptr, last_selectable);
+ }
// Leave items "One" and "Two" enabled.
menu_item()->GetSubmenu()->GetMenuItemAt(0)->SetEnabled(true);
@@ -658,8 +673,12 @@ TEST_F(MenuControllerTest, InitialSelectedItem) {
EXPECT_EQ(1, first_selectable->GetCommand());
// The last selectable item should be item "Two".
last_selectable = FindInitialSelectableMenuItemUp(menu_item());
- ASSERT_NE(nullptr, last_selectable);
- EXPECT_EQ(2, last_selectable->GetCommand());
+ if (SelectionWraps()) {
+ ASSERT_NE(nullptr, last_selectable);
+ EXPECT_EQ(2, last_selectable->GetCommand());
+ } else {
+ ASSERT_EQ(nullptr, last_selectable);
+ }
// Leave only a single item "One" enabled.
menu_item()->GetSubmenu()->GetMenuItemAt(0)->SetEnabled(true);
@@ -672,8 +691,12 @@ TEST_F(MenuControllerTest, InitialSelectedItem) {
EXPECT_EQ(1, first_selectable->GetCommand());
// The last selectable item should be item "One".
last_selectable = FindInitialSelectableMenuItemUp(menu_item());
- ASSERT_NE(nullptr, last_selectable);
- EXPECT_EQ(1, last_selectable->GetCommand());
+ if (SelectionWraps()) {
+ ASSERT_NE(nullptr, last_selectable);
+ EXPECT_EQ(1, last_selectable->GetCommand());
+ } else {
+ ASSERT_EQ(nullptr, last_selectable);
+ }
// Leave only a single item "Three" enabled.
menu_item()->GetSubmenu()->GetMenuItemAt(0)->SetEnabled(false);
@@ -686,8 +709,12 @@ TEST_F(MenuControllerTest, InitialSelectedItem) {
EXPECT_EQ(3, first_selectable->GetCommand());
// The last selectable item should be item "Three".
last_selectable = FindInitialSelectableMenuItemUp(menu_item());
- ASSERT_NE(nullptr, last_selectable);
- EXPECT_EQ(3, last_selectable->GetCommand());
+ if (SelectionWraps()) {
+ ASSERT_NE(nullptr, last_selectable);
+ EXPECT_EQ(3, last_selectable->GetCommand());
+ } else {
+ ASSERT_EQ(nullptr, last_selectable);
+ }
// Leave only a single item ("Two") selected. It should be the first and the
// last selectable item.
@@ -699,8 +726,12 @@ TEST_F(MenuControllerTest, InitialSelectedItem) {
ASSERT_NE(nullptr, first_selectable);
EXPECT_EQ(2, first_selectable->GetCommand());
last_selectable = FindInitialSelectableMenuItemUp(menu_item());
- ASSERT_NE(nullptr, last_selectable);
- EXPECT_EQ(2, last_selectable->GetCommand());
+ if (SelectionWraps()) {
+ ASSERT_NE(nullptr, last_selectable);
+ EXPECT_EQ(2, last_selectable->GetCommand());
+ } else {
+ ASSERT_EQ(nullptr, last_selectable);
+ }
// There should be no next or previous selectable item since there is only a
// single enabled item in the menu.
@@ -730,14 +761,20 @@ TEST_F(MenuControllerTest, NextSelectedItem) {
IncrementSelection();
EXPECT_EQ(4, pending_state_item()->GetCommand());
- // Wrap around.
- IncrementSelection();
- EXPECT_EQ(1, pending_state_item()->GetCommand());
-
- // Move up in the menu.
- // Wrap around.
- DecrementSelection();
- EXPECT_EQ(4, pending_state_item()->GetCommand());
+ if (SelectionWraps()) {
+ // Wrap around.
+ IncrementSelection();
+ EXPECT_EQ(1, pending_state_item()->GetCommand());
+
+ // Move up in the menu.
+ // Wrap around.
+ DecrementSelection();
+ EXPECT_EQ(4, pending_state_item()->GetCommand());
+ } else {
+ // Don't wrap.
+ IncrementSelection();
+ EXPECT_EQ(4, pending_state_item()->GetCommand());
+ }
// Skip disabled item.
DecrementSelection();
@@ -762,7 +799,10 @@ TEST_F(MenuControllerTest, PreviousSelectedItem) {
// Move up and select a previous (in our case the last enabled) item.
DecrementSelection();
- EXPECT_EQ(3, pending_state_item()->GetCommand());
+ if (SelectionWraps())
+ EXPECT_EQ(3, pending_state_item()->GetCommand());
+ else
+ EXPECT_EQ(0, pending_state_item()->GetCommand());
// Clear references in menu controller to the menu item that is going away.
ResetSelection();
@@ -843,7 +883,10 @@ TEST_F(MenuControllerTest, SelectChildButtonView) {
// Increment selection twice to wrap around.
IncrementSelection();
IncrementSelection();
- EXPECT_EQ(1, pending_state_item()->GetCommand());
+ if (SelectionWraps())
+ EXPECT_EQ(1, pending_state_item()->GetCommand());
+ else
+ EXPECT_EQ(5, pending_state_item()->GetCommand());
// Clear references in menu controller to the menu item that is going away.
ResetSelection();
@@ -1288,6 +1331,36 @@ TEST_F(MenuControllerTest, AsynchronousGestureDeletesController) {
EXPECT_EQ(1, nested_delegate->on_menu_closed_called());
}
+TEST_F(MenuControllerTest, ArrowKeysAtEnds) {
+ menu_item()->GetSubmenu()->GetMenuItemAt(2)->SetEnabled(false);
+
+ SetPendingStateItem(menu_item()->GetSubmenu()->GetMenuItemAt(0));
+ EXPECT_EQ(1, pending_state_item()->GetCommand());
+
+ if (SelectionWraps()) {
+ DispatchKey(ui::VKEY_UP);
+ EXPECT_EQ(4, pending_state_item()->GetCommand());
+
+ DispatchKey(ui::VKEY_DOWN);
+ EXPECT_EQ(1, pending_state_item()->GetCommand());
+ } else {
+ DispatchKey(ui::VKEY_UP);
+ EXPECT_EQ(1, pending_state_item()->GetCommand());
+ }
+
+ DispatchKey(ui::VKEY_DOWN);
+ EXPECT_EQ(2, pending_state_item()->GetCommand());
+
+ DispatchKey(ui::VKEY_DOWN);
+ EXPECT_EQ(4, pending_state_item()->GetCommand());
+
+ DispatchKey(ui::VKEY_DOWN);
+ if (SelectionWraps())
+ EXPECT_EQ(1, pending_state_item()->GetCommand());
+ else
+ EXPECT_EQ(4, pending_state_item()->GetCommand());
+}
+
#if defined(USE_AURA)
// Tests that when an asynchronous menu receives a cancel event, that it closes.
TEST_F(MenuControllerTest, AsynchronousCancelEvent) {
diff --git a/chromium/ui/views/controls/menu/menu_host.cc b/chromium/ui/views/controls/menu/menu_host.cc
index f1ff95b4748..f14a1c06685 100644
--- a/chromium/ui/views/controls/menu/menu_host.cc
+++ b/chromium/ui/views/controls/menu/menu_host.cc
@@ -116,9 +116,7 @@ void MenuHost::InitMenuHost(Widget* parent,
const MenuController* menu_controller =
submenu_->GetMenuItem()->GetMenuController();
const MenuConfig& menu_config = MenuConfig::instance();
- bool rounded_border =
- menu_controller && (menu_controller->use_touchable_layout() ||
- (menu_config.corner_radius > 0));
+ bool rounded_border = menu_config.CornerRadiusForMenu(menu_controller) != 0;
bool bubble_border = submenu_->GetScrollViewContainer() &&
submenu_->GetScrollViewContainer()->HasBubbleBorder();
params.shadow_type = bubble_border ? Widget::InitParams::SHADOW_TYPE_NONE
diff --git a/chromium/ui/views/controls/menu/menu_item_view.cc b/chromium/ui/views/controls/menu/menu_item_view.cc
index 257f71e6eaa..22a219b4660 100644
--- a/chromium/ui/views/controls/menu/menu_item_view.cc
+++ b/chromium/ui/views/controls/menu/menu_item_view.cc
@@ -12,6 +12,7 @@
#include "ui/accessibility/ax_node_data.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/base/models/menu_model.h"
+#include "ui/base/ui_base_features.h"
#include "ui/gfx/canvas.h"
#include "ui/gfx/color_utils.h"
#include "ui/gfx/geometry/rect.h"
@@ -29,6 +30,7 @@
#include "ui/views/controls/menu/menu_scroll_view_container.h"
#include "ui/views/controls/menu/menu_separator.h"
#include "ui/views/controls/menu/submenu_view.h"
+#include "ui/views/controls/separator.h"
#include "ui/views/widget/widget.h"
namespace views {
@@ -171,7 +173,8 @@ void MenuItemView::GetAccessibleNodeData(ui::AXNodeData* node_data) {
switch (GetType()) {
case SUBMENU:
- node_data->AddState(ax::mojom::State::kHaspopup);
+ case ACTIONABLE_SUBMENU:
+ node_data->SetHasPopup(ax::mojom::HasPopup::kMenu);
break;
case CHECKBOX:
case RADIO: {
@@ -267,7 +270,7 @@ MenuItemView* MenuItemView::AddMenuItemAt(
item->SetMinorIcon(minor_icon);
if (!icon.isNull())
item->SetIcon(icon);
- if (type == SUBMENU)
+ if (type == SUBMENU || type == ACTIONABLE_SUBMENU)
item->CreateSubmenu();
if (GetDelegate() && !GetDelegate()->IsCommandVisible(item_id))
item->SetVisible(false);
@@ -398,6 +401,19 @@ void MenuItemView::SetSelected(bool selected) {
SchedulePaint();
}
+void MenuItemView::SetSelectionOfActionableSubmenu(
+ bool submenu_area_of_actionable_submenu_selected) {
+ DCHECK_EQ(ACTIONABLE_SUBMENU, type_);
+ if (submenu_area_of_actionable_submenu_selected_ ==
+ submenu_area_of_actionable_submenu_selected) {
+ return;
+ }
+
+ submenu_area_of_actionable_submenu_selected_ =
+ submenu_area_of_actionable_submenu_selected;
+ SchedulePaint();
+}
+
void MenuItemView::SetTooltip(const base::string16& tooltip, int item_id) {
MenuItemView* item = GetMenuItemByID(item_id);
DCHECK(item);
@@ -458,6 +474,13 @@ int MenuItemView::GetHeightForWidth(int width) const {
return height;
}
+gfx::Rect MenuItemView::GetSubmenuAreaOfActionableSubmenu() const {
+ DCHECK_EQ(ACTIONABLE_SUBMENU, type_);
+ const MenuConfig& config = MenuConfig::instance();
+ return gfx::Rect(gfx::Point(vertical_separator_->bounds().right(), 0),
+ gfx::Size(config.actionable_submenu_width, height()));
+}
+
const MenuItemView::MenuItemDimensions& MenuItemView::GetDimensions() const {
if (!is_dimensions_valid())
dimensions_ = CalculateDimensions();
@@ -495,8 +518,10 @@ const MenuItemView* MenuItemView::GetRootMenuItem() const {
}
base::char16 MenuItemView::GetMnemonic() {
- if (!GetRootMenuItem()->has_mnemonics_)
+ if (!GetRootMenuItem()->has_mnemonics_ ||
+ !MenuConfig::instance().use_mnemonics) {
return 0;
+ }
size_t index = 0;
do {
@@ -580,6 +605,8 @@ void MenuItemView::Layout() {
continue;
if (submenu_arrow_image_view_ == child)
continue;
+ if (vertical_separator_ == child)
+ continue;
int width = child->GetPreferredSize().width();
child->SetBounds(x - width, 0, width, height());
x -= width + kChildXPadding;
@@ -611,13 +638,25 @@ void MenuItemView::Layout() {
}
if (submenu_arrow_image_view_) {
- int x = width() - config.arrow_width - config.arrow_to_edge_padding;
+ int x = width() - config.arrow_width -
+ (type_ == ACTIONABLE_SUBMENU
+ ? config.actionable_submenu_arrow_to_edge_padding
+ : config.arrow_to_edge_padding);
int y =
(height() + GetTopMargin() - GetBottomMargin() - kSubmenuArrowSize) /
2;
submenu_arrow_image_view_->SetBounds(x, y, config.arrow_width,
kSubmenuArrowSize);
}
+
+ if (vertical_separator_) {
+ const gfx::Size preferred_size = vertical_separator_->GetPreferredSize();
+ int x = width() - config.actionable_submenu_width -
+ config.actionable_submenu_vertical_separator_width;
+ int y = (height() - preferred_size.height()) / 2;
+ vertical_separator_->SetBoundsRect(
+ gfx::Rect(gfx::Point(x, y), preferred_size));
+ }
}
}
@@ -682,7 +721,11 @@ void MenuItemView::UpdateMenuPartSizes() {
if (has_icons_)
icon_area_width_ = std::max(icon_area_width_, GetMaxIconViewWidth());
- label_start_ = config.item_left_margin + icon_area_width_;
+ const bool use_touchable_layout =
+ GetMenuController() && GetMenuController()->use_touchable_layout();
+ label_start_ = (use_touchable_layout ? config.touchable_item_left_margin
+ : config.item_left_margin) +
+ icon_area_width_;
int padding = 0;
if (config.always_use_icon_to_label_padding) {
padding = config.icon_to_label_padding;
@@ -690,7 +733,7 @@ void MenuItemView::UpdateMenuPartSizes() {
padding = (has_icons_ || HasChecksOrRadioButtons()) ?
config.icon_to_label_padding : 0;
}
- if (GetMenuController() && GetMenuController()->use_touchable_layout())
+ if (use_touchable_layout)
padding = config.touchable_icon_to_label_padding;
label_start_ += padding;
@@ -705,15 +748,16 @@ void MenuItemView::Init(MenuItemView* parent,
MenuItemView::Type type,
MenuDelegate* delegate) {
delegate_ = delegate;
- controller_ = NULL;
+ controller_ = nullptr;
canceled_ = false;
parent_menu_item_ = parent;
type_ = type;
selected_ = false;
command_ = command;
- submenu_ = NULL;
+ submenu_ = nullptr;
radio_check_image_view_ = nullptr;
submenu_arrow_image_view_ = nullptr;
+ vertical_separator_ = nullptr;
show_mnemonics_ = false;
// Assign our ID, this allows SubmenuItemView to find MenuItemViews.
set_id(kMenuItemViewID);
@@ -729,6 +773,20 @@ void MenuItemView::Init(MenuItemView* parent,
AddChildView(radio_check_image_view_);
}
+ if (type_ == ACTIONABLE_SUBMENU) {
+ vertical_separator_ = new Separator();
+ vertical_separator_->SetVisible(true);
+ vertical_separator_->SetFocusBehavior(FocusBehavior::NEVER);
+ const MenuConfig& config = MenuConfig::instance();
+ vertical_separator_->SetColor(GetNativeTheme()->GetSystemColor(
+ ui::NativeTheme::kColorId_ActionableSubmenuVerticalSeparatorColor));
+ vertical_separator_->SetPreferredSize(
+ gfx::Size(config.actionable_submenu_vertical_separator_width,
+ config.actionable_submenu_vertical_separator_height));
+ vertical_separator_->set_can_process_events_within_subtree(false);
+ AddChildView(vertical_separator_);
+ }
+
if (submenu_arrow_image_view_)
submenu_arrow_image_view_->SetVisible(HasSubmenu());
@@ -844,6 +902,15 @@ void MenuItemView::PaintButton(gfx::Canvas* canvas, PaintButtonMode mode) {
ui::NativeTheme* native_theme = GetNativeTheme();
if (render_selection) {
gfx::Rect item_bounds(0, 0, width(), height());
+ if (type_ == ACTIONABLE_SUBMENU) {
+ if (submenu_area_of_actionable_submenu_selected_) {
+ item_bounds = GetSubmenuAreaOfActionableSubmenu();
+ } else {
+ item_bounds = gfx::Rect(gfx::Size(
+ width() - MenuConfig::instance().actionable_submenu_width - 1,
+ height()));
+ }
+ }
AdjustBoundsForRTLUI(&item_bounds);
native_theme->Paint(canvas->sk_canvas(),
@@ -962,6 +1029,10 @@ SkColor MenuItemView::GetTextColor(bool minor,
if (!emphasized)
color_id = ui::NativeTheme::kColorId_DisabledMenuItemForegroundColor;
}
+
+ if (GetMenuController() && GetMenuController()->use_touchable_layout())
+ color_id = ui::NativeTheme::kColorId_TouchableMenuItemLabelColor;
+
return GetNativeTheme()->GetSystemColor(color_id);
}
@@ -1012,6 +1083,8 @@ gfx::Size MenuItemView::GetChildPreferredSize() const {
continue;
if (submenu_arrow_image_view_ == child)
continue;
+ if (vertical_separator_ == child)
+ continue;
if (i)
width += kChildXPadding;
width += child->GetPreferredSize().width();
@@ -1034,30 +1107,18 @@ MenuItemView::MenuItemDimensions MenuItemView::CalculateDimensions() const {
const MenuConfig& menu_config = MenuConfig::instance();
if (GetMenuController() && GetMenuController()->use_touchable_layout()) {
- // MenuItemViews that use the touchable layout have fixed height and width.
+ // Touchable layout uses a fixed size, but adjusts the height for icons.
dimensions.height = menu_config.touchable_menu_height;
+ if (icon_view_) {
+ dimensions.height = icon_view_->height() +
+ 2 * menu_config.vertical_touchable_menu_item_padding;
+ }
dimensions.standard_width = menu_config.touchable_menu_width;
return dimensions;
}
const gfx::FontList& font_list = GetFontList();
base::string16 minor_text = GetMinorText();
- if (menu_config.fixed_text_item_height &&
- menu_config.fixed_container_item_height && menu_config.fixed_menu_width &&
- GetMenuController() && !GetMenuController()->is_combobox()) {
- bool has_children = NonIconChildViewsCount() > 0;
- dimensions.height = has_children ? menu_config.fixed_container_item_height
- : menu_config.fixed_text_item_height;
- dimensions.children_width = 0;
- dimensions.minor_text_width =
- minor_text.empty() ? 0 : gfx::GetStringWidth(minor_text, font_list);
- int leave_for_minor = dimensions.minor_text_width
- ? dimensions.minor_text_width +
- menu_config.label_to_minor_text_padding
- : 0;
- dimensions.standard_width = menu_config.fixed_menu_width - leave_for_minor;
- return dimensions;
- }
dimensions.height = child_size.height();
// Adjust item content height if menu has both items with and without icons.
@@ -1069,8 +1130,10 @@ MenuItemView::MenuItemDimensions MenuItemView::CalculateDimensions() const {
dimensions.height += GetBottomMargin() + GetTopMargin();
// In case of a container, only the container size needs to be filled.
- if (IsContainer())
+ if (IsContainer()) {
+ ApplyMinimumDimensions(&dimensions);
return dimensions;
+ }
// Get Icon margin overrides for this particular item.
const MenuDelegate* delegate = GetDelegate();
@@ -1105,11 +1168,37 @@ MenuItemView::MenuItemDimensions MenuItemView::CalculateDimensions() const {
font_list.GetHeight() + GetBottomMargin() + GetTopMargin());
dimensions.height =
std::max(dimensions.height, MenuConfig::instance().item_min_height);
+
+ ApplyMinimumDimensions(&dimensions);
return dimensions;
}
+void MenuItemView::ApplyMinimumDimensions(MenuItemDimensions* dims) const {
+ // Don't apply minimums to menus without controllers or to comboboxes.
+ if (!GetMenuController() || GetMenuController()->is_combobox())
+ return;
+
+ int used =
+ dims->standard_width + dims->children_width + dims->minor_text_width;
+ const MenuConfig& config = MenuConfig::instance();
+ if (used < config.minimum_menu_width)
+ dims->standard_width += (config.minimum_menu_width - used);
+
+ dims->height = std::max(dims->height,
+ IsContainer() ? config.minimum_container_item_height
+ : config.minimum_text_item_height);
+}
+
int MenuItemView::GetLabelStartForThisItem() const {
const MenuConfig& config = MenuConfig::instance();
+
+ // Touchable items with icons do not respect |label_start_|.
+ if (GetMenuController() && GetMenuController()->use_touchable_layout() &&
+ icon_view_) {
+ return config.touchable_item_left_margin + icon_view_->width() +
+ config.touchable_icon_to_label_padding;
+ }
+
int label_start = label_start_ + left_icon_margin_ + right_icon_margin_;
if ((config.icons_in_label || type_ == CHECKBOX || type_ == RADIO) &&
icon_view_)
@@ -1124,12 +1213,9 @@ base::string16 MenuItemView::GetMinorText() const {
return base::string16();
}
- ui::Accelerator accelerator;
- if (MenuConfig::instance().show_accelerators && GetDelegate() &&
- GetCommand() &&
- GetDelegate()->GetAccelerator(GetCommand(), &accelerator)) {
- return accelerator.GetShortcutText();
- }
+ base::string16 accel_text;
+ if (MenuConfig::instance().ShouldShowAcceleratorText(this, &accel_text))
+ return accel_text;
return minor_text_;
}
@@ -1149,7 +1235,7 @@ int MenuItemView::NonIconChildViewsCount() const {
// not the number of menu items.
return child_count() - (icon_view_ ? 1 : 0) -
(radio_check_image_view_ ? 1 : 0) -
- (submenu_arrow_image_view_ ? 1 : 0);
+ (submenu_arrow_image_view_ ? 1 : 0) - (vertical_separator_ ? 1 : 0);
}
int MenuItemView::GetMaxIconViewWidth() const {
diff --git a/chromium/ui/views/controls/menu/menu_item_view.h b/chromium/ui/views/controls/menu/menu_item_view.h
index be24fb73cf7..6276932ceaf 100644
--- a/chromium/ui/views/controls/menu/menu_item_view.h
+++ b/chromium/ui/views/controls/menu/menu_item_view.h
@@ -17,7 +17,6 @@
#include "build/build_config.h"
#include "ui/base/models/menu_separator_types.h"
#include "ui/gfx/image/image_skia.h"
-#include "ui/views/controls/menu/menu_config.h"
#include "ui/views/controls/menu/menu_controller.h"
#include "ui/views/controls/menu/menu_types.h"
#include "ui/views/view.h"
@@ -45,6 +44,7 @@ class TestMenuItemViewShown;
class MenuController;
class MenuDelegate;
+class Separator;
class TestMenuItemView;
class SubmenuView;
@@ -83,15 +83,16 @@ class VIEWS_EXPORT MenuItemView : public View {
// ID used to identify empty menu items.
static const int kEmptyMenuItemViewID;
- // Different types of menu items. EMPTY is a special type for empty
- // menus that is only used internally.
+ // Different types of menu items.
enum Type {
- NORMAL,
- SUBMENU,
- CHECKBOX,
- RADIO,
- SEPARATOR,
- EMPTY
+ NORMAL, // Performs an action when selected.
+ SUBMENU, // Presents a submenu within another menu.
+ ACTIONABLE_SUBMENU, // A SUBMENU that is also a COMMAND.
+ CHECKBOX, // Can be selected/checked to toggle a boolean state.
+ RADIO, // Can be selected/checked among a group of choices.
+ SEPARATOR, // Shows a horizontal line separator.
+ EMPTY, // EMPTY is a special type for empty menus that is only used
+ // internally.
};
// Where the menu should be drawn, above or below the bounds (when
@@ -257,6 +258,15 @@ class VIEWS_EXPORT MenuItemView : public View {
// Returns true if the item is selected.
bool IsSelected() const { return selected_; }
+ // Sets whether the submenu area of an ACTIONABLE_SUBMENU is selected.
+ void SetSelectionOfActionableSubmenu(
+ bool submenu_area_of_actionable_submenu_selected);
+
+ // Whether the submenu area of an ACTIONABLE_SUBMENU is selected.
+ bool IsSubmenuAreaOfActionableSubmenuSelected() const {
+ return submenu_area_of_actionable_submenu_selected_;
+ }
+
// Sets the |tooltip| for a menu item view with |item_id| identifier.
void SetTooltip(const base::string16& tooltip, int item_id);
@@ -288,6 +298,9 @@ class VIEWS_EXPORT MenuItemView : public View {
// dimensions.
int GetHeightForWidth(int width) const override;
+ // Returns the bounds of the submenu part of the ACTIONABLE_SUBMENU.
+ gfx::Rect GetSubmenuAreaOfActionableSubmenu() const;
+
// Return the preferred dimensions of the item in pixel.
const MenuItemDimensions& GetDimensions() const;
@@ -430,6 +443,14 @@ class VIEWS_EXPORT MenuItemView : public View {
// Calculates and returns the MenuItemDimensions.
MenuItemDimensions CalculateDimensions() const;
+ // Imposes MenuConfig's minimum sizes, if any, on the supplied
+ // dimensions and returns the new dimensions. It is guaranteed that:
+ // ApplyMinimumDimensions(x).standard_width >= x.standard_width
+ // ApplyMinimumDimensions(x).children_width == x.children_width
+ // ApplyMinimumDimensions(x).minor_text_width == x.minor_text_width
+ // ApplyMinimumDimensions(x).height >= x.height
+ void ApplyMinimumDimensions(MenuItemDimensions* dims) const;
+
// Get the horizontal position at which to draw the menu item's label.
int GetLabelStartForThisItem() const;
@@ -484,6 +505,9 @@ class VIEWS_EXPORT MenuItemView : public View {
// Whether we're selected.
bool selected_;
+ // Whether the submenu area of an ACTIONABLE_SUBMENU is selected.
+ bool submenu_area_of_actionable_submenu_selected_;
+
// Command id.
int command_;
@@ -565,6 +589,10 @@ class VIEWS_EXPORT MenuItemView : public View {
// The forced visual selection state of this item, if any.
base::Optional<bool> forced_visual_selection_;
+ // The vertical separator that separates the actionable and submenu regions of
+ // an ACTIONABLE_SUBMENU.
+ Separator* vertical_separator_;
+
DISALLOW_COPY_AND_ASSIGN(MenuItemView);
};
diff --git a/chromium/ui/views/controls/menu/menu_item_view_unittest.cc b/chromium/ui/views/controls/menu/menu_item_view_unittest.cc
index bdc22a6b784..07273f5de1a 100644
--- a/chromium/ui/views/controls/menu/menu_item_view_unittest.cc
+++ b/chromium/ui/views/controls/menu/menu_item_view_unittest.cc
@@ -41,6 +41,8 @@ class TestMenuItemView : public MenuItemView {
void AddEmptyMenus() { MenuItemView::AddEmptyMenus(); }
+ void SetHasMnemonics(bool has_mnemonics) { has_mnemonics_ = has_mnemonics; }
+
private:
DISALLOW_COPY_AND_ASSIGN(TestMenuItemView);
};
@@ -70,15 +72,14 @@ TEST(MenuItemViewUnitTest, TestMenuItemViewWithFlexibleWidthChild) {
ASSERT_EQ(flexible_view, submenu->GetMenuItemAt(1));
gfx::Size flexible_size = flexible_view->GetPreferredSize();
- // The flexible view's "preferred size" should be 1x1...
- EXPECT_EQ(flexible_size, gfx::Size(1, 1));
+ EXPECT_EQ(1, flexible_size.width());
// ...but it should use whatever space is available to make a square.
int flex_height = flexible_view->GetHeightForWidth(label_size.width());
EXPECT_EQ(label_size.width(), flex_height);
- // The submenu should be tall enough to allow for both menu items at the given
- // width.
+ // The submenu should be tall enough to allow for both menu items at the
+ // given width.
EXPECT_EQ(label_size.height() + flex_height,
submenu->GetPreferredSize().height());
}
@@ -147,6 +148,24 @@ TEST(MenuItemViewUnitTest, TestEmptySubmenuWhenAllChildItemsAreHidden) {
empty_item->title());
}
+TEST(MenuItemViewUnitTest, UseMnemonicOnPlatform) {
+ TestMenuItemView root_menu;
+ views::MenuItemView* item1 =
+ root_menu.AppendMenuItemWithLabel(1, base::ASCIIToUTF16("&Item 1"));
+ views::MenuItemView* item2 =
+ root_menu.AppendMenuItemWithLabel(2, base::ASCIIToUTF16("I&tem 2"));
+
+ root_menu.SetHasMnemonics(true);
+
+ if (MenuConfig::instance().use_mnemonics) {
+ EXPECT_EQ('i', item1->GetMnemonic());
+ EXPECT_EQ('t', item2->GetMnemonic());
+ } else {
+ EXPECT_EQ(0, item1->GetMnemonic());
+ EXPECT_EQ(0, item2->GetMnemonic());
+ }
+}
+
class MenuItemViewPaintUnitTest : public ViewsTestBase {
public:
MenuItemViewPaintUnitTest() {}
diff --git a/chromium/ui/views/controls/menu/menu_model_adapter.cc b/chromium/ui/views/controls/menu/menu_model_adapter.cc
index e1a2339bddb..e9cfe1c466c 100644
--- a/chromium/ui/views/controls/menu/menu_model_adapter.cc
+++ b/chromium/ui/views/controls/menu/menu_model_adapter.cc
@@ -80,6 +80,9 @@ MenuItemView* MenuModelAdapter::AddMenuItemFromModelAt(ui::MenuModel* model,
case ui::MenuModel::TYPE_SUBMENU:
type = MenuItemView::SUBMENU;
break;
+ case ui::MenuModel::TYPE_ACTIONABLE_SUBMENU:
+ type = MenuItemView::ACTIONABLE_SUBMENU;
+ break;
}
if (*type == MenuItemView::SEPARATOR) {
@@ -267,9 +270,11 @@ void MenuModelAdapter::BuildMenuImpl(MenuItemView* menu, ui::MenuModel* model) {
for (int i = 0; i < item_count; ++i) {
MenuItemView* item = AppendMenuItem(menu, model, i);
- if (model->GetTypeAt(i) == ui::MenuModel::TYPE_SUBMENU) {
+ if (model->GetTypeAt(i) == ui::MenuModel::TYPE_SUBMENU ||
+ model->GetTypeAt(i) == ui::MenuModel::TYPE_ACTIONABLE_SUBMENU) {
DCHECK(item);
- DCHECK_EQ(MenuItemView::SUBMENU, item->GetType());
+ DCHECK(item->GetType() == MenuItemView::SUBMENU ||
+ item->GetType() == MenuItemView::ACTIONABLE_SUBMENU);
ui::MenuModel* submodel = model->GetSubmenuModelAt(i);
DCHECK(submodel);
BuildMenuImpl(item, submodel);
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 c5df9547874..df3bb3ef251 100644
--- a/chromium/ui/views/controls/menu/menu_model_adapter_unittest.cc
+++ b/chromium/ui/views/controls/menu/menu_model_adapter_unittest.cc
@@ -16,8 +16,9 @@
namespace {
// Base command id for test menu and its submenu.
-const int kRootIdBase = 100;
-const int kSubmenuIdBase = 200;
+constexpr int kRootIdBase = 100;
+constexpr int kSubmenuIdBase = 200;
+constexpr int kActionableSubmenuIdBase = 300;
class MenuModelBase : public ui::MenuModel {
public:
@@ -139,61 +140,68 @@ class SubmenuModel : public MenuModelBase {
DISALLOW_COPY_AND_ASSIGN(SubmenuModel);
};
+class ActionableSubmenuModel : public MenuModelBase {
+ public:
+ ActionableSubmenuModel() : MenuModelBase(kActionableSubmenuIdBase) {
+ items_.push_back(Item(TYPE_COMMAND, "actionable submenu item 0", NULL));
+ items_.push_back(Item(TYPE_COMMAND, "actionable submenu item 1", NULL));
+ }
+ ~ActionableSubmenuModel() override = default;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(ActionableSubmenuModel);
+};
+
class RootModel : public MenuModelBase {
public:
RootModel() : MenuModelBase(kRootIdBase) {
- submenu_model_.reset(new SubmenuModel);
+ submenu_model_ = std::make_unique<SubmenuModel>();
+ actionable_submenu_model_ = std::make_unique<ActionableSubmenuModel>();
items_.push_back(Item(TYPE_COMMAND, "command 0", NULL));
items_.push_back(Item(TYPE_CHECK, "check 1", NULL));
items_.push_back(Item(TYPE_SEPARATOR, "", NULL));
items_.push_back(Item(TYPE_SUBMENU, "submenu 3", submenu_model_.get()));
items_.push_back(Item(TYPE_RADIO, "radio 4", NULL));
+ items_.push_back(Item(TYPE_ACTIONABLE_SUBMENU, "actionable 5",
+ actionable_submenu_model_.get()));
}
~RootModel() override {}
private:
std::unique_ptr<MenuModel> submenu_model_;
+ std::unique_ptr<MenuModel> actionable_submenu_model_;
DISALLOW_COPY_AND_ASSIGN(RootModel);
};
-} // namespace
-
-namespace views {
-
-typedef ViewsTestBase MenuModelAdapterTest;
-
-TEST_F(MenuModelAdapterTest, BasicTest) {
- // Build model and adapter.
- RootModel model;
- views::MenuModelAdapter delegate(&model);
-
- // Create menu. Build menu twice to check that rebuilding works properly.
- MenuItemView* menu = new views::MenuItemView(&delegate);
- // MenuRunner takes ownership of menu.
- std::unique_ptr<MenuRunner> menu_runner(new MenuRunner(menu, 0));
- delegate.BuildMenu(menu);
- delegate.BuildMenu(menu);
- EXPECT_TRUE(menu->HasSubmenu());
+void CheckSubmenu(const RootModel& model,
+ views::MenuItemView* menu,
+ views::MenuModelAdapter* delegate,
+ int submenu_id,
+ int expected_children,
+ int submenu_model_index,
+ int id_base) {
+ views::MenuItemView* submenu = menu->GetMenuItemByID(submenu_id);
+ views::SubmenuView* subitem_container = submenu->GetSubmenu();
+ EXPECT_EQ(expected_children, subitem_container->child_count());
- // Check top level menu items.
- views::SubmenuView* item_container = menu->GetSubmenu();
- EXPECT_EQ(5, item_container->child_count());
+ for (int i = 0; i < subitem_container->child_count(); ++i) {
+ MenuModelBase* submodel = static_cast<MenuModelBase*>(
+ model.GetSubmenuModelAt(submenu_model_index));
+ EXPECT_TRUE(submodel);
- for (int i = 0; i < item_container->child_count(); ++i) {
- const MenuModelBase::Item& model_item = model.GetItemDefinition(i);
+ const MenuModelBase::Item& model_item = submodel->GetItemDefinition(i);
- const int id = i + kRootIdBase;
- MenuItemView* item = menu->GetMenuItemByID(id);
+ const int id = i + id_base;
+ views::MenuItemView* item = menu->GetMenuItemByID(id);
if (!item) {
EXPECT_EQ(ui::MenuModel::TYPE_SEPARATOR, model_item.type);
continue;
}
-
// Check placement.
- EXPECT_EQ(i, menu->GetSubmenu()->GetIndexOf(item));
+ EXPECT_EQ(i, submenu->GetSubmenu()->GetIndexOf(item));
// Check type.
switch (model_item.type) {
@@ -212,27 +220,45 @@ TEST_F(MenuModelAdapterTest, BasicTest) {
case ui::MenuModel::TYPE_SUBMENU:
EXPECT_EQ(views::MenuItemView::SUBMENU, item->GetType());
break;
+ case ui::MenuModel::TYPE_ACTIONABLE_SUBMENU:
+ EXPECT_EQ(views::MenuItemView::ACTIONABLE_SUBMENU, item->GetType());
+ break;
}
// Check activation.
- static_cast<views::MenuDelegate*>(&delegate)->ExecuteCommand(id);
- EXPECT_EQ(i, model.last_activation());
- model.set_last_activation(-1);
+ static_cast<views::MenuDelegate*>(delegate)->ExecuteCommand(id);
+ EXPECT_EQ(i, submodel->last_activation());
+ submodel->set_last_activation(-1);
}
+}
- // Check submenu items.
- views::MenuItemView* submenu = menu->GetMenuItemByID(103);
- views::SubmenuView* subitem_container = submenu->GetSubmenu();
- EXPECT_EQ(2, subitem_container->child_count());
+} // namespace
- for (int i = 0; i < subitem_container->child_count(); ++i) {
- MenuModelBase* submodel = static_cast<MenuModelBase*>(
- model.GetSubmenuModelAt(3));
- EXPECT_TRUE(submodel);
+namespace views {
- const MenuModelBase::Item& model_item = submodel->GetItemDefinition(i);
+typedef ViewsTestBase MenuModelAdapterTest;
+
+TEST_F(MenuModelAdapterTest, BasicTest) {
+ // Build model and adapter.
+ RootModel model;
+ views::MenuModelAdapter delegate(&model);
- const int id = i + kSubmenuIdBase;
+ // Create menu. Build menu twice to check that rebuilding works properly.
+ MenuItemView* menu = new views::MenuItemView(&delegate);
+ // MenuRunner takes ownership of menu.
+ std::unique_ptr<MenuRunner> menu_runner(new MenuRunner(menu, 0));
+ delegate.BuildMenu(menu);
+ delegate.BuildMenu(menu);
+ EXPECT_TRUE(menu->HasSubmenu());
+
+ // Check top level menu items.
+ views::SubmenuView* item_container = menu->GetSubmenu();
+ EXPECT_EQ(6, item_container->child_count());
+
+ for (int i = 0; i < item_container->child_count(); ++i) {
+ const MenuModelBase::Item& model_item = model.GetItemDefinition(i);
+
+ const int id = i + kRootIdBase;
MenuItemView* item = menu->GetMenuItemByID(id);
if (!item) {
EXPECT_EQ(ui::MenuModel::TYPE_SEPARATOR, model_item.type);
@@ -240,7 +266,7 @@ TEST_F(MenuModelAdapterTest, BasicTest) {
}
// Check placement.
- EXPECT_EQ(i, submenu->GetSubmenu()->GetIndexOf(item));
+ EXPECT_EQ(i, menu->GetSubmenu()->GetIndexOf(item));
// Check type.
switch (model_item.type) {
@@ -259,14 +285,27 @@ TEST_F(MenuModelAdapterTest, BasicTest) {
case ui::MenuModel::TYPE_SUBMENU:
EXPECT_EQ(views::MenuItemView::SUBMENU, item->GetType());
break;
+ case ui::MenuModel::TYPE_ACTIONABLE_SUBMENU:
+ EXPECT_EQ(views::MenuItemView::ACTIONABLE_SUBMENU, item->GetType());
+ break;
}
// Check activation.
static_cast<views::MenuDelegate*>(&delegate)->ExecuteCommand(id);
- EXPECT_EQ(i, submodel->last_activation());
- submodel->set_last_activation(-1);
+ EXPECT_EQ(i, model.last_activation());
+ model.set_last_activation(-1);
}
+ // Check the submenu.
+ const int submenu_index = 3;
+ CheckSubmenu(model, menu, &delegate, kRootIdBase + submenu_index, 2,
+ submenu_index, kSubmenuIdBase);
+
+ // Check the actionable submenu.
+ const int actionable_submenu_index = 5;
+ CheckSubmenu(model, menu, &delegate, kRootIdBase + actionable_submenu_index,
+ 2, actionable_submenu_index, kActionableSubmenuIdBase);
+
// Check that selecting the root item is safe. The MenuModel does
// not care about the root so MenuModelAdapter should do nothing
// (not hit the NOTREACHED check) when the root is selected.
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 8bd58e67d17..ae0c8bafd05 100644
--- a/chromium/ui/views/controls/menu/menu_runner_cocoa_unittest.mm
+++ b/chromium/ui/views/controls/menu/menu_runner_cocoa_unittest.mm
@@ -12,8 +12,10 @@
#include "base/test/test_timeouts.h"
#include "base/threading/thread_task_runner_handle.h"
#import "testing/gtest_mac.h"
+#include "ui/base/accelerators/accelerator.h"
#include "ui/base/models/simple_menu_model.h"
#include "ui/events/event_utils.h"
+#import "ui/events/test/cocoa_test_event_utils.h"
#include "ui/views/controls/menu/menu_runner_impl_adapter.h"
#include "ui/views/test/views_test_base.h"
@@ -21,14 +23,16 @@ namespace views {
namespace test {
namespace {
+constexpr int kTestCommandId = 0;
+
class TestModel : public ui::SimpleMenuModel {
public:
TestModel() : ui::SimpleMenuModel(&delegate_), delegate_(this) {}
void set_checked_command(int command) { checked_command_ = command; }
- void set_menu_open_callback(const base::Closure& callback) {
- menu_open_callback_ = callback;
+ void set_menu_open_callback(base::OnceClosure callback) {
+ menu_open_callback_ = std::move(callback);
}
private:
@@ -43,7 +47,17 @@ class TestModel : public ui::SimpleMenuModel {
void MenuWillShow(SimpleMenuModel* source) override {
if (!model_->menu_open_callback_.is_null())
- model_->menu_open_callback_.Run();
+ std::move(model_->menu_open_callback_).Run();
+ }
+
+ bool GetAcceleratorForCommandId(
+ int command_id,
+ ui::Accelerator* accelerator) const override {
+ if (command_id == kTestCommandId) {
+ *accelerator = ui::Accelerator(ui::VKEY_E, ui::EF_CONTROL_DOWN);
+ return true;
+ }
+ return false;
}
private:
@@ -55,7 +69,7 @@ class TestModel : public ui::SimpleMenuModel {
private:
int checked_command_ = -1;
Delegate delegate_;
- base::Closure menu_open_callback_;
+ base::OnceClosure menu_open_callback_;
DISALLOW_COPY_AND_ASSIGN(TestModel);
};
@@ -84,7 +98,7 @@ class MenuRunnerCocoaTest : public ViewsTestBase,
ViewsTestBase::SetUp();
menu_.reset(new TestModel());
- menu_->AddCheckItem(0, base::ASCIIToUTF16("Menu Item"));
+ menu_->AddCheckItem(kTestCommandId, base::ASCIIToUTF16("Menu Item"));
parent_ = new views::Widget();
parent_->Init(CreateParams(Widget::InitParams::TYPE_WINDOW_FRAMELESS));
@@ -114,16 +128,17 @@ class MenuRunnerCocoaTest : public ViewsTestBase,
int IsAsync() const { return GetParam() == MenuType::VIEWS; }
// Runs the menu after registering |callback| as the menu open callback.
- void RunMenu(const base::Closure& callback) {
+ void RunMenu(base::OnceClosure callback) {
if (IsAsync()) {
// Cancelling an async menu under MenuControllerCocoa::OpenMenuImpl()
// (which invokes WillShowMenu()) will cause a UAF when that same function
// tries to show the menu. So post a task instead.
- base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, callback);
+ base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
+ std::move(callback));
} else {
menu_->set_menu_open_callback(
- base::Bind(&MenuRunnerCocoaTest::RunMenuWrapperCallback,
- base::Unretained(this), callback));
+ base::BindOnce(&MenuRunnerCocoaTest::RunMenuWrapperCallback,
+ base::Unretained(this), std::move(callback)));
}
runner_->RunMenuAt(parent_, nullptr, gfx::Rect(), MENU_ANCHOR_TOPLEFT,
@@ -140,13 +155,15 @@ class MenuRunnerCocoaTest : public ViewsTestBase,
// go up by one (the anchor view) while the menu is shown.
EXPECT_EQ(1u, [[parent_->GetNativeView() subviews] count]);
- base::Closure callback =
- base::Bind(&MenuRunnerCocoaTest::ComboboxRunMenuAtCallback,
- base::Unretained(this));
- if (IsAsync())
- base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, callback);
- else
- menu_->set_menu_open_callback(callback);
+ base::OnceClosure callback =
+ base::BindOnce(&MenuRunnerCocoaTest::ComboboxRunMenuAtCallback,
+ base::Unretained(this));
+ if (IsAsync()) {
+ base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
+ std::move(callback));
+ } else {
+ menu_->set_menu_open_callback(std::move(callback));
+ }
runner_->RunMenuAt(parent_, nullptr, anchor, MENU_ANCHOR_TOPLEFT,
MenuRunner::COMBOBOX);
@@ -178,6 +195,44 @@ class MenuRunnerCocoaTest : public ViewsTestBase,
QuitAsyncRunLoop();
}
+ NSMenu* GetNativeNSMenu() {
+ if (GetParam() == MenuType::VIEWS)
+ return nil;
+
+ internal::MenuRunnerImplCocoa* cocoa_runner =
+ static_cast<internal::MenuRunnerImplCocoa*>(runner_);
+ return [cocoa_runner->menu_controller_ menu];
+ }
+
+ void ModelDeleteThenSelectItemCallback() {
+ // AppKit may retain a reference to the NSMenu.
+ base::scoped_nsobject<NSMenu> native_menu(GetNativeNSMenu(),
+ base::scoped_policy::RETAIN);
+
+ // A View showing a menu typically owns a MenuRunner unique_ptr, which will
+ // will be destroyed (releasing the MenuRunnerImpl) alongside the MenuModel.
+ runner_->Release();
+ runner_ = nullptr;
+ menu_ = nullptr;
+
+ // The menu is closing (yet "alive"), but the model is destroyed. The user
+ // may have already made an event to select an item in the menu. This
+ // doesn't bother views menus (see MenuRunnerImpl::empty_delegate_) but
+ // Cocoa menu items are refcounted and have access to a raw weak pointer in
+ // the MenuController.
+ if (GetParam() == MenuType::VIEWS) {
+ QuitAsyncRunLoop();
+ return;
+ }
+
+ EXPECT_TRUE(native_menu.get());
+
+ // Simulate clicking the item using its accelerator.
+ NSEvent* accelerator = cocoa_test_event_utils::KeyEventWithKeyCode(
+ 'e', 'e', NSKeyDown, NSCommandKeyMask);
+ [native_menu performKeyEquivalent:accelerator];
+ }
+
void MenuCancelAndDeleteCallback() {
runner_->Cancel();
runner_->Release();
@@ -192,9 +247,9 @@ class MenuRunnerCocoaTest : public ViewsTestBase,
int menu_close_count_ = 0;
private:
- void RunMenuWrapperCallback(const base::Closure& callback) {
+ void RunMenuWrapperCallback(base::OnceClosure callback) {
EXPECT_TRUE(runner_->IsRunning());
- callback.Run();
+ std::move(callback).Run();
}
void ComboboxRunMenuAtCallback() {
@@ -248,8 +303,8 @@ class MenuRunnerCocoaTest : public ViewsTestBase,
TEST_P(MenuRunnerCocoaTest, RunMenuAndCancel) {
base::TimeTicks min_time = ui::EventTimeForNow();
- RunMenu(base::Bind(&MenuRunnerCocoaTest::MenuCancelCallback,
- base::Unretained(this)));
+ RunMenu(base::BindOnce(&MenuRunnerCocoaTest::MenuCancelCallback,
+ base::Unretained(this)));
EXPECT_EQ(1, menu_close_count_);
EXPECT_FALSE(runner_->IsRunning());
@@ -271,17 +326,26 @@ TEST_P(MenuRunnerCocoaTest, RunMenuAndCancel) {
}
TEST_P(MenuRunnerCocoaTest, RunMenuAndDelete) {
- RunMenu(base::Bind(&MenuRunnerCocoaTest::MenuDeleteCallback,
- base::Unretained(this)));
+ RunMenu(base::BindOnce(&MenuRunnerCocoaTest::MenuDeleteCallback,
+ base::Unretained(this)));
// Note the close callback is NOT invoked for deleted menus.
EXPECT_EQ(0, menu_close_count_);
}
+// Tests a potential lifetime issue using the Cocoa MenuController, which has a
+// weak reference to the model.
+TEST_P(MenuRunnerCocoaTest, RunMenuAndDeleteThenSelectItem) {
+ RunMenu(
+ base::BindOnce(&MenuRunnerCocoaTest::ModelDeleteThenSelectItemCallback,
+ base::Unretained(this)));
+ EXPECT_EQ(0, menu_close_count_);
+}
+
// Ensure a menu can be safely released immediately after a call to Cancel() in
// the same run loop iteration.
TEST_P(MenuRunnerCocoaTest, DestroyAfterCanceling) {
- RunMenu(base::Bind(&MenuRunnerCocoaTest::MenuCancelAndDeleteCallback,
- base::Unretained(this)));
+ RunMenu(base::BindOnce(&MenuRunnerCocoaTest::MenuCancelAndDeleteCallback,
+ base::Unretained(this)));
if (IsAsync()) {
EXPECT_EQ(1, menu_close_count_);
@@ -294,8 +358,8 @@ TEST_P(MenuRunnerCocoaTest, DestroyAfterCanceling) {
TEST_P(MenuRunnerCocoaTest, RunMenuTwice) {
for (int i = 0; i < 2; ++i) {
- RunMenu(base::Bind(&MenuRunnerCocoaTest::MenuCancelCallback,
- base::Unretained(this)));
+ RunMenu(base::BindOnce(&MenuRunnerCocoaTest::MenuCancelCallback,
+ base::Unretained(this)));
EXPECT_FALSE(runner_->IsRunning());
EXPECT_EQ(i + 1, menu_close_count_);
}
@@ -339,7 +403,7 @@ TEST_P(MenuRunnerCocoaTest, ComboboxAnchoring) {
combobox_rect.width(), 0),
last_anchor_frame_);
- menu_->set_checked_command(0);
+ menu_->set_checked_command(kTestCommandId);
RunMenuAt(anchor_rect);
// Native constant used by MenuRunnerImplCocoa.
diff --git a/chromium/ui/views/controls/menu/menu_runner_impl.cc b/chromium/ui/views/controls/menu/menu_runner_impl.cc
index cd9ac6df3ff..1c7b5c4c72a 100644
--- a/chromium/ui/views/controls/menu/menu_runner_impl.cc
+++ b/chromium/ui/views/controls/menu/menu_runner_impl.cc
@@ -92,9 +92,9 @@ void MenuRunnerImpl::RunMenuAt(Widget* parent,
MenuController* controller = MenuController::GetActiveInstance();
if (controller) {
if ((run_types & MenuRunner::IS_NESTED) != 0) {
- if (!controller->IsBlockingRun()) {
+ if (controller->for_drop()) {
controller->CancelAll();
- controller = NULL;
+ controller = nullptr;
} else {
// Only nest the delegate when not cancelling drag-and-drop. When
// cancelling this will become the root delegate of the new
@@ -112,7 +112,7 @@ void MenuRunnerImpl::RunMenuAt(Widget* parent,
}
// Drop menus don't block the message loop, so it's ok to create a new
// MenuController.
- controller = NULL;
+ controller = nullptr;
}
}
@@ -122,7 +122,7 @@ void MenuRunnerImpl::RunMenuAt(Widget* parent,
owns_controller_ = false;
if (!controller) {
// No menus are showing, show one.
- controller = new MenuController(!for_drop_, this);
+ controller = new MenuController(for_drop_, this);
owns_controller_ = true;
}
controller->set_is_combobox((run_types & MenuRunner::COMBOBOX) != 0);
@@ -204,6 +204,8 @@ bool MenuRunnerImpl::ShouldShowMnemonics(MenuButton* button) {
show_mnemonics |= ui::win::IsAltPressed();
#elif defined(USE_X11)
show_mnemonics |= ui::IsAltPressed();
+#elif defined(OS_MACOSX)
+ show_mnemonics = false;
#endif
return show_mnemonics;
}
diff --git a/chromium/ui/views/controls/menu/menu_runner_impl_cocoa.h b/chromium/ui/views/controls/menu/menu_runner_impl_cocoa.h
index c89e9cee9c7..599000dd285 100644
--- a/chromium/ui/views/controls/menu/menu_runner_impl_cocoa.h
+++ b/chromium/ui/views/controls/menu/menu_runner_impl_cocoa.h
@@ -16,6 +16,9 @@
@class MenuControllerCocoa;
namespace views {
+namespace test {
+class MenuRunnerCocoaTest;
+}
namespace internal {
// A menu runner implementation that uses NSMenu to show a context menu.
@@ -35,6 +38,8 @@ class VIEWS_EXPORT MenuRunnerImplCocoa : public MenuRunnerImplInterface {
base::TimeTicks GetClosingEventTime() const override;
private:
+ friend class views::test::MenuRunnerCocoaTest;
+
~MenuRunnerImplCocoa() override;
// The Cocoa menu controller that this instance is bridging.
diff --git a/chromium/ui/views/controls/menu/menu_runner_impl_cocoa.mm b/chromium/ui/views/controls/menu/menu_runner_impl_cocoa.mm
index e68a76f83ed..8ccd3382da9 100644
--- a/chromium/ui/views/controls/menu/menu_runner_impl_cocoa.mm
+++ b/chromium/ui/views/controls/menu/menu_runner_impl_cocoa.mm
@@ -148,7 +148,12 @@ void MenuRunnerImplCocoa::Release() {
return; // We already canceled.
delete_after_run_ = true;
- [menu_controller_ cancel];
+
+ // Reset |menu_controller_| to ensure it clears itself as a delegate to
+ // prevent NSMenu attempting to access the weak pointer to the ui::MenuModel
+ // it holds (which is not owned by |this|). Toolkit-views menus use
+ // MenuRunnerImpl::empty_delegate_ to handle this case.
+ menu_controller_.reset();
} else {
delete this;
}
diff --git a/chromium/ui/views/controls/menu/menu_runner_unittest.cc b/chromium/ui/views/controls/menu/menu_runner_unittest.cc
index 79875e46f84..4c6c320cd93 100644
--- a/chromium/ui/views/controls/menu/menu_runner_unittest.cc
+++ b/chromium/ui/views/controls/menu/menu_runner_unittest.cc
@@ -10,6 +10,7 @@
#include "base/macros.h"
#include "base/strings/utf_string_conversions.h"
+#include "build/build_config.h"
#include "ui/base/ui_base_types.h"
#include "ui/events/test/event_generator.h"
#include "ui/views/controls/menu/menu_controller.h"
@@ -179,8 +180,9 @@ TEST_F(MenuRunnerTest, LatinMnemonic) {
EXPECT_NE(nullptr, delegate->on_menu_closed_menu());
}
+#if !defined(OS_WIN)
// Tests that a key press on a non-US keyboard layout activates the correct menu
-// item.
+// item. Disabled on Windows because a WM_CHAR event does not activate an item.
TEST_F(MenuRunnerTest, NonLatinMnemonic) {
// TODO: test uses GetContext(), which is not applicable to aura-mus.
// http://crbug.com/663809.
@@ -204,6 +206,7 @@ TEST_F(MenuRunnerTest, NonLatinMnemonic) {
EXPECT_EQ(1, delegate->on_menu_closed_called());
EXPECT_NE(nullptr, delegate->on_menu_closed_menu());
}
+#endif // !defined(OS_WIN)
// Tests that attempting to nest a menu within a drag-and-drop menu does not
// cause a crash. Instead the drag and drop action should be canceled, and the
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 2e2e6b2aa26..abcc8e28e7e 100644
--- a/chromium/ui/views/controls/menu/menu_scroll_view_container.cc
+++ b/chromium/ui/views/controls/menu/menu_scroll_view_container.cc
@@ -250,10 +250,8 @@ void MenuScrollViewContainer::OnPaintBackground(gfx::Canvas* canvas) {
gfx::Rect bounds(0, 0, width(), height());
NativeTheme::ExtraParams extra;
const MenuConfig& menu_config = MenuConfig::instance();
- extra.menu_background.corner_radius =
- content_view_->GetMenuItem()->GetMenuController()->use_touchable_layout()
- ? menu_config.touchable_corner_radius
- : menu_config.corner_radius;
+ extra.menu_background.corner_radius = menu_config.CornerRadiusForMenu(
+ content_view_->GetMenuItem()->GetMenuController());
GetNativeTheme()->Paint(canvas->sk_canvas(),
NativeTheme::kMenuPopupBackground, NativeTheme::kNormal, bounds, extra);
}
@@ -277,8 +275,14 @@ void MenuScrollViewContainer::CreateDefaultBorder() {
bubble_border_ = nullptr;
const MenuConfig& menu_config = MenuConfig::instance();
-
- int padding = menu_config.use_outer_border && menu_config.corner_radius > 0
+ const ui::NativeTheme* native_theme = GetNativeTheme();
+ MenuController* controller =
+ content_view_->GetMenuItem()->GetMenuController();
+ bool use_outer_border =
+ menu_config.use_outer_border ||
+ (native_theme && native_theme->UsesHighContrastColors());
+ int corner_radius = menu_config.CornerRadiusForMenu(controller);
+ int padding = use_outer_border && corner_radius > 0
? kBorderPaddingDueToRoundedCorners
: 0;
@@ -286,14 +290,13 @@ void MenuScrollViewContainer::CreateDefaultBorder() {
const int horizontal_inset =
menu_config.menu_horizontal_border_size + padding;
- if (menu_config.use_outer_border) {
+ if (use_outer_border) {
SkColor color = GetNativeTheme()
? GetNativeTheme()->GetSystemColor(
ui::NativeTheme::kColorId_MenuBorderColor)
: gfx::kPlaceholderColor;
SetBorder(views::CreateBorderPainter(
- std::make_unique<views::RoundRectPainter>(color,
- menu_config.corner_radius),
+ std::make_unique<views::RoundRectPainter>(color, corner_radius),
gfx::Insets(vertical_inset, horizontal_inset)));
} else {
SetBorder(CreateEmptyBorder(vertical_inset, horizontal_inset,
diff --git a/chromium/ui/views/controls/menu/menu_separator.cc b/chromium/ui/views/controls/menu/menu_separator.cc
index 55bdcf82428..aa27d3c260b 100644
--- a/chromium/ui/views/controls/menu/menu_separator.cc
+++ b/chromium/ui/views/controls/menu/menu_separator.cc
@@ -23,6 +23,8 @@ void MenuSeparator::OnPaint(gfx::Canvas* canvas) {
const MenuConfig& menu_config = MenuConfig::instance();
int pos = 0;
int separator_thickness = menu_config.separator_thickness;
+ if (type_ == ui::DOUBLE_SEPARATOR)
+ separator_thickness = menu_config.double_separator_thickness;
switch (type_) {
case ui::LOWER_SEPARATOR:
pos = height() - separator_thickness;
@@ -30,12 +32,14 @@ void MenuSeparator::OnPaint(gfx::Canvas* canvas) {
case ui::UPPER_SEPARATOR:
break;
default:
- pos = height() / 2;
+ pos = (height() - separator_thickness) / 2;
break;
}
gfx::Rect paint_rect(0, pos, width(), separator_thickness);
- if (menu_config.use_outer_border)
+ if (type_ == ui::PADDED_SEPARATOR)
+ paint_rect.Inset(menu_config.padded_separator_left_margin, 0, 0, 0);
+ else if (menu_config.use_outer_border)
paint_rect.Inset(1, 0);
#if defined(OS_WIN)
@@ -70,6 +74,12 @@ gfx::Size MenuSeparator::CalculatePreferredSize() const {
case ui::UPPER_SEPARATOR:
height = menu_config.separator_upper_height;
break;
+ case ui::DOUBLE_SEPARATOR:
+ height = menu_config.double_separator_height;
+ break;
+ case ui::PADDED_SEPARATOR:
+ height = menu_config.separator_thickness;
+ break;
default:
height = menu_config.separator_height;
break;
diff --git a/chromium/ui/views/controls/menu/menu_types.h b/chromium/ui/views/controls/menu/menu_types.h
index d95ce0b33c0..03a0c72a5db 100644
--- a/chromium/ui/views/controls/menu/menu_types.h
+++ b/chromium/ui/views/controls/menu/menu_types.h
@@ -9,8 +9,7 @@ namespace views {
// Where a popup menu should be anchored to for non-RTL languages. The opposite
// position will be used if base::i18n:IsRTL() is true. The BUBBLE flags are
-// used when the menu should get enclosed by a bubble. Note that BUBBLE flags
-// should only be used with menus which have no children. The Fixed flags are
+// used when the menu should get enclosed by a bubble. The Fixed flags are
// used for the menus that have a fixed anchor position.
enum MenuAnchorPosition {
MENU_ANCHOR_TOPLEFT,
diff --git a/chromium/ui/views/controls/menu/submenu_view.cc b/chromium/ui/views/controls/menu/submenu_view.cc
index 9b244c9e9ea..b69a318e69f 100644
--- a/chromium/ui/views/controls/menu/submenu_view.cc
+++ b/chromium/ui/views/controls/menu/submenu_view.cc
@@ -182,8 +182,9 @@ gfx::Size SubmenuView::CalculatePreferredSize() const {
minimum_preferred_width_ - 2 * insets.width()));
if (GetMenuItem()->GetMenuController() &&
- GetMenuItem()->GetMenuController()->use_touchable_layout())
+ GetMenuItem()->GetMenuController()->use_touchable_layout()) {
width = std::max(touchable_minimum_width, width);
+ }
// Then, the height for that width.
int height = 0;
diff --git a/chromium/ui/views/controls/message_box_view.cc b/chromium/ui/views/controls/message_box_view.cc
index 27dc783cf4f..36db5787075 100644
--- a/chromium/ui/views/controls/message_box_view.cc
+++ b/chromium/ui/views/controls/message_box_view.cc
@@ -7,7 +7,6 @@
#include <stddef.h>
#include "base/i18n/rtl.h"
-#include "base/message_loop/message_loop.h"
#include "base/strings/string_split.h"
#include "base/strings/utf_string_conversions.h"
#include "ui/accessibility/ax_node_data.h"
diff --git a/chromium/ui/views/controls/native/native_view_host_aura.cc b/chromium/ui/views/controls/native/native_view_host_aura.cc
index 28033ad99d5..097bcd4879b 100644
--- a/chromium/ui/views/controls/native/native_view_host_aura.cc
+++ b/chromium/ui/views/controls/native/native_view_host_aura.cc
@@ -150,7 +150,8 @@ void NativeViewHostAura::RemovedFromWidget() {
bool NativeViewHostAura::SetCornerRadius(int corner_radius) {
#if defined(OS_WIN)
- // Layer masks don't work on Windows. See crbug.com/713359
+ // TODO(crbug/843250): On Aura, layer masks don't play with HiDPI. Fix this
+ // and enable this on Windows.
return false;
#else
mask_ = views::Painter::CreatePaintedLayer(
diff --git a/chromium/ui/views/controls/native/native_view_host_mac.mm b/chromium/ui/views/controls/native/native_view_host_mac.mm
index e6f31f85f3d..0ca2c4a6a41 100644
--- a/chromium/ui/views/controls/native/native_view_host_mac.mm
+++ b/chromium/ui/views/controls/native/native_view_host_mac.mm
@@ -12,6 +12,13 @@
#include "ui/views/widget/native_widget_mac.h"
#include "ui/views/widget/widget.h"
+// NSViews that can be drawn as a ui::Layer directly will implement this
+// interface. Calling cr_setParentLayer will embed the ui::Layer of the NSView
+// under |parentUiLayer|.
+@interface NSView (UICompositor)
+- (void)cr_setParentUiLayer:(ui::Layer*)parentUiLayer;
+@end
+
namespace views {
namespace {
@@ -32,6 +39,8 @@ void EnsureNativeViewHasNoChildWidgets(NSView* native_view) {
} // namespace
NativeViewHostMac::NativeViewHostMac(NativeViewHost* host) : host_(host) {
+ // Ensure that |host_| have its own ui::Layer and that it draw nothing.
+ host_->SetPaintToLayer(ui::LAYER_NOT_DRAWN);
}
NativeViewHostMac::~NativeViewHostMac() {
@@ -45,6 +54,9 @@ void NativeViewHostMac::AttachNativeView() {
DCHECK(!native_view_);
native_view_.reset([host_->native_view() retain]);
+ if ([native_view_ respondsToSelector:@selector(cr_setParentUiLayer:)])
+ [native_view_ cr_setParentUiLayer:host_->layer()];
+
EnsureNativeViewHasNoChildWidgets(native_view_);
BridgedNativeWidget* bridge = NativeWidgetMac::GetBridgeForNativeWindow(
host_->GetWidget()->GetNativeWindow());
@@ -71,6 +83,9 @@ void NativeViewHostMac::NativeViewDetaching(bool destroyed) {
[host_->native_view() setHidden:YES];
[host_->native_view() removeFromSuperview];
+ if ([native_view_ respondsToSelector:@selector(cr_setParentUiLayer:)])
+ [native_view_ cr_setParentUiLayer:nullptr];
+
EnsureNativeViewHasNoChildWidgets(host_->native_view());
BridgedNativeWidget* bridge = NativeWidgetMac::GetBridgeForNativeWindow(
host_->GetWidget()->GetNativeWindow());
diff --git a/chromium/ui/views/controls/prefix_selector.cc b/chromium/ui/views/controls/prefix_selector.cc
index b1195d51c67..6590a8e0b05 100644
--- a/chromium/ui/views/controls/prefix_selector.cc
+++ b/chromium/ui/views/controls/prefix_selector.cc
@@ -89,6 +89,12 @@ bool PrefixSelector::HasCompositionText() const {
return false;
}
+ui::TextInputClient::FocusReason PrefixSelector::GetFocusReason() const {
+ // TODO(https://crbug.com/824604): Implement this correctly.
+ NOTIMPLEMENTED_LOG_ONCE();
+ return ui::TextInputClient::FOCUS_REASON_OTHER;
+}
+
bool PrefixSelector::GetTextRange(gfx::Range* range) const {
*range = gfx::Range();
return false;
@@ -145,6 +151,12 @@ const std::string& PrefixSelector::GetClientSourceInfo() const {
return base::EmptyString();
}
+bool PrefixSelector::ShouldDoLearning() {
+ // TODO(https://crbug.com/311180): Implement this method.
+ NOTIMPLEMENTED_LOG_ONCE();
+ return false;
+}
+
void PrefixSelector::OnTextInput(const base::string16& text) {
// Small hack to filter out 'tab' and 'enter' input, as the expectation is
// that they are control characters and will not affect the currently-active
diff --git a/chromium/ui/views/controls/prefix_selector.h b/chromium/ui/views/controls/prefix_selector.h
index 1e70d1ad3ed..e3654eebbb3 100644
--- a/chromium/ui/views/controls/prefix_selector.h
+++ b/chromium/ui/views/controls/prefix_selector.h
@@ -44,6 +44,7 @@ class VIEWS_EXPORT PrefixSelector : public ui::TextInputClient {
bool GetCompositionCharacterBounds(uint32_t index,
gfx::Rect* rect) const override;
bool HasCompositionText() const override;
+ FocusReason GetFocusReason() const override;
bool GetTextRange(gfx::Range* range) const override;
bool GetCompositionTextRange(gfx::Range* range) const override;
bool GetSelectionRange(gfx::Range* range) const override;
@@ -60,6 +61,7 @@ class VIEWS_EXPORT PrefixSelector : public ui::TextInputClient {
bool IsTextEditCommandEnabled(ui::TextEditCommand command) const override;
void SetTextEditCommandForNextKeyEvent(ui::TextEditCommand command) override;
const std::string& GetClientSourceInfo() const override;
+ bool ShouldDoLearning() override;
private:
// Invoked when text is typed. Tries to change the selection appropriately.
diff --git a/chromium/ui/views/controls/scroll_view.cc b/chromium/ui/views/controls/scroll_view.cc
index 06a8b1625c7..e58ef7d3cdc 100644
--- a/chromium/ui/views/controls/scroll_view.cc
+++ b/chromium/ui/views/controls/scroll_view.cc
@@ -8,6 +8,8 @@
#include "base/logging.h"
#include "base/macros.h"
#include "ui/base/material_design/material_design_controller.h"
+#include "ui/base/ui_base_features.h"
+#include "ui/compositor/overscroll/scroll_input_handler.h"
#include "ui/events/event.h"
#include "ui/gfx/canvas.h"
#include "ui/native_theme/native_theme.h"
@@ -24,15 +26,6 @@ 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
-};
-
class ScrollCornerView : public View {
public:
ScrollCornerView() {}
@@ -187,8 +180,8 @@ ScrollView::ScrollView()
min_height_(-1),
max_height_(-1),
hide_horizontal_scrollbar_(false),
- scroll_with_layers_enabled_(
- base::FeatureList::IsEnabled(kToolkitViewsScrollWithLayers)) {
+ scroll_with_layers_enabled_(base::FeatureList::IsEnabled(
+ features::kUiCompositorScrollWithLayers)) {
set_notify_enter_exit_on_child(true);
AddChildView(contents_viewport_);
@@ -221,6 +214,14 @@ ScrollView::ScrollView()
more_content_bottom_->SetPaintToLayer();
}
UpdateBackground();
+
+ if (ui::MaterialDesignController::IsSecondaryUiMaterial()) {
+ focus_ring_ = FocusRing::Install(this);
+ focus_ring_->SetHasFocusPredicate([](View* view) -> bool {
+ auto* v = static_cast<ScrollView*>(view);
+ return v->draw_focus_indicator_;
+ });
+ }
}
ScrollView::~ScrollView() {
@@ -334,17 +335,10 @@ void ScrollView::SetHasFocusIndicator(bool has_focus_indicator) {
return;
draw_focus_indicator_ = has_focus_indicator;
- if (ui::MaterialDesignController::IsSecondaryUiMaterial()) {
- DCHECK_EQ(draw_focus_indicator_, !focus_ring_);
- if (has_focus_indicator) {
- focus_ring_ = FocusRing::Install(this);
- } else {
- FocusRing::Uninstall(this);
- focus_ring_ = nullptr;
- }
- } else {
+ if (ui::MaterialDesignController::IsSecondaryUiMaterial())
+ focus_ring_->SchedulePaint();
+ else
UpdateBorder();
- }
SchedulePaint();
}
@@ -519,6 +513,30 @@ void ScrollView::Layout() {
container_size.SetToMax(viewport_bounds.size());
contents_->SetBoundsRect(gfx::Rect(container_size));
contents_->layer()->SetScrollable(viewport_bounds.size());
+
+ // Flip the viewport with layer transforms under RTL. Note the net effect is
+ // to flip twice, so the text is not mirrored. This is necessary because
+ // compositor scrolling is not RTL-aware. So although a toolkit-views layout
+ // will flip, increasing a horizontal gfx::ScrollOffset will move content to
+ // the left, regardless of RTL. A gfx::ScrollOffset must be positive, so to
+ // move (unscrolled) content to the right, we need to flip the viewport
+ // layer. That would flip all the content as well, so flip (and translate)
+ // the content layer. Compensating in this way allows the scrolling/offset
+ // logic to remain the same when scrolling via layers or bounds offsets.
+ if (base::i18n::IsRTL()) {
+ gfx::Transform flip;
+ flip.Translate(viewport_bounds.width(), 0);
+ flip.Scale(-1, 1);
+ contents_viewport_->layer()->SetTransform(flip);
+
+ // Add `contents_->width() - viewport_width` to the translation step. This
+ // is to prevent the top-left of the (flipped) contents aligning to the
+ // top-left of the viewport. Instead, the top-right should align in RTL.
+ gfx::Transform shift;
+ shift.Translate(2 * contents_->width() - viewport_bounds.width(), 0);
+ shift.Scale(-1, 1);
+ contents_->layer()->SetTransform(shift);
+ }
}
header_viewport_->SetBounds(contents_x, contents_y,
@@ -552,6 +570,7 @@ bool ScrollView::OnKeyPressed(const ui::KeyEvent& event) {
bool ScrollView::OnMouseWheel(const ui::MouseWheelEvent& e) {
bool processed = false;
+ // TODO(https://crbug.com/615948): Use composited scrolling.
if (vert_sb_->visible())
processed = vert_sb_->OnMouseWheel(e);
@@ -562,13 +581,18 @@ bool ScrollView::OnMouseWheel(const ui::MouseWheelEvent& e) {
}
void ScrollView::OnScrollEvent(ui::ScrollEvent* event) {
-#if defined(OS_MACOSX)
if (!contents_)
return;
- // 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.
+ ui::ScrollInputHandler* compositor_scroller =
+ GetWidget()->GetCompositor()->scroll_input_handler();
+ if (compositor_scroller) {
+ DCHECK(scroll_with_layers_enabled_);
+ if (compositor_scroller->OnScrollEvent(*event, contents_->layer())) {
+ event->SetHandled();
+ event->StopPropagation();
+ }
+ }
// A direction might not be known when the event stream starts, notify both
// scrollbars that they may be about scroll, or that they may need to cancel
@@ -577,7 +601,6 @@ void ScrollView::OnScrollEvent(ui::ScrollEvent* event) {
horiz_sb_->ObserveScrollEvent(*event);
if (vert_sb_)
vert_sb_->ObserveScrollEvent(*event);
-#endif
}
void ScrollView::OnGestureEvent(ui::GestureEvent* event) {
@@ -589,6 +612,7 @@ void ScrollView::OnGestureEvent(ui::GestureEvent* event) {
event->type() == ui::ET_GESTURE_SCROLL_END ||
event->type() == ui::ET_SCROLL_FLING_START;
+ // TODO(https://crbug.com/615948): Use composited scrolling.
if (vert_sb_->visible()) {
if (vert_sb_->bounds().Contains(event->location()) || scroll_event)
vert_sb_->OnGestureEvent(event);
diff --git a/chromium/ui/views/controls/scroll_view.h b/chromium/ui/views/controls/scroll_view.h
index ff7f71b19c3..e0e279ce914 100644
--- a/chromium/ui/views/controls/scroll_view.h
+++ b/chromium/ui/views/controls/scroll_view.h
@@ -11,6 +11,7 @@
#include "base/gtest_prod_util.h"
#include "base/macros.h"
#include "ui/native_theme/native_theme.h"
+#include "ui/views/controls/focus_ring.h"
#include "ui/views/controls/scrollbar/scroll_bar.h"
namespace gfx {
@@ -246,12 +247,12 @@ class VIEWS_EXPORT ScrollView : public View, public ScrollBarController {
// it overflows.
bool draw_overflow_indicator_ = true;
- // Focus ring, if one is installed.
- View* focus_ring_ = nullptr;
-
// Set to true if the scroll with layers feature is enabled.
const bool scroll_with_layers_enabled_;
+ // The focus ring for this ScrollView.
+ std::unique_ptr<FocusRing> focus_ring_;
+
DISALLOW_COPY_AND_ASSIGN(ScrollView);
};
diff --git a/chromium/ui/views/controls/scroll_view_unittest.cc b/chromium/ui/views/controls/scroll_view_unittest.cc
index 6b818c25420..d2596d71109 100644
--- a/chromium/ui/views/controls/scroll_view_unittest.cc
+++ b/chromium/ui/views/controls/scroll_view_unittest.cc
@@ -6,9 +6,13 @@
#include "base/macros.h"
#include "base/run_loop.h"
+#include "base/test/icu_test_util.h"
+#include "base/test/scoped_feature_list.h"
#include "base/test/test_timeouts.h"
#include "base/threading/thread_task_runner_handle.h"
+#include "build/build_config.h"
#include "testing/gtest/include/gtest/gtest.h"
+#include "ui/base/ui_base_features.h"
#include "ui/compositor/scoped_animation_duration_scale_mode.h"
#include "ui/events/test/event_generator.h"
#include "ui/views/border.h"
@@ -229,8 +233,8 @@ class ScrollViewTest : public ViewsTestBase {
class WidgetScrollViewTest : public test::WidgetTest,
public ui::CompositorObserver {
public:
- static const int kDefaultHeight = 100;
- static const int kDefaultWidth = 100;
+ static constexpr int kDefaultHeight = 100;
+ static constexpr int kDefaultWidth = 100;
WidgetScrollViewTest() {}
@@ -303,6 +307,9 @@ class WidgetScrollViewTest : public test::WidgetTest,
WidgetTest::TearDown();
}
+ protected:
+ Widget* widget() const { return widget_; }
+
private:
// ui::CompositorObserver:
void OnCompositingDidCommit(ui::Compositor* compositor) override {
@@ -330,8 +337,74 @@ class WidgetScrollViewTest : public test::WidgetTest,
DISALLOW_COPY_AND_ASSIGN(WidgetScrollViewTest);
};
-const int WidgetScrollViewTest::kDefaultHeight;
-const int WidgetScrollViewTest::kDefaultWidth;
+constexpr int WidgetScrollViewTest::kDefaultHeight;
+constexpr int WidgetScrollViewTest::kDefaultWidth;
+
+// A gtest parameter to permute over whether ScrollView uses a left-to-right or
+// right-to-left locale, or whether it uses ui::Layers or View bounds offsets to
+// position contents (i.e. features::kUiCompositorScrollWithLayers).
+enum class UiConfig { kLtr, kLtrWithLayers, kRtl, kRtlWithLayers };
+
+class WidgetScrollViewTestRTLAndLayers
+ : public WidgetScrollViewTest,
+ public ::testing::WithParamInterface<UiConfig> {
+ public:
+ WidgetScrollViewTestRTLAndLayers() : locale_(IsTestingRtl() ? "he" : "en") {
+ if (IsTestingLayers()) {
+ layer_config_.InitAndEnableFeature(
+ features::kUiCompositorScrollWithLayers);
+ } else {
+ layer_config_.InitAndDisableFeature(
+ features::kUiCompositorScrollWithLayers);
+ }
+ }
+
+ bool IsTestingRtl() const {
+ return GetParam() == UiConfig::kRtl ||
+ GetParam() == UiConfig::kRtlWithLayers;
+ }
+
+ bool IsTestingLayers() const {
+ return GetParam() == UiConfig::kLtrWithLayers ||
+ GetParam() == UiConfig::kRtlWithLayers;
+ }
+
+ // Returns a point in the coordinate space of |target| by translating a point
+ // inset one pixel from the top of the Widget and one pixel on the leading
+ // side of the Widget. There should be no scroll bar on this side. If
+ // |flip_result| is true, automatically account for flipped coordinates in
+ // RTL.
+ gfx::Point HitTestInCorner(View* target, bool flip_result = true) const {
+ const gfx::Point test_mouse_point_in_root =
+ IsTestingRtl() ? gfx::Point(kDefaultWidth - 1, 1) : gfx::Point(1, 1);
+ gfx::Point point = test_mouse_point_in_root;
+ View::ConvertPointToTarget(widget()->GetRootView(), target, &point);
+ if (flip_result)
+ return gfx::Point(target->GetMirroredXInView(point.x()), point.y());
+ return point;
+ }
+
+ private:
+ base::test::ScopedRestoreICUDefaultLocale locale_;
+ base::test::ScopedFeatureList layer_config_;
+
+ DISALLOW_COPY_AND_ASSIGN(WidgetScrollViewTestRTLAndLayers);
+};
+
+std::string UiConfigToString(const testing::TestParamInfo<UiConfig>& info) {
+ switch (info.param) {
+ case UiConfig::kLtr:
+ return "LTR";
+ case UiConfig::kLtrWithLayers:
+ return "LTR_LAYERS";
+ case UiConfig::kRtl:
+ return "RTL";
+ case UiConfig::kRtlWithLayers:
+ return "RTL_LAYERS";
+ }
+ NOTREACHED();
+ return std::string();
+}
// Verifies the viewport is sized to fit the available space.
TEST_F(ScrollViewTest, ViewportSizedToFit) {
@@ -1436,26 +1509,136 @@ TEST_F(WidgetScrollViewTest, EventLocation) {
contents->last_location());
}
+// Ensure behavior of ScrollRectToVisible() is consistent when scrolling with
+// and without layers, and under LTR and RTL.
+TEST_P(WidgetScrollViewTestRTLAndLayers, ScrollOffsetWithoutLayers) {
+ // Set up with both scrollers. And a nested view hierarchy like:
+ // +-------------+
+ // |XX |
+ // | +----------|
+ // | | |
+ // | | +-------|
+ // | | | |
+ // | | | etc. |
+ // | | | |
+ // +-------------+
+ // Note that "XX" indicates the size of the viewport.
+ constexpr int kNesting = 5;
+ constexpr int kCellWidth = kDefaultWidth;
+ constexpr int kCellHeight = kDefaultHeight;
+ constexpr gfx::Size kContentSize(kCellWidth * kNesting,
+ kCellHeight * kNesting);
+ ScrollView* scroll_view = AddScrollViewWithContentSize(kContentSize, false);
+ ScrollViewTestApi test_api(scroll_view);
+ EXPECT_EQ(gfx::ScrollOffset(0, 0), test_api.CurrentOffset());
+
+ // Sanity check that the contents has a layer iff testing layers.
+ EXPECT_EQ(IsTestingLayers(), !!scroll_view->contents()->layer());
+
+ if (IsTestingRtl()) {
+ // Sanity check the hit-testing logic on the root view. That is, verify that
+ // coordinates really do flip in RTL. The difference inside the viewport is
+ // that the flipping should occur consistently in the entire contents (not
+ // just the visible contents), and take into account the scroll offset.
+ EXPECT_EQ(gfx::Point(kDefaultWidth - 1, 1),
+ HitTestInCorner(scroll_view->GetWidget()->GetRootView(), false));
+ EXPECT_EQ(gfx::Point(kContentSize.width() - 1, 1),
+ HitTestInCorner(scroll_view->contents(), false));
+ } else {
+ EXPECT_EQ(gfx::Point(1, 1),
+ HitTestInCorner(scroll_view->GetWidget()->GetRootView(), false));
+ EXPECT_EQ(gfx::Point(1, 1),
+ HitTestInCorner(scroll_view->contents(), false));
+ }
+
+ // Test vertical scrolling using coordinates on the contents canvas.
+ gfx::Rect offset(0, kCellHeight * 2, kCellWidth, kCellHeight);
+ scroll_view->contents()->ScrollRectToVisible(offset);
+ EXPECT_EQ(gfx::ScrollOffset(0, offset.y()), test_api.CurrentOffset());
+
+ // Rely on auto-flipping for this and future HitTestInCorner() calls.
+ EXPECT_EQ(gfx::Point(1, kCellHeight * 2 + 1),
+ HitTestInCorner(scroll_view->contents()));
+
+ // Test horizontal scrolling.
+ offset.set_x(kCellWidth * 2);
+ scroll_view->contents()->ScrollRectToVisible(offset);
+ EXPECT_EQ(gfx::ScrollOffset(offset.x(), offset.y()),
+ test_api.CurrentOffset());
+ EXPECT_EQ(gfx::Point(kCellWidth * 2 + 1, kCellHeight * 2 + 1),
+ HitTestInCorner(scroll_view->contents()));
+
+ // Reset the scrolling.
+ scroll_view->contents()->ScrollRectToVisible(gfx::Rect(0, 0, 1, 1));
+
+ // Test transformations through a nested view hierarchy.
+ View* deepest_view = scroll_view->contents();
+ constexpr gfx::Rect kCellRect(kCellWidth, kCellHeight, kContentSize.width(),
+ kContentSize.height());
+ for (int i = 1; i < kNesting; ++i) {
+ SCOPED_TRACE(testing::Message("Nesting = ") << i);
+ View* child = new View;
+ child->SetBoundsRect(kCellRect);
+ deepest_view->AddChildView(child);
+ deepest_view = child;
+
+ // Add a view in one quadrant. Scrolling just this view should only scroll
+ // far enough for it to become visible. That is, it should be positioned at
+ // the bottom right of the viewport, not the top-left. But since there are
+ // scroll bars, the scroll offset needs to go "a bit more".
+ View* partial_view = new View;
+ partial_view->SetSize(gfx::Size(kCellWidth / 3, kCellHeight / 3));
+ deepest_view->AddChildView(partial_view);
+ partial_view->ScrollViewToVisible();
+ int x_offset_in_cell = kCellWidth - partial_view->width();
+ if (!scroll_view->horizontal_scroll_bar()->OverlapsContent())
+ x_offset_in_cell -= scroll_view->horizontal_scroll_bar()->GetThickness();
+ int y_offset_in_cell = kCellHeight - partial_view->height();
+ if (!scroll_view->vertical_scroll_bar()->OverlapsContent())
+ y_offset_in_cell -= scroll_view->vertical_scroll_bar()->GetThickness();
+ EXPECT_EQ(gfx::ScrollOffset(kCellWidth * i - x_offset_in_cell,
+ kCellHeight * i - y_offset_in_cell),
+ test_api.CurrentOffset());
+
+ // Now scroll the rest.
+ deepest_view->ScrollViewToVisible();
+ EXPECT_EQ(gfx::ScrollOffset(kCellWidth * i, kCellHeight * i),
+ test_api.CurrentOffset());
+
+ // The partial view should now be at the top-left of the viewport (top-right
+ // in RTL).
+ EXPECT_EQ(gfx::Point(1, 1), HitTestInCorner(partial_view));
+
+ gfx::Point origin;
+ View::ConvertPointToWidget(partial_view, &origin);
+ constexpr gfx::Point kTestPointRTL(kDefaultWidth - kCellWidth / 3, 0);
+ EXPECT_EQ(IsTestingRtl() ? kTestPointRTL : gfx::Point(), origin);
+ }
+
+ // Scrolling to the deepest view should have moved the viewport so that the
+ // (kNesting - 1) parent views are all off-screen.
+ EXPECT_EQ(gfx::ScrollOffset(kCellWidth * (kNesting - 1),
+ kCellHeight * (kNesting - 1)),
+ test_api.CurrentOffset());
+}
+
// 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);
+TEST_P(WidgetScrollViewTestRTLAndLayers, ScrollOffsetUsingLayers) {
+ // Set up with both scrollers, but don't commit the layer changes yet.
+ ScrollView* scroll_view = AddScrollViewWithContentSize(
+ gfx::Size(kDefaultWidth * 5, 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);
+ gfx::Rect offset(0, kDefaultHeight * 2, kDefaultWidth, 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
+ EXPECT_EQ(IsTestingLayers(), !!container->layer());
if (!container->layer())
return;
@@ -1493,6 +1676,60 @@ TEST_F(WidgetScrollViewTest, ScrollOffsetUsingLayers) {
EXPECT_TRUE(compositor->GetScrollOffsetForLayer(layer_id, &impl_offset));
EXPECT_EQ(gfx::ScrollOffset(0, offset.y()), impl_offset);
+
+ // Test horizontal scrolling.
+ offset.set_x(kDefaultWidth * 2);
+ scroll_view->contents()->ScrollRectToVisible(offset);
+ EXPECT_EQ(gfx::ScrollOffset(offset.x(), offset.y()),
+ test_api.CurrentOffset());
+
+ EXPECT_TRUE(compositor->GetScrollOffsetForLayer(layer_id, &impl_offset));
+ EXPECT_EQ(gfx::ScrollOffset(offset.x(), offset.y()), impl_offset);
}
+// Tests to see the scroll events are handled correctly in composited and
+// non-composited scrolling.
+TEST_F(WidgetScrollViewTest, CompositedScrollEvents) {
+ // Set up with a vertical scroll bar.
+ ScrollView* scroll_view =
+ AddScrollViewWithContentSize(gfx::Size(10, kDefaultHeight * 5));
+ ScrollViewTestApi test_api(scroll_view);
+
+ // Create a fake scroll event and send it to the scroll view.
+ ui::ScrollEvent scroll(ui::ET_SCROLL, gfx::Point(), base::TimeTicks::Now(), 0,
+ 0, -10, 0, -10, 3);
+ EXPECT_FALSE(scroll.handled());
+ EXPECT_FALSE(scroll.stopped_propagation());
+ scroll_view->OnScrollEvent(&scroll);
+
+ // Check to see if the scroll event is handled by the scroll view.
+ if (base::FeatureList::IsEnabled(features::kUiCompositorScrollWithLayers)) {
+ // If UiCompositorScrollWithLayers is enabled, the event is set handled
+ // and its propagation is stopped.
+ EXPECT_TRUE(scroll.handled());
+ EXPECT_TRUE(scroll.stopped_propagation());
+ } else {
+ // If UiCompositorScrollWithLayers is disabled, the event isn't handled.
+ // This informs Widget::OnScrollEvent() to convert to a MouseWheel event
+ // and dispatch again. Simulate that.
+ EXPECT_FALSE(scroll.handled());
+ EXPECT_FALSE(scroll.stopped_propagation());
+ EXPECT_EQ(gfx::ScrollOffset(), test_api.CurrentOffset());
+
+ ui::MouseWheelEvent wheel(scroll);
+ scroll_view->OnMouseEvent(&wheel);
+ }
+
+ // Check if the scroll view has been offset.
+ EXPECT_EQ(gfx::ScrollOffset(0, 10), test_api.CurrentOffset());
+}
+
+INSTANTIATE_TEST_CASE_P(,
+ WidgetScrollViewTestRTLAndLayers,
+ ::testing::Values(UiConfig::kLtr,
+ UiConfig::kRtl,
+ UiConfig::kLtrWithLayers,
+ UiConfig::kRtlWithLayers),
+ &UiConfigToString);
+
} // 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 9add6bb5776..bf652746430 100644
--- a/chromium/ui/views/controls/scrollbar/base_scroll_bar.cc
+++ b/chromium/ui/views/controls/scrollbar/base_scroll_bar.cc
@@ -8,7 +8,6 @@
#include "base/bind_helpers.h"
#include "base/callback.h"
#include "base/compiler_specific.h"
-#include "base/message_loop/message_loop.h"
#include "base/strings/string16.h"
#include "base/strings/utf_string_conversions.h"
#include "build/build_config.h"
diff --git a/chromium/ui/views/controls/slider.cc b/chromium/ui/views/controls/slider.cc
index 015c695e102..cf767aee4c2 100644
--- a/chromium/ui/views/controls/slider.cc
+++ b/chromium/ui/views/controls/slider.cc
@@ -7,7 +7,7 @@
#include <algorithm>
#include "base/logging.h"
-#include "base/message_loop/message_loop.h"
+#include "base/message_loop/message_loop_current.h"
#include "base/strings/stringprintf.h"
#include "base/strings/utf_string_conversions.h"
#include "cc/paint/paint_flags.h"
@@ -138,7 +138,7 @@ void Slider::SetValueInternal(float value, SliderChangeReason reason) {
if (listener_)
listener_->SliderValueChanged(this, value_, old_value, reason);
- if (old_value_valid && base::MessageLoop::current()) {
+ if (old_value_valid && base::MessageLoopCurrent::Get()) {
// 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.
if (!move_animation_) {
diff --git a/chromium/ui/views/controls/styled_label.cc b/chromium/ui/views/controls/styled_label.cc
index 5e7982dcdb9..1f2d6fbcd4e 100644
--- a/chromium/ui/views/controls/styled_label.cc
+++ b/chromium/ui/views/controls/styled_label.cc
@@ -123,9 +123,7 @@ StyledLabel::StyledLabel(const base::string16& text,
width_at_last_layout_(0),
displayed_on_background_color_(SkColorSetRGB(0xFF, 0xFF, 0xFF)),
displayed_on_background_color_set_(false),
- auto_color_readability_enabled_(true),
- horizontal_alignment_(base::i18n::IsRTL() ? gfx::ALIGN_RIGHT
- : gfx::ALIGN_LEFT) {
+ auto_color_readability_enabled_(true) {
base::TrimWhitespace(text, base::TRIM_TRAILING, &text_);
}
@@ -271,7 +269,7 @@ void StyledLabel::SetHorizontalAlignment(gfx::HorizontalAlignment alignment) {
if (horizontal_alignment_ == alignment)
return;
horizontal_alignment_ = alignment;
- SchedulePaint();
+ PreferredSizeChanged();
}
void StyledLabel::ClearStyleRanges() {
diff --git a/chromium/ui/views/controls/styled_label.h b/chromium/ui/views/controls/styled_label.h
index 3268ad50e2c..ae5bac5511a 100644
--- a/chromium/ui/views/controls/styled_label.h
+++ b/chromium/ui/views/controls/styled_label.h
@@ -219,7 +219,7 @@ class VIEWS_EXPORT StyledLabel : public View, public LinkListener {
// The horizontal alignment. This value is flipped for RTL. The default
// behavior is to align left in LTR UI and right in RTL UI.
- gfx::HorizontalAlignment horizontal_alignment_;
+ gfx::HorizontalAlignment horizontal_alignment_ = gfx::ALIGN_LEFT;
DISALLOW_COPY_AND_ASSIGN(StyledLabel);
};
diff --git a/chromium/ui/views/controls/styled_label_unittest.cc b/chromium/ui/views/controls/styled_label_unittest.cc
index 7aa780d2c8f..4f18b6880c4 100644
--- a/chromium/ui/views/controls/styled_label_unittest.cc
+++ b/chromium/ui/views/controls/styled_label_unittest.cc
@@ -9,8 +9,10 @@
#include <memory>
#include <string>
+#include "base/i18n/base_i18n_switches.h"
#include "base/macros.h"
#include "base/strings/utf_string_conversions.h"
+#include "base/test/icu_test_util.h"
#include "base/test/scoped_feature_list.h"
#include "build/build_config.h"
#include "testing/gtest/include/gtest/gtest.h"
@@ -707,48 +709,64 @@ TEST_F(StyledLabelTest, LineWrapperWithCustomView) {
styled()->GetHeightForWidth(100));
}
-TEST_F(StyledLabelTest, LeftAlignment) {
- const std::string multiline_text("one\ntwo\nthree");
- InitStyledLabel(multiline_text);
- styled()->SetHorizontalAlignment(gfx::ALIGN_LEFT);
+TEST_F(StyledLabelTest, AlignmentInLTR) {
+ const std::string text("text");
+ InitStyledLabel(text);
styled()->SetBounds(0, 0, 1000, 1000);
styled()->Layout();
- ASSERT_EQ(3, styled()->child_count());
+ ASSERT_EQ(1, styled()->child_count());
+
+ // Test the default alignment puts the text on the leading side (left).
EXPECT_EQ(0, styled()->child_at(0)->bounds().x());
- EXPECT_EQ(0, styled()->child_at(1)->bounds().x());
- EXPECT_EQ(0, styled()->child_at(2)->bounds().x());
-}
-TEST_F(StyledLabelTest, RightAlignment) {
- const std::string multiline_text("one\ntwo\nthree");
- InitStyledLabel(multiline_text);
styled()->SetHorizontalAlignment(gfx::ALIGN_RIGHT);
- styled()->SetBounds(0, 0, 1000, 1000);
styled()->Layout();
- ASSERT_EQ(3, styled()->child_count());
EXPECT_EQ(1000, styled()->child_at(0)->bounds().right());
- EXPECT_EQ(1000, styled()->child_at(1)->bounds().right());
- EXPECT_EQ(1000, styled()->child_at(2)->bounds().right());
-}
-TEST_F(StyledLabelTest, CenterAlignment) {
- const std::string multiline_text("one\ntwo\nthree");
- InitStyledLabel(multiline_text);
+ styled()->SetHorizontalAlignment(gfx::ALIGN_LEFT);
+ styled()->Layout();
+ EXPECT_EQ(0, styled()->child_at(0)->bounds().x());
+
styled()->SetHorizontalAlignment(gfx::ALIGN_CENTER);
+ styled()->Layout();
+ Label label(ASCIIToUTF16(text));
+ EXPECT_EQ((1000 - label.GetPreferredSize().width()) / 2,
+ styled()->child_at(0)->bounds().x());
+}
+
+TEST_F(StyledLabelTest, AlignmentInRTL) {
+ // |g_icu_text_direction| is cached to prevent reading new commandline switch.
+ // Set |g_icu_text_direction| to |UNKNOWN_DIRECTION| in order to read the new
+ // commandline switch.
+ base::test::ScopedRestoreICUDefaultLocale scoped_locale("en_US");
+ base::CommandLine::ForCurrentProcess()->AppendSwitchASCII(
+ switches::kForceUIDirection, switches::kForceDirectionRTL);
+
+ const std::string text("text");
+ InitStyledLabel(text);
styled()->SetBounds(0, 0, 1000, 1000);
styled()->Layout();
+ ASSERT_EQ(1, styled()->child_count());
- Label label_one(ASCIIToUTF16("one"));
- Label label_two(ASCIIToUTF16("two"));
- Label label_three(ASCIIToUTF16("three"));
+ // Test the default alignment puts the text on the leading side (right).
+ // Note that x-coordinates in RTL place the origin (0) on the right.
+ EXPECT_EQ(0, styled()->child_at(0)->bounds().x());
- ASSERT_EQ(3, styled()->child_count());
- EXPECT_EQ((1000 - label_one.GetPreferredSize().width()) / 2,
+ // Setting |ALIGN_LEFT| should be flipped to |ALIGN_RIGHT|.
+ styled()->SetHorizontalAlignment(gfx::ALIGN_LEFT);
+ styled()->Layout();
+ EXPECT_EQ(1000, styled()->child_at(0)->bounds().right());
+
+ // Setting |ALIGN_RIGHT| should be flipped to |ALIGN_LEFT|.
+ styled()->SetHorizontalAlignment(gfx::ALIGN_RIGHT);
+ styled()->Layout();
+ EXPECT_EQ(0, styled()->child_at(0)->bounds().x());
+
+ styled()->SetHorizontalAlignment(gfx::ALIGN_CENTER);
+ styled()->Layout();
+ Label label(ASCIIToUTF16(text));
+ EXPECT_EQ((1000 - label.GetPreferredSize().width()) / 2,
styled()->child_at(0)->bounds().x());
- EXPECT_EQ((1000 - label_two.GetPreferredSize().width()) / 2,
- styled()->child_at(1)->bounds().x());
- EXPECT_EQ((1000 - label_three.GetPreferredSize().width()) / 2,
- styled()->child_at(2)->bounds().x());
}
TEST_P(MDStyledLabelTest, ViewsCenteredWithLinkAndCustomView) {
diff --git a/chromium/ui/views/controls/tabbed_pane/tabbed_pane.cc b/chromium/ui/views/controls/tabbed_pane/tabbed_pane.cc
index bc33e72aca4..17f9259f012 100644
--- a/chromium/ui/views/controls/tabbed_pane/tabbed_pane.cc
+++ b/chromium/ui/views/controls/tabbed_pane/tabbed_pane.cc
@@ -36,17 +36,17 @@ namespace {
// TODO(markusheintz|msw): Use NativeTheme colors.
constexpr SkColor kTabTitleColor_InactiveBorder =
- SkColorSetARGBMacro(0xFF, 0x64, 0x64, 0x64);
+ SkColorSetARGB(0xFF, 0x64, 0x64, 0x64);
constexpr SkColor kTabTitleColor_InactiveHighlight =
- SkColorSetARGBMacro(0xFF, 0x80, 0x86, 0x8B);
+ SkColorSetARGB(0xFF, 0x80, 0x86, 0x8B);
constexpr SkColor kTabTitleColor_ActiveBorder = SK_ColorBLACK;
constexpr SkColor kTabTitleColor_ActiveHighlight =
- SkColorSetARGBMacro(0xFF, 0x42, 0x85, 0xF4);
+ SkColorSetARGB(0xFF, 0x42, 0x85, 0xF4);
const SkColor kTabTitleColor_Hovered = SK_ColorBLACK;
const SkColor kTabBorderColor = SkColorSetRGB(0xC8, 0xC8, 0xC8);
const SkScalar kTabBorderThickness = 1.0f;
constexpr SkColor kTabHighlightBackgroundColor =
- SkColorSetARGBMacro(0xFF, 0xE8, 0xF0, 0xFE);
+ SkColorSetARGB(0xFF, 0xE8, 0xF0, 0xFE);
constexpr int kTabHighlightBorderRadius = 32;
constexpr int kTabHighlightPreferredHeight = 32;
constexpr int kTabHighlightPreferredWidth = 208;
diff --git a/chromium/ui/views/controls/tabbed_pane/tabbed_pane_unittest.cc b/chromium/ui/views/controls/tabbed_pane/tabbed_pane_unittest.cc
index f499e202ee4..5ad416d35b1 100644
--- a/chromium/ui/views/controls/tabbed_pane/tabbed_pane_unittest.cc
+++ b/chromium/ui/views/controls/tabbed_pane/tabbed_pane_unittest.cc
@@ -7,7 +7,6 @@
#include <memory>
#include "base/macros.h"
-#include "base/message_loop/message_loop.h"
#include "base/strings/utf_string_conversions.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "ui/accessibility/ax_action_data.h"
diff --git a/chromium/ui/views/controls/table/table_view.cc b/chromium/ui/views/controls/table/table_view.cc
index d7f37cf103f..c4325de2745 100644
--- a/chromium/ui/views/controls/table/table_view.cc
+++ b/chromium/ui/views/controls/table/table_view.cc
@@ -31,15 +31,12 @@
#include "ui/views/layout/layout_provider.h"
#include "ui/views/style/typography.h"
-// Size of images.
-static const int kImageSize = 16;
-
-static const int kGroupingIndicatorSize = 6;
-
namespace views {
namespace {
+constexpr int kGroupingIndicatorSize = 6;
+
// Returns result, unless ascending is false in which case -result is returned.
int SwapCompareResult(int result, bool ascending) {
return ascending ? result : -result;
@@ -588,14 +585,15 @@ void TableView::OnPaint(gfx::Canvas* canvas) {
if (j == 0 && table_type_ == ICON_AND_TEXT) {
gfx::ImageSkia image = model_->GetIcon(model_index);
if (!image.isNull()) {
- int image_x = GetMirroredXWithWidthInView(text_x, kImageSize);
+ int image_x =
+ GetMirroredXWithWidthInView(text_x, ui::TableModel::kIconSize);
canvas->DrawImageInt(
- image, 0, 0, image.width(), image.height(),
- image_x,
- cell_bounds.y() + (cell_bounds.height() - kImageSize) / 2,
- kImageSize, kImageSize, true);
+ image, 0, 0, image.width(), image.height(), image_x,
+ cell_bounds.y() +
+ (cell_bounds.height() - ui::TableModel::kIconSize) / 2,
+ ui::TableModel::kIconSize, ui::TableModel::kIconSize, true);
}
- text_x += kImageSize + cell_element_spacing;
+ text_x += ui::TableModel::kIconSize + cell_element_spacing;
}
if (text_x < cell_bounds.right() - cell_margin) {
canvas->DrawStringRectWithFlags(
@@ -743,7 +741,7 @@ void TableView::AdjustCellBoundsForText(int visible_column_index,
if (grouper_)
text_x += kGroupingIndicatorSize + cell_element_spacing;
if (table_type_ == ICON_AND_TEXT)
- text_x += kImageSize + cell_element_spacing;
+ text_x += ui::TableModel::kIconSize + cell_element_spacing;
}
bounds->set_x(text_x);
bounds->set_width(std::max(0, bounds->right() - cell_margin - text_x));
@@ -770,7 +768,7 @@ void TableView::UpdateVisibleColumnSizes() {
const int cell_element_spacing = GetCellElementSpacing();
int first_column_padding = 0;
if (table_type_ == ICON_AND_TEXT && header_)
- first_column_padding += kImageSize + cell_element_spacing;
+ first_column_padding += ui::TableModel::kIconSize + cell_element_spacing;
if (grouper_)
first_column_padding += kGroupingIndicatorSize + cell_element_spacing;
diff --git a/chromium/ui/views/controls/textfield/textfield.cc b/chromium/ui/views/controls/textfield/textfield.cc
index 82cdf5b8669..3c1d189c674 100644
--- a/chromium/ui/views/controls/textfield/textfield.cc
+++ b/chromium/ui/views/controls/textfield/textfield.cc
@@ -57,7 +57,6 @@
#if defined(OS_WIN)
#include "base/win/win_util.h"
-#include "ui/base/ime/win/osk_display_manager.h"
#endif
#if defined(OS_LINUX) && !defined(OS_CHROMEOS)
@@ -83,12 +82,6 @@ namespace views {
namespace {
#if defined(OS_MACOSX)
-const ui::EventFlags kPlatformModifier = ui::EF_COMMAND_DOWN;
-#else
-const ui::EventFlags kPlatformModifier = ui::EF_CONTROL_DOWN;
-#endif // OS_MACOSX
-
-#if defined(OS_MACOSX)
const gfx::SelectionBehavior kLineSelectionBehavior = gfx::SELECTION_EXTEND;
const gfx::SelectionBehavior kWordSelectionBehavior = gfx::SELECTION_CARET;
const gfx::SelectionBehavior kMoveParagraphSelectionBehavior =
@@ -299,6 +292,9 @@ Textfield::Textfield()
UpdateBorder();
SetFocusBehavior(FocusBehavior::ALWAYS);
+ if (use_focus_ring_)
+ focus_ring_ = FocusRing::Install(this);
+
#if !defined(OS_MACOSX)
// Do not map accelerators on Mac. E.g. They might not reflect custom
// keybindings that a user has set. But also on Mac, these commands dispatch
@@ -595,12 +591,8 @@ void Textfield::SetInvalid(bool invalid) {
return;
invalid_ = invalid;
UpdateBorder();
-
- if (HasFocus() && use_focus_ring_) {
- FocusRing::Install(this, invalid_
- ? ui::NativeTheme::kColorId_AlertSeverityHigh
- : ui::NativeTheme::kColorId_NumColors);
- }
+ if (focus_ring_)
+ focus_ring_->SetInvalid(invalid);
}
void Textfield::ClearEditHistory() {
@@ -646,9 +638,9 @@ const char* Textfield::GetClassName() const {
}
void Textfield::SetBorder(std::unique_ptr<Border> b) {
- if (use_focus_ring_ && HasFocus())
- FocusRing::Uninstall(this);
use_focus_ring_ = false;
+ if (focus_ring_)
+ focus_ring_.reset();
View::SetBorder(std::move(b));
}
@@ -664,16 +656,22 @@ gfx::NativeCursor Textfield::GetCursor(const ui::MouseEvent& event) {
bool Textfield::OnMousePressed(const ui::MouseEvent& event) {
const bool had_focus = HasFocus();
bool handled = controller_ && controller_->HandleMouseEvent(this, event);
+
+ // If the controller triggered the focus, then record the focus reason as
+ // other.
+ if (!had_focus && HasFocus())
+ focus_reason_ = ui::TextInputClient::FOCUS_REASON_OTHER;
+
if (!handled &&
(event.IsOnlyLeftMouseButton() || event.IsOnlyRightMouseButton())) {
if (!had_focus)
- RequestFocus();
+ RequestFocusWithPointer(ui::EventPointerType::POINTER_TYPE_MOUSE);
ShowImeIfNeeded();
}
#if defined(OS_LINUX) && !defined(OS_CHROMEOS)
if (!handled && !had_focus && event.IsOnlyMiddleMouseButton())
- RequestFocus();
+ RequestFocusWithPointer(ui::EventPointerType::POINTER_TYPE_MOUSE);
#endif
return selection_controller_.OnMousePressed(
@@ -747,7 +745,7 @@ bool Textfield::OnKeyReleased(const ui::KeyEvent& event) {
void Textfield::OnGestureEvent(ui::GestureEvent* event) {
switch (event->type()) {
case ui::ET_GESTURE_TAP_DOWN:
- RequestFocus();
+ RequestFocusWithPointer(event->details().primary_pointer_type());
ShowImeIfNeeded();
event->SetHandled();
break;
@@ -776,13 +774,6 @@ void Textfield::OnGestureEvent(ui::GestureEvent* event) {
OnAfterUserAction();
}
CreateTouchSelectionControllerAndNotifyIt();
-#if defined(OS_WIN)
- if (!read_only()) {
- DCHECK(ui::OnScreenKeyboardDisplayManager::GetInstance());
- ui::OnScreenKeyboardDisplayManager::GetInstance()
- ->DisplayVirtualKeyboard(nullptr);
- }
-#endif
event->SetHandled();
break;
case ui::ET_GESTURE_LONG_PRESS:
@@ -857,6 +848,28 @@ bool Textfield::CanHandleAccelerators() const {
return GetRenderText()->focused() && View::CanHandleAccelerators();
}
+void Textfield::RequestFocusWithPointer(ui::EventPointerType pointer_type) {
+ if (HasFocus())
+ return;
+
+ switch (pointer_type) {
+ case ui::EventPointerType::POINTER_TYPE_MOUSE:
+ focus_reason_ = ui::TextInputClient::FOCUS_REASON_MOUSE;
+ break;
+ case ui::EventPointerType::POINTER_TYPE_PEN:
+ focus_reason_ = ui::TextInputClient::FOCUS_REASON_PEN;
+ break;
+ case ui::EventPointerType::POINTER_TYPE_TOUCH:
+ focus_reason_ = ui::TextInputClient::FOCUS_REASON_TOUCH;
+ break;
+ default:
+ focus_reason_ = ui::TextInputClient::FOCUS_REASON_OTHER;
+ break;
+ }
+
+ View::RequestFocus();
+}
+
void Textfield::AboutToRequestFocusFromTabTraversal(bool reverse) {
SelectAll(PlatformStyle::kTextfieldScrollsToStartOnFocusChange);
}
@@ -1074,6 +1087,10 @@ void Textfield::OnPaint(gfx::Canvas* canvas) {
}
void Textfield::OnFocus() {
+ // Set focus reason if focused was gained without mouse or touch input.
+ if (focus_reason_ == ui::TextInputClient::FOCUS_REASON_NONE)
+ focus_reason_ = ui::TextInputClient::FOCUS_REASON_OTHER;
+
#if defined(OS_MACOSX)
if (text_input_type_ == ui::TEXT_INPUT_TYPE_PASSWORD)
password_input_enabler_.reset(new ui::ScopedPasswordInputEnabler());
@@ -1089,16 +1106,13 @@ void Textfield::OnFocus() {
OnCaretBoundsChanged();
if (ShouldBlinkCursor())
StartBlinkingCursor();
- if (use_focus_ring_) {
- FocusRing::Install(this, invalid_
- ? ui::NativeTheme::kColorId_AlertSeverityHigh
- : ui::NativeTheme::kColorId_NumColors);
- }
SchedulePaint();
View::OnFocus();
}
void Textfield::OnBlur() {
+ focus_reason_ = ui::TextInputClient::FOCUS_REASON_NONE;
+
gfx::RenderText* render_text = GetRenderText();
render_text->set_focused(false);
@@ -1118,8 +1132,6 @@ void Textfield::OnBlur() {
DestroyTouchSelection();
- if (use_focus_ring_)
- FocusRing::Uninstall(this);
SchedulePaint();
View::OnBlur();
@@ -1354,23 +1366,23 @@ bool Textfield::GetAcceleratorForCommandId(int command_id,
ui::Accelerator* accelerator) const {
switch (command_id) {
case IDS_APP_UNDO:
- *accelerator = ui::Accelerator(ui::VKEY_Z, kPlatformModifier);
+ *accelerator = ui::Accelerator(ui::VKEY_Z, ui::EF_PLATFORM_ACCELERATOR);
return true;
case IDS_APP_CUT:
- *accelerator = ui::Accelerator(ui::VKEY_X, kPlatformModifier);
+ *accelerator = ui::Accelerator(ui::VKEY_X, ui::EF_PLATFORM_ACCELERATOR);
return true;
case IDS_APP_COPY:
- *accelerator = ui::Accelerator(ui::VKEY_C, kPlatformModifier);
+ *accelerator = ui::Accelerator(ui::VKEY_C, ui::EF_PLATFORM_ACCELERATOR);
return true;
case IDS_APP_PASTE:
- *accelerator = ui::Accelerator(ui::VKEY_V, kPlatformModifier);
+ *accelerator = ui::Accelerator(ui::VKEY_V, ui::EF_PLATFORM_ACCELERATOR);
return true;
case IDS_APP_SELECT_ALL:
- *accelerator = ui::Accelerator(ui::VKEY_A, kPlatformModifier);
+ *accelerator = ui::Accelerator(ui::VKEY_A, ui::EF_PLATFORM_ACCELERATOR);
return true;
case IDS_CONTENT_CONTEXT_EMOJI:
@@ -1535,6 +1547,10 @@ bool Textfield::HasCompositionText() const {
return model_->HasCompositionText();
}
+ui::TextInputClient::FocusReason Textfield::GetFocusReason() const {
+ return focus_reason_;
+}
+
bool Textfield::GetTextRange(gfx::Range* range) const {
if (!ImeEditingAllowed())
return false;
@@ -1601,13 +1617,14 @@ bool Textfield::ChangeTextDirectionAndLayoutAlignment(
// Restore text directionality mode when the indicated direction matches the
// current forced mode; otherwise, force the mode indicated. This helps users
// manage BiDi text layout without getting stuck in forced LTR or RTL modes.
- const gfx::DirectionalityMode mode = direction == base::i18n::RIGHT_TO_LEFT
- ? gfx::DIRECTIONALITY_FORCE_RTL
- : gfx::DIRECTIONALITY_FORCE_LTR;
- if (mode == GetRenderText()->directionality_mode())
- GetRenderText()->SetDirectionalityMode(gfx::DIRECTIONALITY_FROM_TEXT);
- else
- GetRenderText()->SetDirectionalityMode(mode);
+ const bool default_rtl = direction == base::i18n::RIGHT_TO_LEFT;
+ const auto new_mode = default_rtl ? gfx::DIRECTIONALITY_FORCE_RTL
+ : gfx::DIRECTIONALITY_FORCE_LTR;
+ auto* render_text = GetRenderText();
+ const bool modes_match = new_mode == render_text->directionality_mode();
+ render_text->SetDirectionalityMode(modes_match ? gfx::DIRECTIONALITY_FROM_TEXT
+ : new_mode);
+ SetHorizontalAlignment(default_rtl ? gfx::ALIGN_RIGHT : gfx::ALIGN_LEFT);
SchedulePaint();
return true;
}
@@ -1732,6 +1749,12 @@ const std::string& Textfield::GetClientSourceInfo() const {
return base::EmptyString();
}
+bool Textfield::ShouldDoLearning() {
+ // TODO(https://crbug.com/311180): Implement this method.
+ NOTIMPLEMENTED_LOG_ONCE();
+ return false;
+}
+
////////////////////////////////////////////////////////////////////////////////
// Textfield, protected:
@@ -2157,7 +2180,9 @@ void Textfield::OnCaretBoundsChanged() {
#if defined(OS_MACOSX)
// On Mac, the context menu contains a look up item which displays the
// selected text. As such, the menu needs to be updated if the selection has
- // changed.
+ // changed. Be careful to reset the MenuRunner first so it doesn't reference
+ // the old model.
+ context_menu_runner_.reset();
context_menu_contents_.reset();
#endif
diff --git a/chromium/ui/views/controls/textfield/textfield.h b/chromium/ui/views/controls/textfield/textfield.h
index 8f26353fa19..3df902cef6d 100644
--- a/chromium/ui/views/controls/textfield/textfield.h
+++ b/chromium/ui/views/controls/textfield/textfield.h
@@ -28,6 +28,7 @@
#include "ui/gfx/selection_model.h"
#include "ui/gfx/text_constants.h"
#include "ui/views/context_menu_controller.h"
+#include "ui/views/controls/focus_ring.h"
#include "ui/views/controls/textfield/textfield_model.h"
#include "ui/views/drag_controller.h"
#include "ui/views/selection_controller.h"
@@ -343,6 +344,7 @@ class VIEWS_EXPORT Textfield : public View,
bool GetCompositionCharacterBounds(uint32_t index,
gfx::Rect* rect) const override;
bool HasCompositionText() const override;
+ FocusReason GetFocusReason() const override;
bool GetTextRange(gfx::Range* range) const override;
bool GetCompositionTextRange(gfx::Range* range) const override;
bool GetSelectionRange(gfx::Range* range) const override;
@@ -358,6 +360,7 @@ class VIEWS_EXPORT Textfield : public View,
bool IsTextEditCommandEnabled(ui::TextEditCommand command) const override;
void SetTextEditCommandForNextKeyEvent(ui::TextEditCommand command) override;
const std::string& GetClientSourceInfo() const override;
+ bool ShouldDoLearning() override;
protected:
// Inserts or appends a character in response to an IME operation.
@@ -481,6 +484,13 @@ class VIEWS_EXPORT Textfield : public View,
// Textfield::GetCaretBlinkMs().
void OnCursorBlinkTimerFired();
+ // Like RequestFocus, but explicitly states that the focus is triggered by
+ // a pointer event.
+ void RequestFocusWithPointer(ui::EventPointerType pointer_type);
+
+ // Returns the color to use for the FocusRing, if one is present.
+ SkColor GetFocusRingColor() const;
+
// The text model.
std::unique_ptr<TextfieldModel> model_;
@@ -602,6 +612,13 @@ class VIEWS_EXPORT Textfield : public View,
std::unique_ptr<ui::ScopedPasswordInputEnabler> password_input_enabler_;
#endif // defined(OS_MACOSX)
+ // How this textfield was focused.
+ ui::TextInputClient::FocusReason focus_reason_ =
+ ui::TextInputClient::FOCUS_REASON_NONE;
+
+ // The focus ring for this TextField.
+ std::unique_ptr<FocusRing> focus_ring_;
+
// Used to bind callback functions to this object.
base::WeakPtrFactory<Textfield> weak_ptr_factory_;
diff --git a/chromium/ui/views/controls/textfield/textfield_model.cc b/chromium/ui/views/controls/textfield/textfield_model.cc
index 7b06af51838..4d77de34ffc 100644
--- a/chromium/ui/views/controls/textfield/textfield_model.cc
+++ b/chromium/ui/views/controls/textfield/textfield_model.cc
@@ -36,16 +36,15 @@ class Edit {
// Revert the change made by this edit in |model|.
void Undo(TextfieldModel* model) {
- model->ModifyText(new_text_start_, new_text_end(),
- old_text_, old_text_start_,
- old_cursor_pos_);
+ model->ModifyText(new_text_start_, new_text_end(), old_text_,
+ old_text_start_, old_selection_);
}
// Apply the change of this edit to the |model|.
void Redo(TextfieldModel* model) {
- model->ModifyText(old_text_start_, old_text_end(),
- new_text_, new_text_start_,
- new_cursor_pos_);
+ model->ModifyText(old_text_start_, old_text_end(), new_text_,
+ new_text_start_,
+ gfx::Range(new_cursor_pos_, new_cursor_pos_));
}
// Try to merge the |edit| into this edit and returns true on success. The
@@ -72,23 +71,22 @@ class Edit {
Edit(Type type,
MergeType merge_type,
- size_t old_cursor_pos,
const base::string16& old_text,
size_t old_text_start,
+ gfx::Range old_selection,
bool delete_backward,
size_t new_cursor_pos,
const base::string16& new_text,
size_t new_text_start)
: type_(type),
merge_type_(merge_type),
- old_cursor_pos_(old_cursor_pos),
old_text_(old_text),
old_text_start_(old_text_start),
+ old_selection_(old_selection),
delete_backward_(delete_backward),
new_cursor_pos_(new_cursor_pos),
new_text_(new_text),
- new_text_start_(new_text_start) {
- }
+ new_text_start_(new_text_start) {}
// Each type of edit provides its own specific merge implementation.
virtual bool DoMerge(const Edit* edit) = 0;
@@ -131,12 +129,12 @@ class Edit {
// The type of merging allowed.
MergeType merge_type_;
- // Old cursor position.
- size_t old_cursor_pos_;
// Deleted text by this edit.
base::string16 old_text_;
// The index of |old_text_|.
size_t old_text_start_;
+ // The range of the text selection prior to the edit.
+ gfx::Range old_selection_;
// True if the deletion is made backward.
bool delete_backward_;
// New cursor position.
@@ -154,14 +152,13 @@ class InsertEdit : public Edit {
InsertEdit(bool mergeable, const base::string16& new_text, size_t at)
: Edit(INSERT_EDIT,
mergeable ? MERGEABLE : DO_NOT_MERGE,
- at /* old cursor */,
base::string16(),
at,
- false /* N/A */,
- at + new_text.length() /* new cursor */,
+ gfx::Range(at, at),
+ false /* N/A */,
+ at + new_text.length() /* new cursor */,
new_text,
- at) {
- }
+ at) {}
// Edit implementation.
bool DoMerge(const Edit* edit) override {
@@ -180,21 +177,21 @@ class ReplaceEdit : public Edit {
public:
ReplaceEdit(MergeType merge_type,
const base::string16& old_text,
- size_t old_cursor_pos,
size_t old_text_start,
+ gfx::Range old_selection,
bool backward,
size_t new_cursor_pos,
const base::string16& new_text,
size_t new_text_start)
- : Edit(REPLACE_EDIT, merge_type,
- old_cursor_pos,
+ : Edit(REPLACE_EDIT,
+ merge_type,
old_text,
old_text_start,
+ old_selection,
backward,
new_cursor_pos,
new_text,
- new_text_start) {
- }
+ new_text_start) {}
// Edit implementation.
bool DoMerge(const Edit* edit) override {
@@ -214,17 +211,17 @@ class DeleteEdit : public Edit {
DeleteEdit(bool mergeable,
const base::string16& text,
size_t text_start,
- bool backward)
+ bool backward,
+ gfx::Range old_selection)
: Edit(DELETE_EDIT,
mergeable ? MERGEABLE : DO_NOT_MERGE,
- (backward ? text_start + text.length() : text_start),
text,
text_start,
+ old_selection,
backward,
text_start,
base::string16(),
- text_start) {
- }
+ text_start) {}
// Edit implementation.
bool DoMerge(const Edit* edit) override {
@@ -334,14 +331,13 @@ bool TextfieldModel::SetText(const base::string16& new_text) {
if (text() != new_text) {
if (changed) // No need to remember composition.
Undo();
- size_t old_cursor = GetCursorPosition();
// SetText moves the cursor to the end.
size_t new_cursor = new_text.length();
- SelectAll(false);
// If there is a composition text, don't merge with previous edit.
// Otherwise, force merge the edits.
ExecuteAndRecordReplace(changed ? DO_NOT_MERGE : FORCE_MERGE,
- old_cursor, new_cursor, new_text, 0U);
+ gfx::Range(0, text().length()), new_cursor,
+ new_text, 0U);
render_text_->SetCursorPosition(new_cursor);
}
ClearSelection();
@@ -627,11 +623,8 @@ void TextfieldModel::DeleteSelectionAndInsertTextAt(
size_t position) {
if (HasCompositionText())
CancelCompositionText();
- ExecuteAndRecordReplace(DO_NOT_MERGE,
- GetCursorPosition(),
- position + new_text.length(),
- new_text,
- position);
+ ExecuteAndRecordReplace(DO_NOT_MERGE, render_text_->selection(),
+ position + new_text.length(), new_text, position);
}
base::string16 TextfieldModel::GetTextFromRange(const gfx::Range& range) const {
@@ -776,8 +769,9 @@ void TextfieldModel::ExecuteAndRecordDelete(gfx::Range range, bool mergeable) {
size_t old_text_start = range.GetMin();
const base::string16 old_text = text().substr(old_text_start, range.length());
bool backward = range.is_reversed();
+ gfx::Range curr_selection = render_text_->selection();
auto edit = std::make_unique<DeleteEdit>(mergeable, old_text, old_text_start,
- backward);
+ backward, curr_selection);
edit->Redo(this);
AddOrMergeEditHistory(std::move(edit));
}
@@ -787,23 +781,21 @@ void TextfieldModel::ExecuteAndRecordReplaceSelection(
const base::string16& new_text) {
size_t new_text_start = render_text_->selection().GetMin();
size_t new_cursor_pos = new_text_start + new_text.length();
- ExecuteAndRecordReplace(merge_type,
- GetCursorPosition(),
- new_cursor_pos,
- new_text,
- new_text_start);
+ ExecuteAndRecordReplace(merge_type, render_text_->selection(), new_cursor_pos,
+ new_text, new_text_start);
}
void TextfieldModel::ExecuteAndRecordReplace(MergeType merge_type,
- size_t old_cursor_pos,
+ gfx::Range replacement_range,
size_t new_cursor_pos,
const base::string16& new_text,
size_t new_text_start) {
- size_t old_text_start = render_text_->selection().GetMin();
- bool backward = render_text_->selection().is_reversed();
+ size_t old_text_start = replacement_range.GetMin();
+ bool backward = replacement_range.is_reversed();
auto edit = std::make_unique<ReplaceEdit>(
- merge_type, GetSelectedText(), old_cursor_pos, old_text_start, backward,
- new_cursor_pos, new_text, new_text_start);
+ merge_type, GetTextFromRange(replacement_range), old_text_start,
+ render_text_->selection(), backward, new_cursor_pos, new_text,
+ new_text_start);
edit->Redo(this);
AddOrMergeEditHistory(std::move(edit));
}
@@ -840,7 +832,7 @@ void TextfieldModel::ModifyText(size_t delete_from,
size_t delete_to,
const base::string16& new_text,
size_t new_text_insert_at,
- size_t new_cursor_pos) {
+ gfx::Range selection) {
DCHECK_LE(delete_from, delete_to);
base::string16 old_text = text();
ClearComposition();
@@ -848,8 +840,11 @@ void TextfieldModel::ModifyText(size_t delete_from,
render_text_->SetText(old_text.erase(delete_from, delete_to - delete_from));
if (!new_text.empty())
render_text_->SetText(old_text.insert(new_text_insert_at, new_text));
- render_text_->SetCursorPosition(new_cursor_pos);
- // TODO(oshima): Select text that was just undone, like Mac (but not GTK).
+ if (selection.start() == selection.end()) {
+ render_text_->SetCursorPosition(selection.start());
+ } else {
+ render_text_->SelectRange(selection);
+ }
}
// static
diff --git a/chromium/ui/views/controls/textfield/textfield_model.h b/chromium/ui/views/controls/textfield/textfield_model.h
index 20c6687a97c..771d7a9c330 100644
--- a/chromium/ui/views/controls/textfield/textfield_model.h
+++ b/chromium/ui/views/controls/textfield/textfield_model.h
@@ -260,7 +260,7 @@ class VIEWS_EXPORT TextfieldModel {
void ExecuteAndRecordReplaceSelection(internal::MergeType merge_type,
const base::string16& new_text);
void ExecuteAndRecordReplace(internal::MergeType merge_type,
- size_t old_cursor_pos,
+ gfx::Range replacement_range,
size_t new_cursor_pos,
const base::string16& new_text,
size_t new_text_start);
@@ -270,15 +270,15 @@ class VIEWS_EXPORT TextfieldModel {
void AddOrMergeEditHistory(std::unique_ptr<internal::Edit> edit);
// Modify the text buffer in following way:
- // 1) Delete the string from |delete_from| to |delte_to|.
+ // 1) Delete the string from |delete_from| to |delete_to|.
// 2) Insert the |new_text| at the index |new_text_insert_at|.
// Note that the index is after deletion.
- // 3) Move the cursor to |new_cursor_pos|.
+ // 3) Select |selection|.
void ModifyText(size_t delete_from,
size_t delete_to,
const base::string16& new_text,
size_t new_text_insert_at,
- size_t new_cursor_pos);
+ gfx::Range selection);
void ClearComposition();
diff --git a/chromium/ui/views/controls/textfield/textfield_model_unittest.cc b/chromium/ui/views/controls/textfield/textfield_model_unittest.cc
index c35b12260bf..60884ff58b0 100644
--- a/chromium/ui/views/controls/textfield/textfield_model_unittest.cc
+++ b/chromium/ui/views/controls/textfield/textfield_model_unittest.cc
@@ -11,7 +11,6 @@
#include "base/auto_reset.h"
#include "base/macros.h"
-#include "base/message_loop/message_loop.h"
#include "base/strings/string16.h"
#include "base/strings/utf_string_conversions.h"
#include "build/build_config.h"
@@ -1387,7 +1386,9 @@ TEST_F(TextfieldModelTest, UndoRedo_CutCopyPasteTest) {
EXPECT_EQ(1U, model.GetCursorPosition());
EXPECT_TRUE(model.Undo());
EXPECT_STR_EQ("ABCDE", model.text());
- EXPECT_EQ(3U, model.GetCursorPosition());
+ EXPECT_EQ(1U, model.GetCursorPosition());
+ EXPECT_TRUE(model.render_text()->selection().EqualsIgnoringDirection(
+ gfx::Range(1, 3)));
EXPECT_TRUE(model.Undo());
EXPECT_STR_EQ("", model.text());
EXPECT_EQ(0U, model.GetCursorPosition());
@@ -1418,7 +1419,9 @@ TEST_F(TextfieldModelTest, UndoRedo_CutCopyPasteTest) {
EXPECT_EQ(1U, model.GetCursorPosition());
EXPECT_TRUE(model.Undo());
EXPECT_STR_EQ("ABCDE", model.text());
- EXPECT_EQ(3U, model.GetCursorPosition());
+ EXPECT_EQ(1U, model.GetCursorPosition());
+ EXPECT_TRUE(model.render_text()->selection().EqualsIgnoringDirection(
+ gfx::Range(1, 3)));
EXPECT_TRUE(model.Undo());
EXPECT_STR_EQ("", model.text());
EXPECT_EQ(0U, model.GetCursorPosition());
@@ -1460,8 +1463,9 @@ TEST_F(TextfieldModelTest, UndoRedo_CutCopyPasteTest) {
// An empty cut shouldn't create an edit.
EXPECT_TRUE(model.Undo());
EXPECT_STR_EQ("ABCBCBCDE", model.text());
- EXPECT_EQ(3U, model.GetCursorPosition());
-
+ EXPECT_EQ(1U, model.GetCursorPosition());
+ EXPECT_TRUE(model.render_text()->selection().EqualsIgnoringDirection(
+ gfx::Range(1, 3)));
// Test Copy.
ResetModel(&model);
model.SetText(base::ASCIIToUTF16("12345"));
@@ -1533,6 +1537,47 @@ TEST_F(TextfieldModelTest, UndoRedo_CursorTest) {
EXPECT_FALSE(model.Redo());
}
+TEST_F(TextfieldModelTest, Undo_SelectionTest) {
+ gfx::Range range = gfx::Range(2, 4);
+ TextfieldModel model(nullptr);
+ model.SetText(base::ASCIIToUTF16("abcdef"));
+ model.SelectRange(range);
+ EXPECT_EQ(model.render_text()->selection(), range);
+
+ // Deleting the selected text should change the text and the range.
+ EXPECT_TRUE(model.Backspace());
+ EXPECT_STR_EQ("abef", model.text());
+ EXPECT_EQ(model.render_text()->selection(), gfx::Range(2, 2));
+
+ // Undoing the deletion should restore the former range.
+ EXPECT_TRUE(model.Undo());
+ EXPECT_STR_EQ("abcdef", model.text());
+ EXPECT_EQ(model.render_text()->selection(), range);
+
+ // When range.start = range.end, nothing is selected and
+ // range.start = range.end = cursor position
+ model.MoveCursor(gfx::CHARACTER_BREAK, gfx::CURSOR_LEFT, gfx::SELECTION_NONE);
+ EXPECT_EQ(model.render_text()->selection(), gfx::Range(2, 2));
+
+ // Deleting a single character should change the text and cursor location.
+ EXPECT_TRUE(model.Backspace());
+ EXPECT_STR_EQ("acdef", model.text());
+ EXPECT_EQ(model.render_text()->selection(), gfx::Range(1, 1));
+
+ // Undoing the deletion should restore the former range.
+ EXPECT_TRUE(model.Undo());
+ EXPECT_STR_EQ("abcdef", model.text());
+ EXPECT_EQ(model.render_text()->selection(), gfx::Range(2, 2));
+
+ MoveCursorTo(model, model.text().length());
+ EXPECT_TRUE(model.Backspace());
+ model.SelectRange(gfx::Range(1, 3));
+ model.SetText(base::ASCIIToUTF16("[set]"));
+ EXPECT_TRUE(model.Undo());
+ EXPECT_STR_EQ("abcde", model.text());
+ EXPECT_EQ(model.render_text()->selection(), gfx::Range(1, 3));
+}
+
void RunInsertReplaceTest(TextfieldModel& model) {
const bool reverse = model.render_text()->selection().is_reversed();
model.InsertChar('1');
diff --git a/chromium/ui/views/controls/textfield/textfield_unittest.cc b/chromium/ui/views/controls/textfield/textfield_unittest.cc
index 775c61fd31d..12e9a987863 100644
--- a/chromium/ui/views/controls/textfield/textfield_unittest.cc
+++ b/chromium/ui/views/controls/textfield/textfield_unittest.cc
@@ -26,6 +26,7 @@
#include "ui/base/clipboard/clipboard.h"
#include "ui/base/clipboard/scoped_clipboard_writer.h"
#include "ui/base/dragdrop/drag_drop_types.h"
+#include "ui/base/emoji/emoji_panel_helper.h"
#include "ui/base/ime/input_method_base.h"
#include "ui/base/ime/input_method_delegate.h"
#include "ui/base/ime/input_method_factory.h"
@@ -704,9 +705,11 @@ class TextfieldTest : public ViewsTestBase, public TextfieldController {
EXPECT_TRUE(menu->IsEnabledAt(menu_index++ /* LOOK UP */));
EXPECT_TRUE(menu->IsEnabledAt(menu_index++ /* Separator */));
}
- EXPECT_TRUE(menu->IsEnabledAt(menu_index++ /* EMOJI */));
- EXPECT_TRUE(menu->IsEnabledAt(menu_index++ /* Separator */));
#endif
+ if (ui::IsEmojiPanelSupported()) {
+ EXPECT_TRUE(menu->IsEnabledAt(menu_index++ /* EMOJI */));
+ EXPECT_TRUE(menu->IsEnabledAt(menu_index++ /* Separator */));
+ }
EXPECT_EQ(can_undo, menu->IsEnabledAt(menu_index++ /* UNDO */));
EXPECT_TRUE(menu->IsEnabledAt(menu_index++ /* Separator */));
@@ -3432,6 +3435,69 @@ TEST_F(TextfieldTest, SendingDeletePreservesShiftFlag) {
EXPECT_EQ(ui::EF_SHIFT_DOWN, textfield_->event_flags());
}
+TEST_F(TextfieldTest, EmojiItem_EmptyField) {
+ InitTextfield();
+ EXPECT_TRUE(textfield_->context_menu_controller());
+
+ // Enable the emoji feature.
+ base::test::ScopedFeatureList scoped_feature_list;
+ scoped_feature_list.InitAndEnableFeature(features::kEnableEmojiContextMenu);
+
+ // A normal empty field may show the Emoji option (if supported).
+ ui::MenuModel* context_menu = GetContextMenuModel();
+ EXPECT_TRUE(context_menu);
+ EXPECT_GT(context_menu->GetItemCount(), 0);
+ // Not all OS/versions support the emoji menu.
+ EXPECT_EQ(ui::IsEmojiPanelSupported(),
+ context_menu->GetLabelAt(0) ==
+ l10n_util::GetStringUTF16(IDS_CONTENT_CONTEXT_EMOJI));
+}
+
+TEST_F(TextfieldTest, EmojiItem_ReadonlyField) {
+ InitTextfield();
+ EXPECT_TRUE(textfield_->context_menu_controller());
+
+ // Enable the emoji feature.
+ base::test::ScopedFeatureList scoped_feature_list;
+ scoped_feature_list.InitAndEnableFeature(features::kEnableEmojiContextMenu);
+
+ textfield_->SetReadOnly(true);
+ // In no case is the emoji option showing on a read-only field.
+ ui::MenuModel* context_menu = GetContextMenuModel();
+ EXPECT_TRUE(context_menu);
+ EXPECT_GT(context_menu->GetItemCount(), 0);
+ EXPECT_NE(context_menu->GetLabelAt(0),
+ l10n_util::GetStringUTF16(IDS_CONTENT_CONTEXT_EMOJI));
+}
+
+TEST_F(TextfieldTest, EmojiItem_FieldWithText) {
+ InitTextfield();
+ EXPECT_TRUE(textfield_->context_menu_controller());
+
+#if defined(OS_MACOSX)
+ // On Mac, when there is text, the "Look up" item (+ separator) takes the top
+ // position, and emoji comes after.
+ constexpr int kExpectedEmojiIndex = 2;
+#else
+ constexpr int kExpectedEmojiIndex = 0;
+#endif
+
+ // Enable the emoji feature.
+ base::test::ScopedFeatureList scoped_feature_list;
+ scoped_feature_list.InitAndEnableFeature(features::kEnableEmojiContextMenu);
+
+ // A field with text may still show the Emoji option (if supported).
+ textfield_->SetText(base::ASCIIToUTF16("some text"));
+ textfield_->SelectAll(false);
+ ui::MenuModel* context_menu = GetContextMenuModel();
+ EXPECT_TRUE(context_menu);
+ EXPECT_GT(context_menu->GetItemCount(), 0);
+ // Not all OS/versions support the emoji menu.
+ EXPECT_EQ(ui::IsEmojiPanelSupported(),
+ context_menu->GetLabelAt(kExpectedEmojiIndex) ==
+ l10n_util::GetStringUTF16(IDS_CONTENT_CONTEXT_EMOJI));
+}
+
#if defined(OS_MACOSX)
// Tests to see if the BiDi submenu items are updated correctly when the
// textfield's text direction is changed.
@@ -3470,6 +3536,10 @@ TEST_F(TextfieldTest, LookUpItemUpdate) {
InitTextfield();
EXPECT_TRUE(textfield_->context_menu_controller());
+ // Make sure the Emoji feature is disabled for this test.
+ base::test::ScopedFeatureList scoped_feature_list;
+ scoped_feature_list.InitAndDisableFeature(features::kEnableEmojiContextMenu);
+
const base::string16 kTextOne = ASCIIToUTF16("crake");
textfield_->SetText(kTextOne);
textfield_->SelectAll(false);
@@ -3552,4 +3622,122 @@ TEST_F(TextfieldTest, AccessibilitySelectionEvents) {
textfield_->GetAccessibilitySelectionFiredCount());
}
+TEST_F(TextfieldTest, FocusReasonMouse) {
+ InitTextfield();
+ widget_->GetFocusManager()->ClearFocus();
+ EXPECT_EQ(ui::TextInputClient::FOCUS_REASON_NONE,
+ textfield_->GetFocusReason());
+
+ const auto& bounds = textfield_->bounds();
+ MouseClick(bounds, 10);
+
+ EXPECT_EQ(ui::TextInputClient::FOCUS_REASON_MOUSE,
+ textfield_->GetFocusReason());
+}
+
+TEST_F(TextfieldTest, FocusReasonTouchTap) {
+ InitTextfield();
+ widget_->GetFocusManager()->ClearFocus();
+ EXPECT_EQ(ui::TextInputClient::FOCUS_REASON_NONE,
+ textfield_->GetFocusReason());
+
+ ui::GestureEventDetails tap_details(ui::ET_GESTURE_TAP_DOWN);
+ tap_details.set_primary_pointer_type(
+ ui::EventPointerType::POINTER_TYPE_TOUCH);
+ GestureEventForTest tap(GetCursorPositionX(0), 0, tap_details);
+ textfield_->OnGestureEvent(&tap);
+
+ EXPECT_EQ(ui::TextInputClient::FOCUS_REASON_TOUCH,
+ textfield_->GetFocusReason());
+}
+
+TEST_F(TextfieldTest, FocusReasonPenTap) {
+ InitTextfield();
+ widget_->GetFocusManager()->ClearFocus();
+ EXPECT_EQ(ui::TextInputClient::FOCUS_REASON_NONE,
+ textfield_->GetFocusReason());
+
+ ui::GestureEventDetails tap_details(ui::ET_GESTURE_TAP_DOWN);
+ tap_details.set_primary_pointer_type(ui::EventPointerType::POINTER_TYPE_PEN);
+ GestureEventForTest tap(GetCursorPositionX(0), 0, tap_details);
+ textfield_->OnGestureEvent(&tap);
+
+ EXPECT_EQ(ui::TextInputClient::FOCUS_REASON_PEN,
+ textfield_->GetFocusReason());
+}
+
+TEST_F(TextfieldTest, FocusReasonMultipleEvents) {
+ InitTextfield();
+ widget_->GetFocusManager()->ClearFocus();
+ EXPECT_EQ(ui::TextInputClient::FOCUS_REASON_NONE,
+ textfield_->GetFocusReason());
+
+ // Pen tap, followed by a touch tap
+ {
+ ui::GestureEventDetails tap_details(ui::ET_GESTURE_TAP_DOWN);
+ tap_details.set_primary_pointer_type(
+ ui::EventPointerType::POINTER_TYPE_PEN);
+ GestureEventForTest tap(GetCursorPositionX(0), 0, tap_details);
+ textfield_->OnGestureEvent(&tap);
+ }
+
+ {
+ ui::GestureEventDetails tap_details(ui::ET_GESTURE_TAP_DOWN);
+ tap_details.set_primary_pointer_type(
+ ui::EventPointerType::POINTER_TYPE_TOUCH);
+ GestureEventForTest tap(GetCursorPositionX(0), 0, tap_details);
+ textfield_->OnGestureEvent(&tap);
+ }
+
+ EXPECT_EQ(ui::TextInputClient::FOCUS_REASON_PEN,
+ textfield_->GetFocusReason());
+}
+
+TEST_F(TextfieldTest, FocusReasonFocusBlurFocus) {
+ InitTextfield();
+ widget_->GetFocusManager()->ClearFocus();
+ EXPECT_EQ(ui::TextInputClient::FOCUS_REASON_NONE,
+ textfield_->GetFocusReason());
+
+ // Pen tap, blur, then programmatic focus.
+ ui::GestureEventDetails tap_details(ui::ET_GESTURE_TAP_DOWN);
+ tap_details.set_primary_pointer_type(ui::EventPointerType::POINTER_TYPE_PEN);
+ GestureEventForTest tap(GetCursorPositionX(0), 0, tap_details);
+ textfield_->OnGestureEvent(&tap);
+
+ widget_->GetFocusManager()->ClearFocus();
+
+ textfield_->RequestFocus();
+
+ EXPECT_EQ(ui::TextInputClient::FOCUS_REASON_OTHER,
+ textfield_->GetFocusReason());
+}
+
+TEST_F(TextfieldTest, ChangeTextDirectionAndLayoutAlignmentTest) {
+ InitTextfield();
+
+ textfield_->ChangeTextDirectionAndLayoutAlignment(
+ base::i18n::TextDirection::RIGHT_TO_LEFT);
+ EXPECT_EQ(textfield_->GetTextDirection(),
+ base::i18n::TextDirection::RIGHT_TO_LEFT);
+ EXPECT_EQ(textfield_->GetHorizontalAlignment(),
+ gfx::HorizontalAlignment::ALIGN_RIGHT);
+
+ textfield_->ChangeTextDirectionAndLayoutAlignment(
+ base::i18n::TextDirection::RIGHT_TO_LEFT);
+ const base::string16& text = test_api_->GetRenderText()->GetDisplayText();
+ base::i18n::TextDirection text_direction =
+ base::i18n::GetFirstStrongCharacterDirection(text);
+ EXPECT_EQ(textfield_->GetTextDirection(), text_direction);
+ EXPECT_EQ(textfield_->GetHorizontalAlignment(),
+ gfx::HorizontalAlignment::ALIGN_RIGHT);
+
+ textfield_->ChangeTextDirectionAndLayoutAlignment(
+ base::i18n::TextDirection::LEFT_TO_RIGHT);
+ EXPECT_EQ(textfield_->GetTextDirection(),
+ base::i18n::TextDirection::LEFT_TO_RIGHT);
+ EXPECT_EQ(textfield_->GetHorizontalAlignment(),
+ gfx::HorizontalAlignment::ALIGN_LEFT);
+}
+
} // namespace views
diff --git a/chromium/ui/views/controls/tree/tree_view.cc b/chromium/ui/views/controls/tree/tree_view.cc
index 4b8d78b239a..b8b07ce5377 100644
--- a/chromium/ui/views/controls/tree/tree_view.cc
+++ b/chromium/ui/views/controls/tree/tree_view.cc
@@ -7,7 +7,6 @@
#include <algorithm>
#include "base/i18n/rtl.h"
-#include "base/message_loop/message_loop.h"
#include "build/build_config.h"
#include "components/vector_icons/vector_icons.h"
#include "ui/accessibility/ax_node_data.h"
@@ -262,8 +261,13 @@ void TreeView::SetSelectedNode(TreeModelNode* model_node) {
SchedulePaintForNode(selected_node_);
}
- if (selected_node_)
- ScrollRectToVisible(GetForegroundBoundsForNode(selected_node_));
+ if (selected_node_) {
+ // GetForegroundBoundsForNode() returns RTL-flipped coordinates for paint.
+ // Un-flip before passing to ScrollRectToVisible(), which uses layout
+ // coordinates.
+ ScrollRectToVisible(
+ GetMirroredRect(GetForegroundBoundsForNode(selected_node_)));
+ }
// Notify controller if the old selection was empty to handle the case of
// remove explicitly resetting selected_node_ before invoking this.
@@ -739,6 +743,9 @@ void TreeView::LayoutEditor() {
DCHECK(selected_node_);
// Position the editor so that its text aligns with the text we drew.
gfx::Rect row_bounds = GetForegroundBoundsForNode(selected_node_);
+
+ // GetForegroundBoundsForNode() returns a "flipped" x for painting. First, un-
+ // flip it for the following calculations and ScrollRectToVisible().
row_bounds.set_x(
GetMirroredXWithWidthInView(row_bounds.x(), row_bounds.width()));
row_bounds.set_x(row_bounds.x() + text_offset_);
diff --git a/chromium/ui/views/controls/views_text_services_context_menu.cc b/chromium/ui/views/controls/views_text_services_context_menu.cc
index ec5975312cc..03a3a7b6e61 100644
--- a/chromium/ui/views/controls/views_text_services_context_menu.cc
+++ b/chromium/ui/views/controls/views_text_services_context_menu.cc
@@ -4,7 +4,10 @@
#include "ui/views/controls/views_text_services_context_menu.h"
+#include <memory>
+
#include "base/logging.h"
+#include "ui/views/controls/views_text_services_context_menu_base.h"
namespace views {
@@ -12,7 +15,7 @@ namespace views {
std::unique_ptr<ViewsTextServicesContextMenu>
ViewsTextServicesContextMenu::Create(ui::SimpleMenuModel* menu,
Textfield* client) {
- return nullptr;
+ return std::make_unique<ViewsTextServicesContextMenuBase>(menu, client);
}
bool ViewsTextServicesContextMenu::IsTextDirectionCheckedForTesting(
diff --git a/chromium/ui/views/controls/views_text_services_context_menu_base.cc b/chromium/ui/views/controls/views_text_services_context_menu_base.cc
new file mode 100644
index 00000000000..e5fdea933b5
--- /dev/null
+++ b/chromium/ui/views/controls/views_text_services_context_menu_base.cc
@@ -0,0 +1,53 @@
+// Copyright 2018 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/views_text_services_context_menu_base.h"
+
+#include "ui/base/emoji/emoji_panel_helper.h"
+#include "ui/base/models/simple_menu_model.h"
+#include "ui/resources/grit/ui_resources.h"
+#include "ui/strings/grit/ui_strings.h"
+#include "ui/views/controls/textfield/textfield.h"
+
+namespace views {
+
+ViewsTextServicesContextMenuBase::ViewsTextServicesContextMenuBase(
+ ui::SimpleMenuModel* menu,
+ Textfield* client)
+ : client_(client) {
+ DCHECK(client);
+ DCHECK(menu);
+ // Not inserted on read-only fields or if the OS/version doesn't support it.
+ if (!client_->read_only() && ui::IsEmojiPanelSupported()) {
+ menu->InsertSeparatorAt(0, ui::NORMAL_SEPARATOR);
+ menu->InsertItemWithStringIdAt(0, IDS_CONTENT_CONTEXT_EMOJI,
+ IDS_CONTENT_CONTEXT_EMOJI);
+ }
+}
+
+ViewsTextServicesContextMenuBase::~ViewsTextServicesContextMenuBase() {}
+
+bool ViewsTextServicesContextMenuBase::SupportsCommand(int command_id) const {
+ return command_id == IDS_CONTENT_CONTEXT_EMOJI;
+}
+
+bool ViewsTextServicesContextMenuBase::IsCommandIdChecked(
+ int command_id) const {
+ return false;
+}
+
+bool ViewsTextServicesContextMenuBase::IsCommandIdEnabled(
+ int command_id) const {
+ if (command_id == IDS_CONTENT_CONTEXT_EMOJI)
+ return true;
+
+ return false;
+}
+
+void ViewsTextServicesContextMenuBase::ExecuteCommand(int command_id) {
+ if (command_id == IDS_CONTENT_CONTEXT_EMOJI)
+ ui::ShowEmojiPanel();
+}
+
+} // namespace views \ No newline at end of file
diff --git a/chromium/ui/views/controls/views_text_services_context_menu_base.h b/chromium/ui/views/controls/views_text_services_context_menu_base.h
new file mode 100644
index 00000000000..7c39439b83a
--- /dev/null
+++ b/chromium/ui/views/controls/views_text_services_context_menu_base.h
@@ -0,0 +1,41 @@
+// Copyright 2018 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_VIEWS_TEXT_SERVICES_CONTEXT_MENU_BASE_H_
+#define UI_VIEWS_CONTROLS_VIEWS_TEXT_SERVICES_CONTEXT_MENU_BASE_H_
+
+#include "base/macros.h"
+#include "ui/views/controls/views_text_services_context_menu.h"
+
+namespace views {
+
+// This base class is used to add and handle text service items in the text
+// context menu. Specific platforms may subclass and add additional items.
+class ViewsTextServicesContextMenuBase : public ViewsTextServicesContextMenu {
+ public:
+ ViewsTextServicesContextMenuBase(ui::SimpleMenuModel* menu,
+ Textfield* client);
+ ~ViewsTextServicesContextMenuBase() override;
+
+ // Returns true if the given |command_id| is handled by the menu.
+ bool SupportsCommand(int command_id) const override;
+
+ // Methods associated with SimpleMenuModel::Delegate.
+ bool IsCommandIdChecked(int command_id) const override;
+ bool IsCommandIdEnabled(int command_id) const override;
+ void ExecuteCommand(int command_id) override;
+
+ protected:
+ Textfield* client() const { return client_; }
+
+ private:
+ // The view associated with the menu. Weak. Owns |this|.
+ Textfield* client_ = nullptr;
+
+ DISALLOW_COPY_AND_ASSIGN(ViewsTextServicesContextMenuBase);
+};
+
+} // namespace views
+
+#endif // UI_VIEWS_CONTROLS_VIEWS_TEXT_SERVICES_CONTEXT_MENU_BASE_H_
diff --git a/chromium/ui/views/controls/views_text_services_context_menu_mac.mm b/chromium/ui/views/controls/views_text_services_context_menu_mac.mm
index 0c4cd260bed..c7731fb7547 100644
--- a/chromium/ui/views/controls/views_text_services_context_menu_mac.mm
+++ b/chromium/ui/views/controls/views_text_services_context_menu_mac.mm
@@ -6,18 +6,16 @@
#import <Cocoa/Cocoa.h>
-#include "base/feature_list.h"
#include "ui/base/cocoa/text_services_context_menu.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/base/models/simple_menu_model.h"
#include "ui/base/resource/resource_bundle.h"
-#include "ui/base/ui_base_features.h"
#include "ui/gfx/decorated_text.h"
#import "ui/gfx/decorated_text_mac.h"
#include "ui/resources/grit/ui_resources.h"
#include "ui/strings/grit/ui_strings.h"
#include "ui/views/controls/textfield/textfield.h"
-#include "ui/views/view.h"
+#include "ui/views/controls/views_text_services_context_menu_base.h"
#include "ui/views/widget/widget.h"
namespace views {
@@ -28,26 +26,21 @@ namespace {
// text service items in the context menu. The items include Speech, Look Up
// and BiDi.
class ViewsTextServicesContextMenuMac
- : public ViewsTextServicesContextMenu,
+ : public ViewsTextServicesContextMenuBase,
public ui::TextServicesContextMenu::Delegate {
public:
ViewsTextServicesContextMenuMac(ui::SimpleMenuModel* menu, Textfield* client)
- : text_services_menu_(this), client_(client) {
- // The index to use when inserting items into the menu.
- int index = 0;
-
+ : ViewsTextServicesContextMenuBase(menu, client),
+ text_services_menu_(this) {
+ // Insert the "Look up" item in the first position.
base::string16 text = GetSelectedText();
if (!text.empty()) {
+ menu->InsertSeparatorAt(0, ui::NORMAL_SEPARATOR);
menu->InsertItemAt(
- index++, IDS_CONTENT_CONTEXT_LOOK_UP,
+ 0, IDS_CONTENT_CONTEXT_LOOK_UP,
l10n_util::GetStringFUTF16(IDS_CONTENT_CONTEXT_LOOK_UP, text));
- menu->InsertSeparatorAt(index++, ui::NORMAL_SEPARATOR);
- }
- if (base::FeatureList::IsEnabled(features::kEnableEmojiContextMenu)) {
- menu->InsertItemWithStringIdAt(index++, IDS_CONTENT_CONTEXT_EMOJI,
- IDS_CONTENT_CONTEXT_EMOJI);
- menu->InsertSeparatorAt(index++, ui::NORMAL_SEPARATOR);
}
+
text_services_menu_.AppendToContextMenu(menu);
text_services_menu_.AppendEditableItems(menu);
}
@@ -57,13 +50,8 @@ class ViewsTextServicesContextMenuMac
// ViewsTextServicesContextMenu:
bool SupportsCommand(int command_id) const override {
return text_services_menu_.SupportsCommand(command_id) ||
- command_id == IDS_CONTENT_CONTEXT_EMOJI ||
- command_id == IDS_CONTENT_CONTEXT_LOOK_UP;
- }
-
- bool IsCommandIdChecked(int command_id) const override {
- DCHECK_EQ(IDS_CONTENT_CONTEXT_LOOK_UP, command_id);
- return false;
+ command_id == IDS_CONTENT_CONTEXT_LOOK_UP ||
+ ViewsTextServicesContextMenuBase::SupportsCommand(command_id);
}
bool IsCommandIdEnabled(int command_id) const override {
@@ -71,35 +59,32 @@ class ViewsTextServicesContextMenuMac
return text_services_menu_.IsCommandIdEnabled(command_id);
switch (command_id) {
- case IDS_CONTENT_CONTEXT_EMOJI:
- return true;
-
case IDS_CONTENT_CONTEXT_LOOK_UP:
return true;
default:
- return false;
+ return ViewsTextServicesContextMenuBase::IsCommandIdEnabled(command_id);
}
}
void ExecuteCommand(int command_id) override {
switch (command_id) {
- case IDS_CONTENT_CONTEXT_EMOJI:
- [NSApp orderFrontCharacterPalette:nil];
- break;
-
case IDS_CONTENT_CONTEXT_LOOK_UP:
LookUpInDictionary();
break;
+
+ default:
+ ViewsTextServicesContextMenuBase::ExecuteCommand(command_id);
+ break;
}
}
// TextServicesContextMenu::Delegate:
base::string16 GetSelectedText() const override {
- if (client_->GetTextInputType() == ui::TEXT_INPUT_TYPE_PASSWORD)
+ if (client()->GetTextInputType() == ui::TEXT_INPUT_TYPE_PASSWORD)
return base::string16();
- return client_->GetSelectedText();
+ return client()->GetSelectedText();
}
bool IsTextDirectionEnabled(
@@ -110,7 +95,7 @@ class ViewsTextServicesContextMenuMac
bool IsTextDirectionChecked(
base::i18n::TextDirection direction) const override {
return direction != base::i18n::UNKNOWN_DIRECTION &&
- client_->GetTextDirection() == direction;
+ client()->GetTextDirection() == direction;
}
void UpdateTextDirection(base::i18n::TextDirection direction) override {
@@ -119,7 +104,7 @@ class ViewsTextServicesContextMenuMac
base::i18n::TextDirection text_direction =
direction == base::i18n::LEFT_TO_RIGHT ? base::i18n::LEFT_TO_RIGHT
: base::i18n::RIGHT_TO_LEFT;
- client_->ChangeTextDirectionAndLayoutAlignment(text_direction);
+ client()->ChangeTextDirectionAndLayoutAlignment(text_direction);
}
private:
@@ -127,10 +112,10 @@ class ViewsTextServicesContextMenuMac
void LookUpInDictionary() {
gfx::Point baseline_point;
gfx::DecoratedText text;
- if (client_->GetWordLookupDataFromSelection(&text, &baseline_point)) {
- Widget* widget = client_->GetWidget();
+ if (client()->GetWordLookupDataFromSelection(&text, &baseline_point)) {
+ Widget* widget = client()->GetWidget();
gfx::NativeView view = widget->GetNativeView();
- views::View::ConvertPointToTarget(client_, widget->GetRootView(),
+ views::View::ConvertPointToTarget(client(), widget->GetRootView(),
&baseline_point);
NSPoint lookup_point = NSMakePoint(
@@ -144,9 +129,6 @@ class ViewsTextServicesContextMenuMac
// Appends and handles the text service menu.
ui::TextServicesContextMenu text_services_menu_;
- // The view associated with the menu. Weak. Owns |this|.
- Textfield* client_;
-
DISALLOW_COPY_AND_ASSIGN(ViewsTextServicesContextMenuMac);
};
diff --git a/chromium/ui/views/controls/webview/web_dialog_view.cc b/chromium/ui/views/controls/webview/web_dialog_view.cc
index ef8b793f745..323d9f895f8 100644
--- a/chromium/ui/views/controls/webview/web_dialog_view.cc
+++ b/chromium/ui/views/controls/webview/web_dialog_view.cc
@@ -307,19 +307,16 @@ content::WebContents* WebDialogView::OpenURLFromTab(
return WebDialogWebContentsDelegate::OpenURLFromTab(source, params);
}
-void WebDialogView::AddNewContents(content::WebContents* source,
- content::WebContents* new_contents,
- WindowOpenDisposition disposition,
- const gfx::Rect& initial_rect,
- bool user_gesture,
- bool* was_blocked) {
- if (delegate_ && delegate_->HandleAddNewContents(
- source, new_contents, disposition, initial_rect, user_gesture)) {
- return;
- }
- WebDialogWebContentsDelegate::AddNewContents(
- source, new_contents, disposition, initial_rect, user_gesture,
- was_blocked);
+void WebDialogView::AddNewContents(
+ content::WebContents* source,
+ std::unique_ptr<content::WebContents> new_contents,
+ WindowOpenDisposition disposition,
+ const gfx::Rect& initial_rect,
+ bool user_gesture,
+ bool* was_blocked) {
+ WebDialogWebContentsDelegate::AddNewContents(source, std::move(new_contents),
+ disposition, initial_rect,
+ user_gesture, was_blocked);
}
void WebDialogView::LoadingStateChanged(content::WebContents* source,
diff --git a/chromium/ui/views/controls/webview/web_dialog_view.h b/chromium/ui/views/controls/webview/web_dialog_view.h
index 3a91e99a7f5..bfba06181e7 100644
--- a/chromium/ui/views/controls/webview/web_dialog_view.h
+++ b/chromium/ui/views/controls/webview/web_dialog_view.h
@@ -104,7 +104,7 @@ class WEBVIEW_EXPORT WebDialogView : public views::ClientView,
content::WebContents* source,
const content::OpenURLParams& params) override;
void AddNewContents(content::WebContents* source,
- content::WebContents* new_contents,
+ std::unique_ptr<content::WebContents> new_contents,
WindowOpenDisposition disposition,
const gfx::Rect& initial_rect,
bool user_gesture,
diff --git a/chromium/ui/views/controls/webview/webview.cc b/chromium/ui/views/controls/webview/webview.cc
index 4ec50f3b056..161185af3b8 100644
--- a/chromium/ui/views/controls/webview/webview.cc
+++ b/chromium/ui/views/controls/webview/webview.cc
@@ -6,6 +6,7 @@
#include <utility>
+#include "base/no_destructor.h"
#include "build/build_config.h"
#include "content/public/browser/browser_accessibility_state.h"
#include "content/public/browser/browser_context.h"
@@ -23,6 +24,27 @@
namespace views {
+namespace {
+
+// A testing stub that creates web contents.
+WebView::WebContentsCreator* GetCreatorForTesting() {
+ static base::NoDestructor<WebView::WebContentsCreator> creator;
+ return creator.get();
+}
+
+} // namespace
+
+WebView::ScopedWebContentsCreatorForTesting::ScopedWebContentsCreatorForTesting(
+ WebContentsCreator creator) {
+ DCHECK(!*GetCreatorForTesting());
+ *GetCreatorForTesting() = creator;
+}
+
+WebView::ScopedWebContentsCreatorForTesting::
+ ~ScopedWebContentsCreatorForTesting() {
+ *GetCreatorForTesting() = WebView::WebContentsCreator();
+}
+
// static
const char WebView::kViewClassName[] = "WebView";
@@ -44,7 +66,7 @@ WebView::~WebView() {
content::WebContents* WebView::GetWebContents() {
if (!web_contents()) {
- wc_owner_.reset(CreateWebContents(browser_context_));
+ wc_owner_ = CreateWebContents(browser_context_);
wc_owner_->SetDelegate(this);
SetWebContents(wc_owner_.get());
}
@@ -364,7 +386,6 @@ void WebView::UpdateCrashedOverlayView() {
base::TERMINATION_STATUS_STILL_RUNNING &&
crashed_overlay_view_) {
SetFocusBehavior(FocusBehavior::NEVER);
- holder_->SetVisible(false);
crashed_overlay_view_->SetVisible(true);
return;
}
@@ -374,7 +395,6 @@ void WebView::UpdateCrashedOverlayView() {
if (crashed_overlay_view_)
crashed_overlay_view_->SetVisible(false);
- holder_->SetVisible(true);
}
void WebView::NotifyAccessibilityWebContentsChanged() {
@@ -382,12 +402,11 @@ void WebView::NotifyAccessibilityWebContentsChanged() {
NotifyAccessibilityEvent(ax::mojom::Event::kChildrenChanged, false);
}
-content::WebContents* WebView::CreateWebContents(
- content::BrowserContext* browser_context) {
- content::WebContents* contents = NULL;
- if (ViewsDelegate::GetInstance()) {
- contents =
- ViewsDelegate::GetInstance()->CreateWebContents(browser_context, NULL);
+std::unique_ptr<content::WebContents> WebView::CreateWebContents(
+ content::BrowserContext* browser_context) {
+ std::unique_ptr<content::WebContents> contents;
+ if (*GetCreatorForTesting()) {
+ contents = GetCreatorForTesting()->Run(browser_context);
}
if (!contents) {
diff --git a/chromium/ui/views/controls/webview/webview.h b/chromium/ui/views/controls/webview/webview.h
index 99affc147bc..87d9a1e7259 100644
--- a/chromium/ui/views/controls/webview/webview.h
+++ b/chromium/ui/views/controls/webview/webview.h
@@ -9,6 +9,7 @@
#include <memory>
+#include "base/callback.h"
#include "base/macros.h"
#include "content/public/browser/web_contents_delegate.h"
#include "content/public/browser/web_contents_observer.h"
@@ -94,6 +95,20 @@ class WEBVIEW_EXPORT WebView : public View,
const char* GetClassName() const override;
NativeViewHost* holder() { return holder_; }
+ using WebContentsCreator =
+ base::RepeatingCallback<std::unique_ptr<content::WebContents>(
+ content::BrowserContext*)>;
+
+ // An instance of this class registers a WebContentsCreator on construction
+ // and deregisters the WebContentsCreator on destruction.
+ class WEBVIEW_EXPORT ScopedWebContentsCreatorForTesting {
+ public:
+ explicit ScopedWebContentsCreatorForTesting(WebContentsCreator creator);
+ ~ScopedWebContentsCreatorForTesting();
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(ScopedWebContentsCreatorForTesting);
+ };
protected:
// Swaps the owned WebContents |wc_owner_| with |new_web_contents|. Returns
@@ -154,7 +169,7 @@ class WEBVIEW_EXPORT WebView : public View,
// Create a regular or test web contents (based on whether we're running
// in a unit test or not).
- content::WebContents* CreateWebContents(
+ std::unique_ptr<content::WebContents> CreateWebContents(
content::BrowserContext* browser_context);
NativeViewHost* const holder_;
diff --git a/chromium/ui/views/controls/webview/webview_unittest.cc b/chromium/ui/views/controls/webview/webview_unittest.cc
index b679af9d8d2..f1d0ce7758d 100644
--- a/chromium/ui/views/controls/webview/webview_unittest.cc
+++ b/chromium/ui/views/controls/webview/webview_unittest.cc
@@ -33,29 +33,11 @@ namespace views {
namespace {
-// Provides functionality to create a test WebContents.
-class WebViewTestViewsDelegate : public views::TestViewsDelegate {
- public:
- WebViewTestViewsDelegate() {}
- ~WebViewTestViewsDelegate() override {}
-
- // Overriden from TestViewsDelegate.
- content::WebContents* CreateWebContents(
- content::BrowserContext* browser_context,
- content::SiteInstance* site_instance) override {
- return content::WebContentsTester::CreateTestWebContents(browser_context,
- site_instance);
- }
-
- private:
- DISALLOW_COPY_AND_ASSIGN(WebViewTestViewsDelegate);
-};
-
// Provides functionality to observe events on a WebContents like
// OnVisibilityChanged/WebContentsDestroyed.
class WebViewTestWebContentsObserver : public content::WebContentsObserver {
public:
- WebViewTestWebContentsObserver(content::WebContents* web_contents)
+ explicit WebViewTestWebContentsObserver(content::WebContents* web_contents)
: web_contents_(web_contents),
was_shown_(false),
shown_count_(0),
@@ -146,8 +128,19 @@ class WebViewUnitTest : public views::test::WidgetTest {
~WebViewUnitTest() override {}
+ std::unique_ptr<content::WebContents> CreateWebContentsForWebView(
+ content::BrowserContext* browser_context) {
+ return content::WebContentsTester::CreateTestWebContents(browser_context,
+ nullptr);
+ }
+
void SetUp() override {
- set_views_delegate(base::WrapUnique(new WebViewTestViewsDelegate));
+ views::WebView::WebContentsCreator creator = base::BindRepeating(
+ &WebViewUnitTest::CreateWebContentsForWebView, base::Unretained(this));
+ scoped_web_contents_creator_ =
+ std::make_unique<views::WebView::ScopedWebContentsCreatorForTesting>(
+ creator);
+ set_views_delegate(base::WrapUnique(new views::TestViewsDelegate));
browser_context_.reset(new content::TestBrowserContext);
WidgetTest::SetUp();
// Set the test content browser client to avoid pulling in needless
@@ -171,6 +164,7 @@ class WebViewUnitTest : public views::test::WidgetTest {
}
void TearDown() override {
+ scoped_web_contents_creator_.reset();
top_level_widget_->Close(); // Deletes all children and itself.
RunPendingMessages();
@@ -187,18 +181,16 @@ class WebViewUnitTest : public views::test::WidgetTest {
NativeViewHost* holder() const { return web_view_->holder_; }
std::unique_ptr<content::WebContents> CreateWebContents() const {
- return base::WrapUnique(content::WebContents::Create(
- content::WebContents::CreateParams(browser_context_.get())));
+ return content::WebContents::Create(
+ content::WebContents::CreateParams(browser_context_.get()));
}
private:
- // TODO(lukasza): https://crbug.com/832100: Move the factory into
- // TestingProfile, so individual tests don't need to worry about it.
- content::ScopedMockRenderProcessHostFactory process_factory_;
-
content::TestBrowserThreadBundle test_browser_thread_bundle_;
std::unique_ptr<content::TestBrowserContext> browser_context_;
content::TestContentBrowserClient test_browser_client_;
+ std::unique_ptr<views::WebView::ScopedWebContentsCreatorForTesting>
+ scoped_web_contents_creator_;
Widget* top_level_widget_ = nullptr;
WebView* web_view_ = nullptr;
diff --git a/chromium/ui/views/debug_utils.cc b/chromium/ui/views/debug_utils.cc
index b451c2da50e..0517f1d098f 100644
--- a/chromium/ui/views/debug_utils.cc
+++ b/chromium/ui/views/debug_utils.cc
@@ -9,6 +9,11 @@
#include "base/logging.h"
#include "ui/views/view.h"
+#if !defined(NDEBUG)
+#include "ui/gfx/geometry/angle_conversions.h"
+#include "ui/gfx/transform_util.h"
+#endif
+
namespace views {
namespace {
void PrintViewHierarchyImp(const View* view,
@@ -53,6 +58,92 @@ void PrintFocusHierarchyImp(const View* view,
if (next_focusable)
PrintFocusHierarchyImp(next_focusable, indent, out);
}
+
+#if !defined(NDEBUG)
+std::string PrintViewGraphImpl(const View* view) {
+ // 64-bit pointer = 16 bytes of hex + "0x" + '\0' = 19.
+ const size_t kMaxPointerStringLength = 19;
+
+ std::string result;
+
+ // Node characteristics.
+ char p[kMaxPointerStringLength];
+
+ const std::string class_name(view->GetClassName());
+ size_t base_name_index = class_name.find_last_of('/');
+ if (base_name_index == std::string::npos)
+ base_name_index = 0;
+ else
+ base_name_index++;
+
+ constexpr size_t kBoundsBufferSize = 512;
+ char bounds_buffer[kBoundsBufferSize];
+
+ // Information about current node.
+ base::snprintf(p, kBoundsBufferSize, "%p", view);
+ result.append(" N");
+ result.append(p + 2);
+ result.append(" [label=\"");
+
+ result.append(class_name.substr(base_name_index).c_str());
+
+ base::snprintf(bounds_buffer, kBoundsBufferSize,
+ "\\n bounds: (%d, %d), (%dx%d)", view->bounds().x(),
+ view->bounds().y(), view->bounds().width(),
+ view->bounds().height());
+ result.append(bounds_buffer);
+
+ gfx::DecomposedTransform decomp;
+ if (!view->GetTransform().IsIdentity() &&
+ gfx::DecomposeTransform(&decomp, view->GetTransform())) {
+ base::snprintf(bounds_buffer, kBoundsBufferSize,
+ "\\n translation: (%f, %f)", decomp.translate[0],
+ decomp.translate[1]);
+ result.append(bounds_buffer);
+
+ base::snprintf(bounds_buffer, kBoundsBufferSize, "\\n rotation: %3.2f",
+ gfx::RadToDeg(std::acos(decomp.quaternion.w()) * 2));
+ result.append(bounds_buffer);
+
+ base::snprintf(bounds_buffer, kBoundsBufferSize,
+ "\\n scale: (%2.4f, %2.4f)", decomp.scale[0],
+ decomp.scale[1]);
+ result.append(bounds_buffer);
+ }
+
+ result.append("\"");
+ if (!view->parent())
+ result.append(", shape=box");
+ if (view->layer()) {
+ if (view->layer()->has_external_content())
+ result.append(", color=green");
+ else
+ result.append(", color=red");
+
+ if (view->layer()->fills_bounds_opaquely())
+ result.append(", style=filled");
+ }
+ result.append("]\n");
+
+ // Link to parent.
+ if (view->parent()) {
+ char pp[kMaxPointerStringLength];
+
+ base::snprintf(pp, kMaxPointerStringLength, "%p", view->parent());
+ result.append(" N");
+ result.append(pp + 2);
+ result.append(" -> N");
+ result.append(p + 2);
+ result.append("\n");
+ }
+
+ for (int i = 0; i < view->child_count(); ++i)
+ result.append(PrintViewGraphImpl(view->child_at(i)));
+
+ return result;
+}
+#endif
+
} // namespace
void PrintViewHierarchy(const View* view) {
@@ -71,4 +162,10 @@ void PrintFocusHierarchy(const View* view) {
LOG(ERROR) << out.str();
}
+#if !defined(NDEBUG)
+std::string PrintViewGraph(const View* view) {
+ return "digraph {\n" + PrintViewGraphImpl(view) + "}\n";
+}
+#endif
+
} // namespace views
diff --git a/chromium/ui/views/debug_utils.h b/chromium/ui/views/debug_utils.h
index feb4aaac362..3051359a207 100644
--- a/chromium/ui/views/debug_utils.h
+++ b/chromium/ui/views/debug_utils.h
@@ -5,6 +5,8 @@
#ifndef UI_VIEWS_DEBUG_UTILS_H_
#define UI_VIEWS_DEBUG_UTILS_H_
+#include <string>
+
#include "ui/views/views_export.h"
namespace views {
@@ -17,6 +19,13 @@ VIEWS_EXPORT void PrintViewHierarchy(const View* view);
// Log the focus traversal hierarchy.
VIEWS_EXPORT void PrintFocusHierarchy(const View* view);
+#if !defined(NDEBUG)
+// Returns string containing a graph of the views hierarchy in graphViz DOT
+// language (http://graphviz.org/). Can be called within debugger and saved
+// to a file to compile/view.
+VIEWS_EXPORT std::string PrintViewGraph(const View* view);
+#endif
+
} // namespace views
#endif // UI_VIEWS_DEBUG_UTILS_H_
diff --git a/chromium/ui/views/examples/button_sticker_sheet.cc b/chromium/ui/views/examples/button_sticker_sheet.cc
index b395387d61a..f1d0099ec9e 100644
--- a/chromium/ui/views/examples/button_sticker_sheet.cc
+++ b/chromium/ui/views/examples/button_sticker_sheet.cc
@@ -116,11 +116,6 @@ void ButtonStickerSheet::CreateExampleView(View* container) {
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) {
diff --git a/chromium/ui/views/examples/dialog_example.cc b/chromium/ui/views/examples/dialog_example.cc
index 97737f1a809..48426144dfa 100644
--- a/chromium/ui/views/examples/dialog_example.cc
+++ b/chromium/ui/views/examples/dialog_example.cc
@@ -59,7 +59,7 @@ class DialogExample::Delegate : public virtual DialogType {
}
// DialogDelegate:
- View* CreateExtraView() {
+ View* CreateExtraView() override {
if (!parent_->has_extra_button_->checked())
return nullptr;
return MdTextButton::CreateSecondaryUiButton(
diff --git a/chromium/ui/views/examples/examples_main.cc b/chromium/ui/views/examples/examples_main.cc
index dd63355096e..9ddb537f8c5 100644
--- a/chromium/ui/views/examples/examples_main.cc
+++ b/chromium/ui/views/examples/examples_main.cc
@@ -83,7 +83,7 @@ int main(int argc, char** argv) {
ui::RegisterPathProvider();
base::FilePath ui_test_pak_path;
- CHECK(PathService::Get(ui::UI_TEST_PAK, &ui_test_pak_path));
+ CHECK(base::PathService::Get(ui::UI_TEST_PAK, &ui_test_pak_path));
ui::ResourceBundle::InitSharedInstanceWithPakPath(ui_test_pak_path);
base::DiscardableMemoryAllocator::SetInstance(
diff --git a/chromium/ui/views/examples/textfield_example.cc b/chromium/ui/views/examples/textfield_example.cc
index 6e4c5fe3ab0..8217a52d909 100644
--- a/chromium/ui/views/examples/textfield_example.cc
+++ b/chromium/ui/views/examples/textfield_example.cc
@@ -30,6 +30,7 @@ TextfieldExample::TextfieldExample()
disabled_(nullptr),
read_only_(nullptr),
invalid_(nullptr),
+ rtl_(nullptr),
show_password_(nullptr),
clear_all_(nullptr),
append_(nullptr),
@@ -51,6 +52,8 @@ void TextfieldExample::CreateExampleView(View* container) {
read_only_->SetText(ASCIIToUTF16("read only"));
invalid_ = new Textfield();
invalid_->SetInvalid(true);
+ rtl_ = new Textfield();
+ rtl_->ChangeTextDirectionAndLayoutAlignment(base::i18n::RIGHT_TO_LEFT);
show_password_ = new LabelButton(this, ASCIIToUTF16("Show password"));
set_background_ =
new LabelButton(this, ASCIIToUTF16("Set non-default background"));
@@ -81,6 +84,7 @@ void TextfieldExample::CreateExampleView(View* container) {
MakeRow(new Label(ASCIIToUTF16("Disabled:")), disabled_);
MakeRow(new Label(ASCIIToUTF16("Read Only:")), read_only_);
MakeRow(new Label(ASCIIToUTF16("Invalid:")), invalid_);
+ MakeRow(new Label(ASCIIToUTF16("RTL:")), rtl_);
MakeRow(new Label(ASCIIToUTF16("Name:")), nullptr);
MakeRow(show_password_, nullptr);
MakeRow(set_background_, nullptr);
@@ -124,18 +128,21 @@ void TextfieldExample::ButtonPressed(Button* sender, const ui::Event& event) {
disabled_->SetText(empty);
read_only_->SetText(empty);
invalid_->SetText(empty);
+ rtl_->SetText(empty);
} else if (sender == append_) {
name_->AppendText(ASCIIToUTF16("[append]"));
password_->AppendText(ASCIIToUTF16("[append]"));
disabled_->SetText(ASCIIToUTF16("[append]"));
read_only_->AppendText(ASCIIToUTF16("[append]"));
invalid_->AppendText(ASCIIToUTF16("[append]"));
+ rtl_->AppendText(ASCIIToUTF16("[append]"));
} else if (sender == set_) {
name_->SetText(ASCIIToUTF16("[set]"));
password_->SetText(ASCIIToUTF16("[set]"));
disabled_->SetText(ASCIIToUTF16("[set]"));
read_only_->SetText(ASCIIToUTF16("[set]"));
invalid_->SetText(ASCIIToUTF16("[set]"));
+ rtl_->SetText(ASCIIToUTF16("[set]"));
} else if (sender == set_style_) {
if (!name_->text().empty()) {
name_->SetColor(SK_ColorGREEN);
diff --git a/chromium/ui/views/examples/textfield_example.h b/chromium/ui/views/examples/textfield_example.h
index 730797f9c66..b2b5fd4a44f 100644
--- a/chromium/ui/views/examples/textfield_example.h
+++ b/chromium/ui/views/examples/textfield_example.h
@@ -47,6 +47,7 @@ class VIEWS_EXAMPLES_EXPORT TextfieldExample : public ExampleBase,
Textfield* disabled_;
Textfield* read_only_;
Textfield* invalid_;
+ Textfield* rtl_;
// Various buttons to control textfield.
LabelButton* show_password_;
diff --git a/chromium/ui/views/focus/focus_manager.cc b/chromium/ui/views/focus/focus_manager.cc
index fb3b9016097..6847bae9d64 100644
--- a/chromium/ui/views/focus/focus_manager.cc
+++ b/chromium/ui/views/focus/focus_manager.cc
@@ -19,6 +19,7 @@
#include "ui/views/focus/focus_search.h"
#include "ui/views/focus/widget_focus_manager.h"
#include "ui/views/view.h"
+#include "ui/views/view_properties.h"
#include "ui/views/view_tracker.h"
#include "ui/views/widget/root_view.h"
#include "ui/views/widget/widget.h"
@@ -113,7 +114,7 @@ bool FocusManager::ContainsView(View* view) {
}
void FocusManager::AdvanceFocus(bool reverse) {
- View* v = GetNextFocusableView(focused_view_, NULL, reverse, false);
+ View* v = GetNextFocusableView(focused_view_, nullptr, reverse, false);
// Note: Do not skip this next block when v == focused_view_. If the user
// tabs past the last focusable element in a webpage, we'll get here, and if
// the TabContentsContainerView is the only focusable view (possible in
@@ -124,8 +125,14 @@ void FocusManager::AdvanceFocus(bool reverse) {
v->AboutToRequestFocusFromTabTraversal(reverse);
// AboutToRequestFocusFromTabTraversal() may have changed focus. If it did,
// don't change focus again.
- if (focused_view == focused_view_)
- SetFocusedViewWithReason(v, kReasonFocusTraversal);
+ if (focused_view != focused_view_)
+ return;
+
+ // Note that GetNextFocusableView may have returned a View in a different
+ // FocusManager.
+ DCHECK(v->GetWidget());
+ v->GetWidget()->GetFocusManager()->SetFocusedViewWithReason(
+ v, kReasonFocusTraversal);
}
}
@@ -199,9 +206,9 @@ View* FocusManager::GetNextFocusableView(View* original_starting_view,
DCHECK(!focused_view_ || ContainsView(focused_view_))
<< " focus_view=" << focused_view_;
- FocusTraversable* focus_traversable = NULL;
+ FocusTraversable* focus_traversable = nullptr;
- View* starting_view = NULL;
+ View* starting_view = nullptr;
if (original_starting_view) {
// Search up the containment hierarchy to see if a view is acting as
// a pane, and wants to implement its own focus traversable to keep
@@ -254,9 +261,15 @@ View* FocusManager::GetNextFocusableView(View* original_starting_view,
FocusTraversable* new_focus_traversable = nullptr;
View* new_starting_view = nullptr;
// When we are going backward, the parent view might gain the next focus.
- bool check_starting_view = reverse;
+ auto check_starting_view =
+ reverse ? FocusSearch::StartingViewPolicy::kCheckStartingView
+ : FocusSearch::StartingViewPolicy::kSkipStartingView;
v = parent_focus_traversable->GetFocusSearch()->FindNextFocusableView(
- starting_view, reverse, FocusSearch::UP, check_starting_view,
+ starting_view,
+ reverse ? FocusSearch::SearchDirection::kBackwards
+ : FocusSearch::SearchDirection::kForwards,
+ FocusSearch::TraversalDirection::kUp, check_starting_view,
+ FocusSearch::AnchoredDialogPolicy::kSkipAnchoredDialog,
&new_focus_traversable, &new_starting_view);
if (new_focus_traversable) {
@@ -281,7 +294,7 @@ View* FocusManager::GetNextFocusableView(View* original_starting_view,
return nullptr;
// Easy, just clear the selection and press tab again.
- // By calling with NULL as the starting view, we'll start from either
+ // By calling with nullptr as the starting view, we'll start from either
// the starting views widget or |widget_|.
Widget* widget = original_starting_view->GetWidget();
if (widget->widget_delegate()->ShouldAdvanceFocusToTopLevelWidget())
@@ -303,6 +316,7 @@ void FocusManager::SetFocusedViewWithReason(View* view,
FocusChangeReason reason) {
if (focused_view_ == view)
return;
+
// TODO(oshima|achuith): This is to diagnose crbug.com/687232.
// Change this to DCHECK once it's resolved.
CHECK(!view || ContainsView(view));
@@ -345,13 +359,16 @@ void FocusManager::SetFocusedViewWithReason(View* view,
for (FocusChangeListener& observer : focus_change_listeners_)
observer.OnDidChangeFocus(old_focused_view, focused_view_);
+
+ if (delegate_)
+ delegate_->OnDidChangeFocus(old_focused_view, focused_view_);
}
void FocusManager::ClearFocus() {
- // SetFocusedView(NULL) is going to clear out the stored view to. We need to
- // persist it in this case.
+ // SetFocusedView(nullptr) is going to clear out the stored view to. We need
+ // to persist it in this case.
views::View* focused_view = GetStoredFocusView();
- SetFocusedView(NULL);
+ SetFocusedView(nullptr);
ClearNativeFocus();
SetStoredFocusView(focused_view);
}
@@ -373,8 +390,8 @@ void FocusManager::AdvanceFocusIfNecessary() {
void FocusManager::StoreFocusedView(bool clear_native_focus) {
View* focused_view = focused_view_;
- // Don't do anything if no focused view. Storing the view (which is NULL), in
- // this case, would clobber the view that was previously saved.
+ // Don't do anything if no focused view. Storing the view (which is nullptr),
+ // in this case, would clobber the view that was previously saved.
if (!focused_view_)
return;
@@ -389,7 +406,7 @@ void FocusManager::StoreFocusedView(bool clear_native_focus) {
// ClearFocus() also stores the focused view.
ClearFocus();
} else {
- SetFocusedView(NULL);
+ SetFocusedView(nullptr);
SetStoredFocusView(focused_view);
}
@@ -437,21 +454,32 @@ View* FocusManager::GetStoredFocusView() {
View* FocusManager::FindFocusableView(FocusTraversable* focus_traversable,
View* starting_view,
bool reverse) {
- FocusTraversable* new_focus_traversable = NULL;
- View* new_starting_view = NULL;
+ FocusTraversable* new_focus_traversable = nullptr;
+ View* new_starting_view = nullptr;
+ auto can_go_into_anchored_dialog =
+ FocusSearch::AnchoredDialogPolicy::kCanGoIntoAnchoredDialog;
View* v = focus_traversable->GetFocusSearch()->FindNextFocusableView(
- starting_view, reverse, FocusSearch::DOWN, false, &new_focus_traversable,
- &new_starting_view);
+ starting_view,
+ reverse ? FocusSearch::SearchDirection::kBackwards
+ : FocusSearch::SearchDirection::kForwards,
+ FocusSearch::TraversalDirection::kDown,
+ FocusSearch::StartingViewPolicy::kSkipStartingView,
+ can_go_into_anchored_dialog, &new_focus_traversable, &new_starting_view);
// Let's go down the FocusTraversable tree as much as we can.
while (new_focus_traversable) {
DCHECK(!v);
focus_traversable = new_focus_traversable;
- new_focus_traversable = NULL;
- starting_view = NULL;
+ new_focus_traversable = nullptr;
+ starting_view = nullptr;
v = focus_traversable->GetFocusSearch()->FindNextFocusableView(
- starting_view, reverse, FocusSearch::DOWN, false,
- &new_focus_traversable, &new_starting_view);
+ starting_view,
+ reverse ? FocusSearch::SearchDirection::kBackwards
+ : FocusSearch::SearchDirection::kForwards,
+ FocusSearch::TraversalDirection::kDown,
+ FocusSearch::StartingViewPolicy::kSkipStartingView,
+ can_go_into_anchored_dialog, &new_focus_traversable,
+ &new_starting_view);
}
return v;
}
@@ -485,7 +513,8 @@ bool FocusManager::HasPriorityHandler(
// static
bool FocusManager::IsTabTraversalKeyEvent(const ui::KeyEvent& key_event) {
- return key_event.key_code() == ui::VKEY_TAB && !key_event.IsControlDown();
+ return key_event.key_code() == ui::VKEY_TAB &&
+ (!key_event.IsControlDown() && !key_event.IsAltDown());
}
void FocusManager::ViewRemoved(View* removed) {
@@ -495,7 +524,7 @@ void FocusManager::ViewRemoved(View* removed) {
// be called while the top level widget is being destroyed.
DCHECK(removed);
if (removed->Contains(focused_view_))
- SetFocusedView(NULL);
+ SetFocusedView(nullptr);
}
void FocusManager::AddFocusChangeListener(FocusChangeListener* listener) {
diff --git a/chromium/ui/views/focus/focus_manager_delegate.h b/chromium/ui/views/focus/focus_manager_delegate.h
index a4d9186f22f..c57571bbc4d 100644
--- a/chromium/ui/views/focus/focus_manager_delegate.h
+++ b/chromium/ui/views/focus/focus_manager_delegate.h
@@ -13,6 +13,8 @@ class Accelerator;
namespace views {
+class View;
+
// Delegate interface for views::FocusManager.
class VIEWS_EXPORT FocusManagerDelegate {
public:
@@ -25,6 +27,9 @@ class VIEWS_EXPORT FocusManagerDelegate {
// target, and so on.
// Returns true if an accelerator was activated.
virtual bool ProcessAccelerator(const ui::Accelerator& accelerator) = 0;
+
+ // Called after focus state has changed.
+ virtual void OnDidChangeFocus(View* focused_before, View* focused_now) = 0;
};
} // namespace views
diff --git a/chromium/ui/views/focus/focus_manager_unittest.cc b/chromium/ui/views/focus/focus_manager_unittest.cc
index 809b6d8a3ac..3c1278fe2f3 100644
--- a/chromium/ui/views/focus/focus_manager_unittest.cc
+++ b/chromium/ui/views/focus/focus_manager_unittest.cc
@@ -16,11 +16,13 @@
#include "ui/base/accelerators/accelerator.h"
#include "ui/events/keycodes/keyboard_codes.h"
#include "ui/views/accessible_pane_view.h"
+#include "ui/views/bubble/bubble_dialog_delegate.h"
#include "ui/views/focus/focus_manager_delegate.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"
#include "ui/views/test/widget_test.h"
+#include "ui/views/view_properties.h"
#include "ui/views/widget/widget.h"
namespace views {
@@ -861,6 +863,19 @@ class AdvanceFocusWidgetDelegate : public WidgetDelegate {
DISALLOW_COPY_AND_ASSIGN(AdvanceFocusWidgetDelegate);
};
+class TestBubbleDialogDelegateView : public BubbleDialogDelegateView {
+ public:
+ TestBubbleDialogDelegateView(View* anchor)
+ : BubbleDialogDelegateView(anchor, BubbleBorder::NONE) {}
+ ~TestBubbleDialogDelegateView() override {}
+
+ // ui::DialogModel override.
+ int GetDialogButtons() const override { return 0; }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(TestBubbleDialogDelegateView);
+};
+
} // namespace
// Verifies focus wrapping happens in the same widget.
@@ -913,4 +928,123 @@ TEST_F(FocusManagerTest, AdvanceFocusStaysInWidget) {
EXPECT_EQ(widget_view, GetFocusManager()->GetFocusedView());
}
+TEST_F(FocusManagerTest, NavigateIntoAnchoredDialog) {
+ // The parent Widget has four focusable views. A child widget dialog has
+ // two focusable views, and it's anchored to the 3rd parent view. Ensure
+ // that focus traverses into the anchored dialog after the 3rd parent
+ // view, and then back to the 4th parent view.
+
+ View* parent1 = new View();
+ View* parent2 = new View();
+ View* parent3 = new View();
+ View* parent4 = new View();
+
+ parent1->SetFocusBehavior(View::FocusBehavior::ALWAYS);
+ parent2->SetFocusBehavior(View::FocusBehavior::ALWAYS);
+ parent3->SetFocusBehavior(View::FocusBehavior::ALWAYS);
+ parent4->SetFocusBehavior(View::FocusBehavior::ALWAYS);
+
+ GetWidget()->GetRootView()->AddChildView(parent1);
+ GetWidget()->GetRootView()->AddChildView(parent2);
+ GetWidget()->GetRootView()->AddChildView(parent3);
+ GetWidget()->GetRootView()->AddChildView(parent4);
+
+ BubbleDialogDelegateView* bubble_delegate =
+ new TestBubbleDialogDelegateView(parent3);
+ test::WidgetTest::WidgetAutoclosePtr bubble_widget(
+ BubbleDialogDelegateView::CreateBubble(bubble_delegate));
+ bubble_delegate->EnableFocusTraversalFromAnchorView();
+ View* child1 = new View();
+ View* child2 = new View();
+ child1->SetFocusBehavior(View::FocusBehavior::ALWAYS);
+ child2->SetFocusBehavior(View::FocusBehavior::ALWAYS);
+ bubble_widget->GetRootView()->AddChildView(child1);
+ bubble_widget->GetRootView()->AddChildView(child2);
+ bubble_delegate->set_close_on_deactivate(false);
+ bubble_widget->Show();
+
+ parent1->RequestFocus();
+
+ // Navigate forwards
+ GetWidget()->GetFocusManager()->AdvanceFocus(false);
+ EXPECT_TRUE(parent2->HasFocus());
+ GetWidget()->GetFocusManager()->AdvanceFocus(false);
+ EXPECT_TRUE(parent3->HasFocus());
+ GetWidget()->GetFocusManager()->AdvanceFocus(false);
+ EXPECT_TRUE(child1->HasFocus());
+ bubble_widget->GetFocusManager()->AdvanceFocus(false);
+ EXPECT_TRUE(child2->HasFocus());
+ bubble_widget->GetFocusManager()->AdvanceFocus(false);
+ EXPECT_TRUE(parent4->HasFocus());
+
+ // Navigate backwards
+ GetWidget()->GetFocusManager()->AdvanceFocus(true);
+ EXPECT_TRUE(child2->HasFocus());
+ bubble_widget->GetFocusManager()->AdvanceFocus(true);
+ EXPECT_TRUE(child1->HasFocus());
+ bubble_widget->GetFocusManager()->AdvanceFocus(true);
+ EXPECT_TRUE(parent3->HasFocus());
+}
+
+TEST_F(FocusManagerTest, AnchoredDialogOnContainerView) {
+ // The parent Widget has four focusable views, with the middle two views
+ // inside of a non-focusable grouping View. A child widget dialog has
+ // two focusable views, and it's anchored to the group View. Ensure
+ // that focus traverses into the anchored dialog after the 3rd parent
+ // view, and then back to the 4th parent view.
+
+ View* parent1 = new View();
+ View* parent2 = new View();
+ View* parent3 = new View();
+ View* parent4 = new View();
+ View* parent_group = new View();
+
+ parent1->SetFocusBehavior(View::FocusBehavior::ALWAYS);
+ parent2->SetFocusBehavior(View::FocusBehavior::ALWAYS);
+ parent3->SetFocusBehavior(View::FocusBehavior::ALWAYS);
+ parent4->SetFocusBehavior(View::FocusBehavior::ALWAYS);
+
+ GetWidget()->GetRootView()->AddChildView(parent1);
+ GetWidget()->GetRootView()->AddChildView(parent_group);
+ parent_group->AddChildView(parent2);
+ parent_group->AddChildView(parent3);
+ GetWidget()->GetRootView()->AddChildView(parent4);
+
+ BubbleDialogDelegateView* bubble_delegate =
+ new TestBubbleDialogDelegateView(parent_group);
+ test::WidgetTest::WidgetAutoclosePtr bubble_widget(
+ BubbleDialogDelegateView::CreateBubble(bubble_delegate));
+ bubble_delegate->EnableFocusTraversalFromAnchorView();
+ View* child1 = new View();
+ View* child2 = new View();
+ child1->SetFocusBehavior(View::FocusBehavior::ALWAYS);
+ child2->SetFocusBehavior(View::FocusBehavior::ALWAYS);
+ bubble_widget->GetRootView()->AddChildView(child1);
+ bubble_widget->GetRootView()->AddChildView(child2);
+ bubble_delegate->set_close_on_deactivate(false);
+ bubble_widget->Show();
+
+ parent1->RequestFocus();
+
+ // Navigate forwards
+ GetWidget()->GetFocusManager()->AdvanceFocus(false);
+ EXPECT_TRUE(parent2->HasFocus());
+ GetWidget()->GetFocusManager()->AdvanceFocus(false);
+ EXPECT_TRUE(parent3->HasFocus());
+ GetWidget()->GetFocusManager()->AdvanceFocus(false);
+ EXPECT_TRUE(child1->HasFocus());
+ bubble_widget->GetFocusManager()->AdvanceFocus(false);
+ EXPECT_TRUE(child2->HasFocus());
+ bubble_widget->GetFocusManager()->AdvanceFocus(false);
+ EXPECT_TRUE(parent4->HasFocus());
+
+ // Navigate backwards
+ GetWidget()->GetFocusManager()->AdvanceFocus(true);
+ EXPECT_TRUE(child2->HasFocus());
+ bubble_widget->GetFocusManager()->AdvanceFocus(true);
+ EXPECT_TRUE(child1->HasFocus());
+ bubble_widget->GetFocusManager()->AdvanceFocus(true);
+ EXPECT_TRUE(parent3->HasFocus());
+}
+
} // namespace views
diff --git a/chromium/ui/views/focus/focus_search.cc b/chromium/ui/views/focus/focus_search.cc
index e91a701fd10..4c33f0b266e 100644
--- a/chromium/ui/views/focus/focus_search.cc
+++ b/chromium/ui/views/focus/focus_search.cc
@@ -2,10 +2,13 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
+#include "ui/views/focus/focus_search.h"
+
#include "base/logging.h"
+#include "ui/views/bubble/bubble_dialog_delegate.h"
#include "ui/views/focus/focus_manager.h"
-#include "ui/views/focus/focus_search.h"
#include "ui/views/view.h"
+#include "ui/views/view_properties.h"
#include "ui/views/widget/widget.h"
namespace views {
@@ -22,19 +25,21 @@ FocusSearch::FocusSearch(View* root, bool cycle, bool accessibility_mode)
#endif
}
-View* FocusSearch::FindNextFocusableView(View* starting_view,
- bool reverse,
- Direction direction,
- bool check_starting_view,
- FocusTraversable** focus_traversable,
- View** focus_traversable_view) {
- *focus_traversable = NULL;
- *focus_traversable_view = NULL;
+View* FocusSearch::FindNextFocusableView(
+ View* starting_view,
+ FocusSearch::SearchDirection search_direction,
+ FocusSearch::TraversalDirection traversal_direction,
+ FocusSearch::StartingViewPolicy check_starting_view,
+ FocusSearch::AnchoredDialogPolicy can_go_into_anchored_dialog,
+ FocusTraversable** focus_traversable,
+ View** focus_traversable_view) {
+ *focus_traversable = nullptr;
+ *focus_traversable_view = nullptr;
if (!root_->has_children()) {
NOTREACHED();
// Nothing to focus on here.
- return NULL;
+ return nullptr;
}
View* initial_starting_view = starting_view;
@@ -44,43 +49,42 @@ View* FocusSearch::FindNextFocusableView(View* starting_view,
if (!starting_view) {
// Default to the first/last child
- starting_view = reverse ? root_->child_at(root_->child_count() - 1) :
- root_->child_at(0);
+ starting_view = search_direction == SearchDirection::kBackwards
+ ? root_->child_at(root_->child_count() - 1)
+ : root_->child_at(0);
// If there was no starting view, then the one we select is a potential
// focus candidate.
- check_starting_view = true;
+ check_starting_view = StartingViewPolicy::kCheckStartingView;
} else {
// The starting view should be a direct or indirect child of the root.
DCHECK(Contains(root_, starting_view));
}
- View* v = NULL;
- if (!reverse) {
- v = FindNextFocusableViewImpl(starting_view, check_starting_view,
- true,
- (direction == DOWN),
- starting_view_group,
- focus_traversable,
- focus_traversable_view);
+ View* v = nullptr;
+ if (search_direction == SearchDirection::kForwards) {
+ v = FindNextFocusableViewImpl(
+ starting_view, check_starting_view, true,
+ (traversal_direction == TraversalDirection::kDown), starting_view_group,
+ focus_traversable, focus_traversable_view);
} else {
// If the starting view is focusable, we don't want to go down, as we are
// traversing the view hierarchy tree bottom-up.
- bool can_go_down = (direction == DOWN) && !IsFocusable(starting_view);
- v = FindPreviousFocusableViewImpl(starting_view, check_starting_view,
- true,
- can_go_down,
- starting_view_group,
- focus_traversable,
+ bool can_go_down = (traversal_direction == TraversalDirection::kDown) &&
+ !IsFocusable(starting_view);
+ v = FindPreviousFocusableViewImpl(starting_view, check_starting_view, true,
+ can_go_down, can_go_into_anchored_dialog,
+ starting_view_group, focus_traversable,
focus_traversable_view);
}
// Don't set the focus to something outside of this view hierarchy.
if (v && v != root_ && !Contains(root_, v))
- v = NULL;
+ v = nullptr;
- // If |cycle_| is true, prefer to keep cycling rather than returning NULL.
+ // If |cycle_| is true, prefer to keep cycling rather than returning nullptr.
if (cycle_ && !v && initial_starting_view) {
- v = FindNextFocusableView(NULL, reverse, direction, check_starting_view,
+ v = FindNextFocusableView(nullptr, search_direction, traversal_direction,
+ check_starting_view, can_go_into_anchored_dialog,
focus_traversable, focus_traversable_view);
DCHECK(IsFocusable(v));
return v;
@@ -93,10 +97,10 @@ View* FocusSearch::FindNextFocusableView(View* starting_view,
}
if (*focus_traversable) {
DCHECK(*focus_traversable_view);
- return NULL;
+ return nullptr;
}
// Nothing found.
- return NULL;
+ return nullptr;
}
bool FocusSearch::IsViewFocusableCandidate(View* v, int skip_group_id) {
@@ -131,7 +135,7 @@ View* FocusSearch::FindSelectedViewForGroup(View* view) {
}
View* FocusSearch::GetParent(View* v) {
- return Contains(root_, v) ? v->parent() : NULL;
+ return Contains(root_, v) ? v->parent() : nullptr;
}
bool FocusSearch::Contains(View* root, const View* v) {
@@ -148,13 +152,13 @@ bool FocusSearch::Contains(View* root, const View* v) {
// with a right sibling and start the search from there.
View* FocusSearch::FindNextFocusableViewImpl(
View* starting_view,
- bool check_starting_view,
+ FocusSearch::StartingViewPolicy check_starting_view,
bool can_go_up,
bool can_go_down,
int skip_group_id,
FocusTraversable** focus_traversable,
View** focus_traversable_view) {
- if (check_starting_view) {
+ if (check_starting_view == StartingViewPolicy::kCheckStartingView) {
if (IsViewFocusableCandidate(starting_view, skip_group_id)) {
View* v = FindSelectedViewForGroup(starting_view);
// The selected view might not be focusable (if it is disabled for
@@ -166,29 +170,37 @@ View* FocusSearch::FindNextFocusableViewImpl(
*focus_traversable = starting_view->GetFocusTraversable();
if (*focus_traversable) {
*focus_traversable_view = starting_view;
- return NULL;
+ return nullptr;
}
}
// First let's try the left child.
if (can_go_down) {
if (starting_view->has_children()) {
- View* v = FindNextFocusableViewImpl(starting_view->child_at(0),
- true, false, true, skip_group_id,
- focus_traversable,
- focus_traversable_view);
+ View* v = FindNextFocusableViewImpl(
+ starting_view->child_at(0), StartingViewPolicy::kCheckStartingView,
+ false, true, skip_group_id, focus_traversable,
+ focus_traversable_view);
if (v || *focus_traversable)
return v;
+ } else {
+ // Check to see if we should navigate into a dialog anchored at this view.
+ BubbleDialogDelegateView* bubble =
+ starting_view->GetProperty(kAnchoredDialogKey);
+ if (bubble) {
+ *focus_traversable = bubble->GetWidget()->GetFocusTraversable();
+ *focus_traversable_view = starting_view;
+ return nullptr;
+ }
}
}
// Then try the right sibling.
View* sibling = starting_view->GetNextFocusableView();
if (sibling) {
- View* v = FindNextFocusableViewImpl(sibling,
- true, false, true, skip_group_id,
- focus_traversable,
- focus_traversable_view);
+ View* v = FindNextFocusableViewImpl(
+ sibling, FocusSearch::StartingViewPolicy::kCheckStartingView, false,
+ true, skip_group_id, focus_traversable, focus_traversable_view);
if (v || *focus_traversable)
return v;
}
@@ -197,20 +209,26 @@ View* FocusSearch::FindNextFocusableViewImpl(
if (can_go_up) {
View* parent = GetParent(starting_view);
while (parent && parent != root_) {
+ BubbleDialogDelegateView* bubble =
+ parent->GetProperty(kAnchoredDialogKey);
+ if (bubble) {
+ *focus_traversable = bubble->GetWidget()->GetFocusTraversable();
+ *focus_traversable_view = starting_view;
+ return nullptr;
+ }
+
sibling = parent->GetNextFocusableView();
if (sibling) {
- return FindNextFocusableViewImpl(sibling,
- true, true, true,
- skip_group_id,
- focus_traversable,
- focus_traversable_view);
+ return FindNextFocusableViewImpl(
+ sibling, StartingViewPolicy::kCheckStartingView, true, true,
+ skip_group_id, focus_traversable, focus_traversable_view);
}
parent = GetParent(parent);
}
}
// We found nothing.
- return NULL;
+ return nullptr;
}
// Strategy for finding the previous focusable view:
@@ -221,12 +239,24 @@ View* FocusSearch::FindNextFocusableViewImpl(
// down).
View* FocusSearch::FindPreviousFocusableViewImpl(
View* starting_view,
- bool check_starting_view,
+ FocusSearch::StartingViewPolicy check_starting_view,
bool can_go_up,
bool can_go_down,
+ FocusSearch::AnchoredDialogPolicy can_go_into_anchored_dialog,
int skip_group_id,
FocusTraversable** focus_traversable,
View** focus_traversable_view) {
+ // Normally when we navigate to a FocusTraversableParent, can_go_down is
+ // false so we don't navigate back in. However, if we just navigated out
+ // of an anchored dialog, allow going down in order to navigate into
+ // children of |starting_view| next.
+ if (starting_view->GetProperty(kAnchoredDialogKey) &&
+ can_go_into_anchored_dialog ==
+ AnchoredDialogPolicy::kSkipAnchoredDialog &&
+ !can_go_down) {
+ can_go_down = true;
+ }
+
// Let's go down and right as much as we can.
if (can_go_down) {
// Before we go into the direct children, we have to check if this view has
@@ -234,16 +264,30 @@ View* FocusSearch::FindPreviousFocusableViewImpl(
*focus_traversable = starting_view->GetFocusTraversable();
if (*focus_traversable) {
*focus_traversable_view = starting_view;
- return NULL;
+ return nullptr;
+ }
+
+ // Check to see if we should navigate into a dialog anchored at this view.
+ if (can_go_into_anchored_dialog ==
+ AnchoredDialogPolicy::kCanGoIntoAnchoredDialog) {
+ BubbleDialogDelegateView* bubble =
+ starting_view->GetProperty(kAnchoredDialogKey);
+ if (bubble) {
+ *focus_traversable = bubble->GetWidget()->GetFocusTraversable();
+ *focus_traversable_view = starting_view;
+ return nullptr;
+ }
}
+ can_go_into_anchored_dialog =
+ AnchoredDialogPolicy::kCanGoIntoAnchoredDialog;
if (starting_view->has_children()) {
View* view =
starting_view->child_at(starting_view->child_count() - 1);
- View* v = FindPreviousFocusableViewImpl(view, true, false, true,
- skip_group_id,
- focus_traversable,
- focus_traversable_view);
+ View* v = FindPreviousFocusableViewImpl(
+ view, StartingViewPolicy::kCheckStartingView, false, true,
+ can_go_into_anchored_dialog, skip_group_id, focus_traversable,
+ focus_traversable_view);
if (v || *focus_traversable)
return v;
}
@@ -251,7 +295,7 @@ View* FocusSearch::FindPreviousFocusableViewImpl(
// Then look at this view. Here, we do not need to see if the view has
// a FocusTraversable, since we do not want to go down any more.
- if (check_starting_view &&
+ if (check_starting_view == StartingViewPolicy::kCheckStartingView &&
IsViewFocusableCandidate(starting_view, skip_group_id)) {
View* v = FindSelectedViewForGroup(starting_view);
// The selected view might not be focusable (if it is disabled for
@@ -263,26 +307,24 @@ View* FocusSearch::FindPreviousFocusableViewImpl(
// Then try the left sibling.
View* sibling = starting_view->GetPreviousFocusableView();
if (sibling) {
- return FindPreviousFocusableViewImpl(sibling,
- true, can_go_up, true,
- skip_group_id,
- focus_traversable,
- focus_traversable_view);
+ return FindPreviousFocusableViewImpl(
+ sibling, StartingViewPolicy::kCheckStartingView, can_go_up, true,
+ can_go_into_anchored_dialog, skip_group_id, focus_traversable,
+ focus_traversable_view);
}
// Then go up the parent.
if (can_go_up) {
View* parent = GetParent(starting_view);
if (parent)
- return FindPreviousFocusableViewImpl(parent,
- true, true, false,
- skip_group_id,
- focus_traversable,
- focus_traversable_view);
+ return FindPreviousFocusableViewImpl(
+ parent, StartingViewPolicy::kCheckStartingView, true, false,
+ can_go_into_anchored_dialog, skip_group_id, focus_traversable,
+ focus_traversable_view);
}
// We found nothing.
- return NULL;
+ return nullptr;
}
} // namespace views
diff --git a/chromium/ui/views/focus/focus_search.h b/chromium/ui/views/focus/focus_search.h
index 9da7df45ae9..4d315449d54 100644
--- a/chromium/ui/views/focus/focus_search.h
+++ b/chromium/ui/views/focus/focus_search.h
@@ -21,9 +21,24 @@ class VIEWS_EXPORT FocusSearch {
// goal is to switch to focusable views on the same level when using the arrow
// keys (ala Windows: in a dialog box, arrow keys typically move between the
// dialog OK, Cancel buttons).
- enum Direction {
- UP = 0,
- DOWN
+ enum class TraversalDirection {
+ kUp,
+ kDown,
+ };
+
+ enum class SearchDirection {
+ kForwards,
+ kBackwards,
+ };
+
+ enum class StartingViewPolicy {
+ kSkipStartingView,
+ kCheckStartingView,
+ };
+
+ enum class AnchoredDialogPolicy {
+ kSkipAnchoredDialog,
+ kCanGoIntoAnchoredDialog,
};
// Constructor.
@@ -52,22 +67,28 @@ class VIEWS_EXPORT FocusSearch {
// - |starting_view| is the view that should be used as the starting point
// when looking for the previous/next view. It may be NULL (in which case
// the first/last view should be used depending if normal/reverse).
- // - |reverse| whether we should find the next (reverse is false) or the
- // previous (reverse is true) view.
- // - |direction| specifies whether we are traversing down (meaning we should
- // look into child views) or traversing up (don't look at child views).
- // - |check_starting_view| is true if starting_view may obtain the next focus.
+ // - |search_direction| whether we should find the next (kForwards) or
+ // previous (kReverse) view.
+ // - |traversal_direction| specifies whether we are traversing down (meaning
+ // we should look into child views) or traversing up (don't look at
+ // child views).
+ // - |check_starting_view| indicated if starting_view may obtain the next
+ // focus.
+ // - |can_go_into_anchored_dialog| controls if focus is allowed to jump
+ // into a dialog anchored at one of the views being traversed.
// - |focus_traversable| is set to the focus traversable that should be
// traversed if one is found (in which case the call returns NULL).
// - |focus_traversable_view| is set to the view associated with the
// FocusTraversable set in the previous parameter (it is used as the
// starting view when looking for the next focusable view).
- virtual View* FindNextFocusableView(View* starting_view,
- bool reverse,
- Direction direction,
- bool check_starting_view,
- FocusTraversable** focus_traversable,
- View** focus_traversable_view);
+ virtual View* FindNextFocusableView(
+ View* starting_view,
+ SearchDirection search_direction,
+ TraversalDirection traversal_direction,
+ StartingViewPolicy check_starting_view,
+ AnchoredDialogPolicy can_go_into_anchored_dialog,
+ FocusTraversable** focus_traversable,
+ View** focus_traversable_view);
protected:
// Get the parent, but stay within the root. Returns NULL if asked for
@@ -102,7 +123,7 @@ class VIEWS_EXPORT FocusSearch {
// traversal of the views hierarchy. |skip_group_id| specifies a group_id,
// -1 means no group. All views from a group are traversed in one pass.
View* FindNextFocusableViewImpl(View* starting_view,
- bool check_starting_view,
+ StartingViewPolicy check_starting_view,
bool can_go_up,
bool can_go_down,
int skip_group_id,
@@ -110,13 +131,15 @@ class VIEWS_EXPORT FocusSearch {
View** focus_traversable_view);
// Same as FindNextFocusableViewImpl but returns the previous focusable view.
- View* FindPreviousFocusableViewImpl(View* starting_view,
- bool check_starting_view,
- bool can_go_up,
- bool can_go_down,
- int skip_group_id,
- FocusTraversable** focus_traversable,
- View** focus_traversable_view);
+ View* FindPreviousFocusableViewImpl(
+ View* starting_view,
+ StartingViewPolicy check_starting_view,
+ bool can_go_up,
+ bool can_go_down,
+ AnchoredDialogPolicy can_go_into_anchored_dialog,
+ int skip_group_id,
+ FocusTraversable** focus_traversable,
+ View** focus_traversable_view);
View* root_;
bool cycle_;
diff --git a/chromium/ui/views/layout/layout_provider.cc b/chromium/ui/views/layout/layout_provider.cc
index 1902f4ff539..822088d3723 100644
--- a/chromium/ui/views/layout/layout_provider.cc
+++ b/chromium/ui/views/layout/layout_provider.cc
@@ -141,4 +141,27 @@ gfx::Insets LayoutProvider::GetDialogInsetsForContentType(
dialog_insets.right());
}
+int LayoutProvider::GetCornerRadiusMetric(EmphasisMetric emphasis_metric,
+ const gfx::Size& size) const {
+ const bool is_touch =
+ ui::MaterialDesignController::IsTouchOptimizedUiEnabled();
+ switch (emphasis_metric) {
+ case EMPHASIS_LOW:
+ return is_touch ? 4 : 2;
+ case EMPHASIS_MEDIUM:
+ return is_touch ? 8 : 4;
+ case EMPHASIS_HIGH:
+ return is_touch ? std::min(size.width(), size.height()) / 2 : 4;
+ default:
+ NOTREACHED();
+ return 0;
+ }
+}
+
+int LayoutProvider::GetShadowElevationMetric(
+ EmphasisMetric emphasis_metric) const {
+ // Just return a value for now.
+ return 2;
+}
+
} // namespace views
diff --git a/chromium/ui/views/layout/layout_provider.h b/chromium/ui/views/layout/layout_provider.h
index 991b8e51681..c61276edd2b 100644
--- a/chromium/ui/views/layout/layout_provider.h
+++ b/chromium/ui/views/layout/layout_provider.h
@@ -7,6 +7,7 @@
#include "base/macros.h"
#include "ui/gfx/geometry/insets.h"
+#include "ui/gfx/geometry/size.h"
#include "ui/views/style/typography_provider.h"
#include "ui/views/views_export.h"
@@ -110,6 +111,18 @@ enum DistanceMetric {
// elements that only show text. Otherwise CONTROL should be used.
enum DialogContentType { CONTROL, TEXT };
+enum EmphasisMetric {
+ // No emphasis needed for shadows, corner radius, etc.
+ EMPHASIS_NONE,
+ // Use this to indicate low-emphasis interactive elements such as buttons and
+ // text fields.
+ EMPHASIS_LOW,
+ // Use this for components with medium emphasis, such as tabs or dialogs.
+ EMPHASIS_MEDIUM,
+ // High-emphasis components like the omnibox or rich suggestions.
+ EMPHASIS_HIGH,
+};
+
class VIEWS_EXPORT LayoutProvider {
public:
LayoutProvider();
@@ -145,6 +158,18 @@ class VIEWS_EXPORT LayoutProvider {
gfx::Insets GetDialogInsetsForContentType(DialogContentType leading,
DialogContentType trailing) const;
+ // TODO (https://crbug.com/822000): Possibly combine the following two
+ // functions into a single function returning a struct. Keeping them separate
+ // for now in case different emphasis is needed for different elements in the
+ // same context. Delete this TODO in Q4 2018.
+
+ // Returns the corner radius specific to the given emphasis metric.
+ virtual int GetCornerRadiusMetric(EmphasisMetric emphasis_metric,
+ const gfx::Size& size = gfx::Size()) const;
+
+ // Returns the shadow elevation metric for the given emphasis.
+ virtual int GetShadowElevationMetric(EmphasisMetric emphasis_metric) const;
+
private:
DefaultTypographyProvider typography_provider_;
diff --git a/chromium/ui/views/linux_ui/linux_ui.h b/chromium/ui/views/linux_ui/linux_ui.h
index 26c49ae32f6..07f30dfe53a 100644
--- a/chromium/ui/views/linux_ui/linux_ui.h
+++ b/chromium/ui/views/linux_ui/linux_ui.h
@@ -196,6 +196,9 @@ class VIEWS_EXPORT LinuxUI : public ui::LinuxInputMethodContextFactory,
// toolkit does not support drawing client-side navigation buttons.
virtual std::unique_ptr<NavButtonProvider> CreateNavButtonProvider() = 0;
#endif
+
+ // Returns a map of KeyboardEvent code to KeyboardEvent key values.
+ virtual base::flat_map<std::string, std::string> GetKeyboardLayoutMap() = 0;
};
} // namespace views
diff --git a/chromium/ui/views/mus/BUILD.gn b/chromium/ui/views/mus/BUILD.gn
index dfe239c16d5..ed64ffdeced 100644
--- a/chromium/ui/views/mus/BUILD.gn
+++ b/chromium/ui/views/mus/BUILD.gn
@@ -49,7 +49,6 @@ jumbo_component("mus") {
"//base:i18n",
"//base/third_party/dynamic_annotations",
"//cc",
- "//mojo/common",
"//mojo/public/cpp/bindings",
"//net",
"//services/catalog/public/cpp",
@@ -57,6 +56,7 @@ jumbo_component("mus") {
"//services/service_manager/public/cpp",
"//services/service_manager/public/mojom",
"//services/ui/public/cpp",
+ "//services/ui/public/cpp/input_devices",
"//services/ui/public/interfaces",
"//skia",
"//third_party/icu",
@@ -113,7 +113,6 @@ jumbo_static_library("test_support") {
sources = [
"../test/native_widget_factory_aura_mus.cc",
- "test_utils.h",
"views_mus_test_suite.cc",
"views_mus_test_suite.h",
]
diff --git a/chromium/ui/views/mus/DEPS b/chromium/ui/views/mus/DEPS
index 21ea014c764..f56df79ddca 100644
--- a/chromium/ui/views/mus/DEPS
+++ b/chromium/ui/views/mus/DEPS
@@ -1,10 +1,8 @@
include_rules = [
"+cc",
- "-cc/blink",
"+components/services/font/public",
"+components/gpu",
"+mojo/cc",
- "+mojo/common",
"+mojo/converters",
"+mojo/edk/embedder",
"+mojo/public",
diff --git a/chromium/ui/views/mus/aura_init.cc b/chromium/ui/views/mus/aura_init.cc
index ea6490d4ec3..96e0ecfc398 100644
--- a/chromium/ui/views/mus/aura_init.cc
+++ b/chromium/ui/views/mus/aura_init.cc
@@ -21,7 +21,6 @@
#include "ui/base/ui_base_paths.h"
#include "ui/views/layout/layout_provider.h"
#include "ui/views/mus/mus_client.h"
-#include "ui/views/style/typography_provider.h"
#include "ui/views/views_delegate.h"
#if defined(OS_LINUX)
@@ -93,14 +92,18 @@ bool AuraInit::Init(service_manager::Connector* connector,
scoped_refptr<base::SingleThreadTaskRunner> io_task_runner,
Mode mode,
bool register_path_provider) {
- env_ = aura::Env::CreateInstance(
- (mode == Mode::AURA_MUS || mode == Mode::AURA_MUS_WINDOW_MANAGER)
- ? aura::Env::Mode::MUS
- : aura::Env::Mode::LOCAL);
-
- if (mode == Mode::AURA_MUS) {
- mus_client_ =
- base::WrapUnique(new MusClient(connector, identity, io_task_runner));
+ env_ = aura::Env::CreateInstance(aura::Env::Mode::MUS);
+
+ if (mode == Mode::AURA_MUS || mode == Mode::AURA_MUS2) {
+ MusClient::InitParams params;
+ params.connector = connector;
+ params.identity = identity;
+ params.io_task_runner = io_task_runner;
+ params.wtc_config = mode == Mode::AURA_MUS2
+ ? aura::WindowTreeClient::Config::kMus2
+ : aura::WindowTreeClient::Config::kMash;
+ params.create_wm_state = true;
+ mus_client_ = std::make_unique<MusClient>(params);
}
// MaterialDesignController may have initialized already (such as happens
// in the utility process).
@@ -114,7 +117,7 @@ bool AuraInit::Init(service_manager::Connector* 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());
+ SkFontConfigInterface::SetGlobal(font_loader_);
// Initialize static default font, by running this now, before any other apps
// load, we ensure all the state is set up.
diff --git a/chromium/ui/views/mus/aura_init.h b/chromium/ui/views/mus/aura_init.h
index 68fa0fb3c8c..c76c8314a24 100644
--- a/chromium/ui/views/mus/aura_init.h
+++ b/chromium/ui/views/mus/aura_init.h
@@ -39,16 +39,18 @@ class ViewsDelegate;
// |resource_file| is the path to the apk file containing the resources.
class VIEWS_MUS_EXPORT AuraInit {
public:
+ // TODO(sky): remove Mode. https://crbug.com/842365.
enum class Mode {
- // Indicates AuraInit should target using aura with mus.
+ // Indicates AuraInit should target using aura with mus. This is deprecated.
AURA_MUS,
// Indicates AuraInit should target using aura with mus, for a Window
- // Manager client.
+ // Manager client. This is deprecated.
AURA_MUS_WINDOW_MANAGER,
- // Indicates AuraInit should target using ui::Window.
- UI
+ // Targets ws2. Mode will eventually be removed entirely and this will be
+ // the default.
+ AURA_MUS2,
};
~AuraInit();
@@ -65,7 +67,7 @@ class VIEWS_MUS_EXPORT AuraInit {
const std::string& resource_file,
const std::string& resource_file_200 = std::string(),
scoped_refptr<base::SingleThreadTaskRunner> io_task_runner = nullptr,
- Mode mode = Mode::UI,
+ Mode mode = Mode::AURA_MUS,
bool register_path_provider = true);
// Only valid if Mode::AURA_MUS was passed to constructor.
diff --git a/chromium/ui/views/mus/clipboard_mus.h b/chromium/ui/views/mus/clipboard_mus.h
index 1a5fff8c63c..54cbe4abea7 100644
--- a/chromium/ui/views/mus/clipboard_mus.h
+++ b/chromium/ui/views/mus/clipboard_mus.h
@@ -5,6 +5,7 @@
#ifndef UI_VIEWS_MUS_CLIPBOARD_MUS_H_
#define UI_VIEWS_MUS_CLIPBOARD_MUS_H_
+#include "base/containers/flat_map.h"
#include "services/ui/public/interfaces/clipboard.mojom.h"
#include "ui/base/clipboard/clipboard.h"
#include "ui/views/mus/mus_export.h"
@@ -77,7 +78,7 @@ class VIEWS_MUS_EXPORT ClipboardMus : public ui::Clipboard {
// WriteObjects(), which then calls our base class DispatchObject() which
// then calls into each data type specific Write() function. Once we've
// collected all the data types, we then pass this to the mus server.
- base::Optional<std::unordered_map<std::string, std::vector<uint8_t>>>
+ base::Optional<base::flat_map<std::string, std::vector<uint8_t>>>
current_clipboard_;
DISALLOW_COPY_AND_ASSIGN(ClipboardMus);
diff --git a/chromium/ui/views/mus/clipboard_unittest.cc b/chromium/ui/views/mus/clipboard_unittest.cc
index 9db8fe50732..b04292e94b7 100644
--- a/chromium/ui/views/mus/clipboard_unittest.cc
+++ b/chromium/ui/views/mus/clipboard_unittest.cc
@@ -4,7 +4,6 @@
#include "ui/views/mus/clipboard_mus.h"
-#include "base/message_loop/message_loop.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "ui/events/platform/platform_event_source.h"
#include "ui/views/mus/mus_client.h"
diff --git a/chromium/ui/views/mus/desktop_window_tree_host_mus.cc b/chromium/ui/views/mus/desktop_window_tree_host_mus.cc
index c417e738573..0233bc336cd 100644
--- a/chromium/ui/views/mus/desktop_window_tree_host_mus.cc
+++ b/chromium/ui/views/mus/desktop_window_tree_host_mus.cc
@@ -280,7 +280,8 @@ float DesktopWindowTreeHostMus::GetScaleFactor() const {
}
void DesktopWindowTreeHostMus::SetBoundsInDIP(const gfx::Rect& bounds_in_dip) {
- SetBoundsInPixels(gfx::ConvertRectToPixel(GetScaleFactor(), bounds_in_dip));
+ SetBoundsInPixels(gfx::ConvertRectToPixel(GetScaleFactor(), bounds_in_dip),
+ viz::LocalSurfaceId());
}
bool DesktopWindowTreeHostMus::ShouldSendClientAreaToServer() const {
@@ -512,7 +513,11 @@ void DesktopWindowTreeHostMus::GetWindowPlacement(
}
gfx::Rect DesktopWindowTreeHostMus::GetWindowBoundsInScreen() const {
- return gfx::ConvertRectToDIP(GetScaleFactor(), GetBoundsInPixels());
+ gfx::Point display_origin = GetDisplay().bounds().origin();
+ gfx::Rect bounds_in_dip =
+ gfx::ConvertRectToDIP(GetScaleFactor(), GetBoundsInPixels());
+ bounds_in_dip.Offset(display_origin.x(), display_origin.y());
+ return bounds_in_dip;
}
gfx::Rect DesktopWindowTreeHostMus::GetClientAreaBoundsInScreen() const {
@@ -697,7 +702,9 @@ bool DesktopWindowTreeHostMus::ShouldWindowContentsBeTransparent() const {
return false;
}
-void DesktopWindowTreeHostMus::FrameTypeChanged() {}
+void DesktopWindowTreeHostMus::FrameTypeChanged() {
+ native_widget_delegate_->AsWidget()->ThemeChanged();
+}
void DesktopWindowTreeHostMus::SetFullscreen(bool fullscreen) {
if (IsFullscreen() == fullscreen)
@@ -824,7 +831,8 @@ void DesktopWindowTreeHostMus::HideImpl() {
}
void DesktopWindowTreeHostMus::SetBoundsInPixels(
- const gfx::Rect& bounds_in_pixels) {
+ const gfx::Rect& bounds_in_pixels,
+ const viz::LocalSurfaceId& local_surface_id) {
gfx::Rect final_bounds_in_pixels = bounds_in_pixels;
if (GetBoundsInPixels().size() != bounds_in_pixels.size()) {
gfx::Size size = bounds_in_pixels.size();
@@ -837,7 +845,8 @@ void DesktopWindowTreeHostMus::SetBoundsInPixels(
final_bounds_in_pixels.set_size(size);
}
const gfx::Rect old_bounds_in_pixels = GetBoundsInPixels();
- WindowTreeHostMus::SetBoundsInPixels(final_bounds_in_pixels);
+ WindowTreeHostMus::SetBoundsInPixels(final_bounds_in_pixels,
+ local_surface_id);
if (old_bounds_in_pixels.size() != final_bounds_in_pixels.size()) {
SendClientAreaToServer();
SendHitTestMaskToServer();
diff --git a/chromium/ui/views/mus/desktop_window_tree_host_mus.h b/chromium/ui/views/mus/desktop_window_tree_host_mus.h
index e1dca1c6735..def36405a77 100644
--- a/chromium/ui/views/mus/desktop_window_tree_host_mus.h
+++ b/chromium/ui/views/mus/desktop_window_tree_host_mus.h
@@ -140,7 +140,8 @@ class VIEWS_MUS_EXPORT DesktopWindowTreeHostMus
// aura::WindowTreeHostMus:
void ShowImpl() override;
void HideImpl() override;
- void SetBoundsInPixels(const gfx::Rect& bounds_in_pixels) override;
+ void SetBoundsInPixels(const gfx::Rect& bounds_in_pixels,
+ const viz::LocalSurfaceId& local_surface_id) override;
// Accessor for DesktopNativeWidgetAura::content_window().
aura::Window* content_window();
diff --git a/chromium/ui/views/mus/desktop_window_tree_host_mus_unittest.cc b/chromium/ui/views/mus/desktop_window_tree_host_mus_unittest.cc
index 50851a87bfa..769904d73e1 100644
--- a/chromium/ui/views/mus/desktop_window_tree_host_mus_unittest.cc
+++ b/chromium/ui/views/mus/desktop_window_tree_host_mus_unittest.cc
@@ -7,6 +7,7 @@
#include "base/debug/stack_trace.h"
#include "base/run_loop.h"
+#include "ui/aura/client/aura_constants.h"
#include "ui/aura/client/cursor_client.h"
#include "ui/aura/client/focus_client.h"
#include "ui/aura/client/transient_window_client.h"
@@ -22,6 +23,7 @@
#include "ui/events/base_event_utils.h"
#include "ui/events/event.h"
#include "ui/views/mus/mus_client.h"
+#include "ui/views/mus/screen_mus.h"
#include "ui/views/test/views_test_base.h"
#include "ui/views/widget/widget.h"
#include "ui/views/widget/widget_delegate.h"
@@ -366,4 +368,32 @@ TEST_F(DesktopWindowTreeHostMusTest, CreateFullscreenWidget) {
}
}
+TEST_F(DesktopWindowTreeHostMusTest, GetWindowBoundsInScreen) {
+ ScreenMus* screen = MusClient::Get()->screen();
+
+ // Add a second display to the right of the primary.
+ const int64_t kSecondDisplayId = 222;
+ screen->display_list().AddDisplay(
+ display::Display(kSecondDisplayId, gfx::Rect(800, 0, 640, 480)),
+ display::DisplayList::Type::NOT_PRIMARY);
+
+ // Verify bounds for a widget on the first display.
+ Widget widget1;
+ Widget::InitParams params1(Widget::InitParams::TYPE_WINDOW);
+ params1.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
+ params1.bounds = gfx::Rect(0, 0, 100, 100);
+ widget1.Init(params1);
+ EXPECT_EQ(gfx::Rect(0, 0, 100, 100), widget1.GetWindowBoundsInScreen());
+
+ // Verify bounds for a widget on the secondary display.
+ Widget widget2;
+ Widget::InitParams params2(Widget::InitParams::TYPE_WINDOW);
+ params2.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
+ params2.bounds = gfx::Rect(0, 0, 100, 100);
+ widget2.Init(params2);
+ aura::WindowTreeHostMus::ForWindow(widget2.GetNativeWindow())
+ ->set_display_id(kSecondDisplayId);
+ EXPECT_EQ(gfx::Rect(800, 0, 100, 100), widget2.GetWindowBoundsInScreen());
+}
+
} // namespace views
diff --git a/chromium/ui/views/mus/mus_client.cc b/chromium/ui/views/mus/mus_client.cc
index 1e049056dfb..b8d4dc77bfd 100644
--- a/chromium/ui/views/mus/mus_client.cc
+++ b/chromium/ui/views/mus/mus_client.cc
@@ -11,6 +11,7 @@
#include "base/threading/thread.h"
#include "services/service_manager/public/cpp/connector.h"
#include "services/ui/public/cpp/gpu/gpu.h"
+#include "services/ui/public/cpp/input_devices/input_device_client.h"
#include "services/ui/public/cpp/property_type_converters.h"
#include "services/ui/public/interfaces/constants.mojom.h"
#include "services/ui/public/interfaces/event_matcher.mojom.h"
@@ -19,7 +20,6 @@
#include "ui/aura/mus/capture_synchronizer.h"
#include "ui/aura/mus/mus_context_factory.h"
#include "ui/aura/mus/property_converter.h"
-#include "ui/aura/mus/window_tree_client.h"
#include "ui/aura/mus/window_tree_host_mus.h"
#include "ui/aura/mus/window_tree_host_mus_init_params.h"
#include "ui/aura/window.h"
@@ -64,12 +64,11 @@ namespace views {
// static
MusClient* MusClient::instance_ = nullptr;
-MusClient::MusClient(service_manager::Connector* connector,
- const service_manager::Identity& identity,
- scoped_refptr<base::SingleThreadTaskRunner> io_task_runner,
- bool create_wm_state,
- MusClientTestingState testing_state)
- : identity_(identity) {
+MusClient::InitParams::InitParams() = default;
+
+MusClient::InitParams::~InitParams() = default;
+
+MusClient::MusClient(const InitParams& params) : identity_(params.identity) {
DCHECK(!instance_);
DCHECK(aura::Env::GetInstance());
instance_ = this;
@@ -81,9 +80,12 @@ MusClient::MusClient(service_manager::Connector* connector,
// instance. Partially initialize the ozone cursor internals here, like we
// partially initialize other ozone subsystems in
// ChromeBrowserMainExtraPartsViews.
- cursor_factory_ozone_ = std::make_unique<ui::CursorDataFactoryOzone>();
+ if (params.create_cursor_factory)
+ cursor_factory_ozone_ = std::make_unique<ui::CursorDataFactoryOzone>();
#endif
+ scoped_refptr<base::SingleThreadTaskRunner> io_task_runner =
+ params.io_task_runner;
if (!io_task_runner) {
io_thread_ = std::make_unique<base::Thread>("IOThread");
base::Thread::Options thread_options(base::MessageLoop::TYPE_IO, 0);
@@ -92,34 +94,49 @@ MusClient::MusClient(service_manager::Connector* connector,
io_task_runner = io_thread_->task_runner();
}
- // TODO(msw): Avoid this... use some default value? Allow clients to extend?
property_converter_ = std::make_unique<aura::PropertyConverter>();
property_converter_->RegisterPrimitiveProperty(
::wm::kShadowElevationKey,
ui::mojom::WindowManager::kShadowElevation_Property,
aura::PropertyConverter::CreateAcceptAnyValueCallback());
- if (create_wm_state)
+ if (params.create_wm_state)
wm_state_ = std::make_unique<wm::WMState>();
- if (testing_state == MusClientTestingState::CREATE_TESTING_STATE) {
+ service_manager::Connector* connector = params.connector;
+ if (params.bind_test_ws_interfaces) {
connector->BindInterface(ui::mojom::kServiceName, &server_test_ptr_);
connector->BindInterface(ui::mojom::kServiceName, &event_injector_);
}
- window_tree_client_ = aura::WindowTreeClient::CreateForWindowTreeFactory(
- connector, this, true, std::move(io_task_runner));
- aura::Env::GetInstance()->SetWindowTreeClient(window_tree_client_.get());
+ if (!params.window_tree_client) {
+ DCHECK(io_task_runner);
+ owned_window_tree_client_ =
+ aura::WindowTreeClient::CreateForWindowTreeFactory(
+ connector, this, true, std::move(io_task_runner),
+ params.wtc_config);
+ window_tree_client_ = owned_window_tree_client_.get();
+ aura::Env::GetInstance()->SetWindowTreeClient(window_tree_client_);
+ } else {
+ window_tree_client_ = params.window_tree_client;
+ }
pointer_watcher_event_router_ =
- std::make_unique<PointerWatcherEventRouter>(window_tree_client_.get());
+ std::make_unique<PointerWatcherEventRouter>(window_tree_client_);
+
+ if (connector) {
+ input_device_client_ = std::make_unique<ui::InputDeviceClient>();
+ ui::mojom::InputDeviceServerPtr input_device_server;
+ connector->BindInterface(ui::mojom::kServiceName, &input_device_server);
+ input_device_client_->Connect(std::move(input_device_server));
- screen_ = std::make_unique<ScreenMus>(this);
- screen_->Init(connector);
+ screen_ = std::make_unique<ScreenMus>(this);
+ screen_->Init(connector);
- std::unique_ptr<ClipboardMus> clipboard = std::make_unique<ClipboardMus>();
- clipboard->Init(connector);
- ui::Clipboard::SetClipboardForCurrentThread(std::move(clipboard));
+ std::unique_ptr<ClipboardMus> clipboard = std::make_unique<ClipboardMus>();
+ clipboard->Init(connector);
+ ui::Clipboard::SetClipboardForCurrentThread(std::move(clipboard));
+ }
ViewsDelegate::GetInstance()->set_native_widget_factory(
base::Bind(&MusClient::CreateNativeWidget, base::Unretained(this)));
@@ -130,7 +147,8 @@ MusClient::MusClient(service_manager::Connector* connector,
MusClient::~MusClient() {
// ~WindowTreeClient calls back to us (we're its delegate), destroy it while
// we are still valid.
- window_tree_client_.reset();
+ owned_window_tree_client_.reset();
+ window_tree_client_ = nullptr;
ui::OSExchangeDataProviderFactory::SetFactory(nullptr);
ui::Clipboard::DestroyClipboardForCurrentThread();
@@ -321,8 +339,10 @@ void MusClient::OnEmbedRootDestroyed(
}
void MusClient::OnPointerEventObserved(const ui::PointerEvent& event,
+ int64_t display_id,
aura::Window* target) {
- pointer_watcher_event_router_->OnPointerEventObserved(event, target);
+ pointer_watcher_event_router_->OnPointerEventObserved(event, display_id,
+ target);
}
void MusClient::OnWindowManagerFrameValuesChanged() {
diff --git a/chromium/ui/views/mus/mus_client.h b/chromium/ui/views/mus/mus_client.h
index 0543e03247a..60239f46548 100644
--- a/chromium/ui/views/mus/mus_client.h
+++ b/chromium/ui/views/mus/mus_client.h
@@ -16,6 +16,7 @@
#include "services/ui/public/interfaces/event_injector.mojom.h"
#include "services/ui/public/interfaces/window_server_test.mojom.h"
#include "ui/aura/client/capture_client.h"
+#include "ui/aura/mus/window_tree_client.h"
#include "ui/aura/mus/window_tree_client_delegate.h"
#include "ui/views/mus/mus_export.h"
#include "ui/views/mus/screen_mus_delegate.h"
@@ -38,6 +39,7 @@ class Connector;
namespace ui {
class CursorDataFactoryOzone;
+class InputDeviceClient;
}
namespace wm {
@@ -56,26 +58,41 @@ namespace internal {
class NativeWidgetDelegate;
}
-namespace test {
-class MusClientTestApi;
-}
-
-enum MusClientTestingState { NO_TESTING, CREATE_TESTING_STATE };
-
// MusClient establishes a connection to mus and sets up necessary state so that
// aura and views target mus. This class is useful for typical clients, not the
// WindowManager. Most clients don't create this directly, rather use AuraInit.
class VIEWS_MUS_EXPORT MusClient : public aura::WindowTreeClientDelegate,
public ScreenMusDelegate {
public:
+ struct VIEWS_MUS_EXPORT InitParams {
+ InitParams();
+ ~InitParams();
+
+ // Production code should provide |connector|, |identity|, |wtc_config|
+ // and an |io_task_runner| if the process already has one. Test code may
+ // skip these parameters (e.g. a unit test that does not need to connect
+ // to the window service does not need to provide a connector).
+ service_manager::Connector* connector = nullptr;
+ service_manager::Identity identity;
+ scoped_refptr<base::SingleThreadTaskRunner> io_task_runner = nullptr;
+ aura::WindowTreeClient::Config wtc_config =
+ aura::WindowTreeClient::Config::kMash;
+
+ // Create a wm::WMState. Some processes (e.g. the browser) may already
+ // have one.
+ bool create_wm_state = true;
+
+ // Tests may need to control objects owned by MusClient.
+ bool create_cursor_factory = true;
+ bool bind_test_ws_interfaces = false;
+
+ // If provided, MusClient will not create the WindowTreeClient. Not owned.
+ // Must outlive MusClient.
+ aura::WindowTreeClient* window_tree_client = nullptr;
+ };
+
// Most clients should use AuraInit, which creates a MusClient.
- // |create_wm_state| indicates whether MusClient should create a wm::WMState.
- MusClient(
- service_manager::Connector* connector,
- const service_manager::Identity& identity,
- scoped_refptr<base::SingleThreadTaskRunner> io_task_runner = nullptr,
- bool create_wm_state = true,
- MusClientTestingState testing_state = MusClientTestingState::NO_TESTING);
+ explicit MusClient(const InitParams& params);
~MusClient() override;
static MusClient* Get() { return instance_; }
@@ -91,14 +108,15 @@ class VIEWS_MUS_EXPORT MusClient : public aura::WindowTreeClientDelegate,
static std::map<std::string, std::vector<uint8_t>>
ConfigurePropertiesFromParams(const Widget::InitParams& init_params);
- aura::WindowTreeClient* window_tree_client() {
- return window_tree_client_.get();
- }
+ aura::WindowTreeClient* window_tree_client() { return window_tree_client_; }
PointerWatcherEventRouter* pointer_watcher_event_router() {
return pointer_watcher_event_router_.get();
}
+ // Getter for type safety. Most code can use display::Screen::GetScreen().
+ ScreenMus* screen() { return screen_.get(); }
+
// Creates DesktopNativeWidgetAura with DesktopWindowTreeHostMus. This is
// set as the factory function used for creating NativeWidgets when a
// NativeWidget has not been explicitly set.
@@ -133,7 +151,6 @@ class VIEWS_MUS_EXPORT MusClient : public aura::WindowTreeClientDelegate,
private:
friend class AuraInit;
- friend class test::MusClientTestApi;
// Creates a DesktopWindowTreeHostMus. This is set as the factory function
// ViewsDelegate such that if DesktopNativeWidgetAura is created without a
@@ -149,6 +166,7 @@ class VIEWS_MUS_EXPORT MusClient : public aura::WindowTreeClientDelegate,
void OnLostConnection(aura::WindowTreeClient* client) override;
void OnEmbedRootDestroyed(aura::WindowTreeHostMus* window_tree_host) override;
void OnPointerEventObserved(const ui::PointerEvent& event,
+ int64_t display_id,
aura::Window* target) override;
aura::PropertyConverter* GetPropertyConverter() override;
@@ -177,10 +195,17 @@ class VIEWS_MUS_EXPORT MusClient : public aura::WindowTreeClientDelegate,
std::unique_ptr<aura::PropertyConverter> property_converter_;
std::unique_ptr<MusPropertyMirror> mus_property_mirror_;
- std::unique_ptr<aura::WindowTreeClient> window_tree_client_;
+ // Non-null if MusClient created the WindowTreeClient.
+ std::unique_ptr<aura::WindowTreeClient> owned_window_tree_client_;
+
+ // Never null.
+ aura::WindowTreeClient* window_tree_client_;
std::unique_ptr<PointerWatcherEventRouter> pointer_watcher_event_router_;
+ // Gives services transparent remote access the InputDeviceManager.
+ std::unique_ptr<ui::InputDeviceClient> input_device_client_;
+
ui::mojom::WindowServerTestPtr server_test_ptr_;
ui::mojom::EventInjectorPtr event_injector_;
diff --git a/chromium/ui/views/mus/pointer_watcher_event_router.cc b/chromium/ui/views/mus/pointer_watcher_event_router.cc
index 82bf200b766..beed6b538c9 100644
--- a/chromium/ui/views/mus/pointer_watcher_event_router.cc
+++ b/chromium/ui/views/mus/pointer_watcher_event_router.cc
@@ -7,12 +7,16 @@
#include "ui/aura/client/capture_client.h"
#include "ui/aura/mus/window_tree_client.h"
#include "ui/aura/window.h"
+#include "ui/display/display.h"
#include "ui/display/screen.h"
#include "ui/events/base_event_utils.h"
#include "ui/events/event.h"
#include "ui/views/pointer_watcher.h"
#include "ui/views/widget/desktop_aura/desktop_native_widget_aura.h"
+using display::Display;
+using display::Screen;
+
namespace views {
namespace {
@@ -85,6 +89,7 @@ void PointerWatcherEventRouter::RemovePointerWatcher(PointerWatcher* watcher) {
void PointerWatcherEventRouter::OnPointerEventObserved(
const ui::PointerEvent& event,
+ int64_t display_id,
aura::Window* target) {
Widget* target_widget = nullptr;
ui::PointerEvent updated_event(event);
@@ -113,10 +118,13 @@ void PointerWatcherEventRouter::OnPointerEventObserved(
}
}
- // 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.root_location();
+ // Compute screen coordinates via |display_id| because there may not be a
+ // |target| that can be used to find a ScreenPositionClient.
+ gfx::Point location_in_screen = event.location();
+ Display display;
+ if (Screen::GetScreen()->GetDisplayWithDisplayId(display_id, &display))
+ location_in_screen.Offset(display.bounds().x(), display.bounds().y());
+
for (PointerWatcher& observer : move_watchers_) {
observer.OnPointerEventObserved(
updated_event, location_in_screen,
@@ -157,8 +165,7 @@ void PointerWatcherEventRouter::OnCaptureChanged(aura::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();
+ gfx::Point location_in_screen = Screen::GetScreen()->GetCursorScreenPoint();
for (PointerWatcher& observer : move_watchers_)
observer.OnPointerEventObserved(event, location_in_screen, nullptr);
for (PointerWatcher& observer : non_move_watchers_)
diff --git a/chromium/ui/views/mus/pointer_watcher_event_router.h b/chromium/ui/views/mus/pointer_watcher_event_router.h
index 2487fbc8e54..2c9dd56e081 100644
--- a/chromium/ui/views/mus/pointer_watcher_event_router.h
+++ b/chromium/ui/views/mus/pointer_watcher_event_router.h
@@ -5,7 +5,8 @@
#ifndef UI_VIEWS_MUS_POINTER_WATCHER_EVENT_ROUTER_H_
#define UI_VIEWS_MUS_POINTER_WATCHER_EVENT_ROUTER_H_
-#include "base/compiler_specific.h"
+#include <stdint.h>
+
#include "base/macros.h"
#include "base/observer_list.h"
#include "ui/aura/client/capture_client_observer.h"
@@ -58,6 +59,7 @@ class VIEWS_MUS_EXPORT PointerWatcherEventRouter
// Called by WindowTreeClientDelegate to notify PointerWatchers appropriately.
void OnPointerEventObserved(const ui::PointerEvent& event,
+ int64_t display_id,
aura::Window* target);
// Called when the |capture_client| has been set or will be unset.
diff --git a/chromium/ui/views/mus/pointer_watcher_event_router_unittest.cc b/chromium/ui/views/mus/pointer_watcher_event_router_unittest.cc
index 7634fff74bc..821cefdcf38 100644
--- a/chromium/ui/views/mus/pointer_watcher_event_router_unittest.cc
+++ b/chromium/ui/views/mus/pointer_watcher_event_router_unittest.cc
@@ -11,6 +11,7 @@
#include "ui/aura/test/mus/window_tree_client_private.h"
#include "ui/events/event.h"
#include "ui/views/mus/mus_client.h"
+#include "ui/views/mus/screen_mus.h"
#include "ui/views/pointer_watcher.h"
#include "ui/views/test/scoped_views_test_helper.h"
@@ -23,18 +24,24 @@ class TestPointerWatcher : public PointerWatcher {
~TestPointerWatcher() override {}
ui::PointerEvent* last_event_observed() { return last_event_observed_.get(); }
+ gfx::Point last_location_in_screen() { return last_location_in_screen_; }
- void Reset() { last_event_observed_.reset(); }
+ void Reset() {
+ last_event_observed_.reset();
+ last_location_in_screen_ = gfx::Point();
+ }
// PointerWatcher:
void OnPointerEventObserved(const ui::PointerEvent& event,
const gfx::Point& location_in_screen,
gfx::NativeView target) override {
last_event_observed_ = std::make_unique<ui::PointerEvent>(event);
+ last_location_in_screen_ = location_in_screen;
}
private:
std::unique_ptr<ui::PointerEvent> last_event_observed_;
+ gfx::Point last_location_in_screen_;
DISALLOW_COPY_AND_ASSIGN(TestPointerWatcher);
};
@@ -43,12 +50,13 @@ class TestPointerWatcher : public PointerWatcher {
class PointerWatcherEventRouterTest : public testing::Test {
public:
- PointerWatcherEventRouterTest() {}
- ~PointerWatcherEventRouterTest() override {}
+ PointerWatcherEventRouterTest() = default;
+ ~PointerWatcherEventRouterTest() override = default;
- void OnPointerEventObserved(const ui::PointerEvent& event) {
+ void OnPointerEventObserved(const ui::PointerEvent& event,
+ int64_t display_id = 0) {
MusClient::Get()->pointer_watcher_event_router()->OnPointerEventObserved(
- event, nullptr);
+ event, display_id, nullptr);
}
PointerWatcherEventRouter::EventTypes event_types() const {
@@ -56,13 +64,14 @@ class PointerWatcherEventRouterTest : public testing::Test {
}
private:
+ base::test::ScopedTaskEnvironment scoped_task_environment_{
+ base::test::ScopedTaskEnvironment::MainThreadType::UI};
+ ScopedViewsTestHelper helper_;
+
DISALLOW_COPY_AND_ASSIGN(PointerWatcherEventRouterTest);
};
TEST_F(PointerWatcherEventRouterTest, EventTypes) {
- base::test::ScopedTaskEnvironment scoped_task_environment(
- base::test::ScopedTaskEnvironment::MainThreadType::UI);
- ScopedViewsTestHelper helper;
TestPointerWatcher pointer_watcher1, pointer_watcher2;
PointerWatcherEventRouter* pointer_watcher_event_router =
MusClient::Get()->pointer_watcher_event_router();
@@ -114,9 +123,6 @@ TEST_F(PointerWatcherEventRouterTest, EventTypes) {
}
TEST_F(PointerWatcherEventRouterTest, PointerWatcherNoMove) {
- base::test::ScopedTaskEnvironment scoped_task_environment(
- base::test::ScopedTaskEnvironment::MainThreadType::UI);
- ScopedViewsTestHelper helper;
ASSERT_TRUE(MusClient::Get());
PointerWatcherEventRouter* pointer_watcher_event_router =
MusClient::Get()->pointer_watcher_event_router();
@@ -188,9 +194,6 @@ TEST_F(PointerWatcherEventRouterTest, PointerWatcherNoMove) {
}
TEST_F(PointerWatcherEventRouterTest, PointerWatcherMove) {
- base::test::ScopedTaskEnvironment scoped_task_environment(
- base::test::ScopedTaskEnvironment::MainThreadType::UI);
- ScopedViewsTestHelper helper;
ASSERT_TRUE(MusClient::Get());
PointerWatcherEventRouter* pointer_watcher_event_router =
MusClient::Get()->pointer_watcher_event_router();
@@ -241,4 +244,36 @@ TEST_F(PointerWatcherEventRouterTest, PointerWatcherMove) {
EXPECT_FALSE(watcher2.last_event_observed());
}
+TEST_F(PointerWatcherEventRouterTest, SecondaryDisplay) {
+ PointerWatcherEventRouter* pointer_watcher_event_router =
+ MusClient::Get()->pointer_watcher_event_router();
+ TestPointerWatcher watcher;
+ pointer_watcher_event_router->AddPointerWatcher(&watcher, false);
+
+ ScreenMus* screen = MusClient::Get()->screen();
+ const uint64_t kFirstDisplayId = screen->GetPrimaryDisplay().id();
+
+ // The first display is at 0,0.
+ ASSERT_TRUE(screen->GetPrimaryDisplay().bounds().origin().IsOrigin());
+
+ // Add a secondary display to the right of the primary.
+ const uint64_t kSecondDisplayId = 222;
+ screen->display_list().AddDisplay(
+ display::Display(kSecondDisplayId, gfx::Rect(800, 0, 640, 480)),
+ display::DisplayList::Type::NOT_PRIMARY);
+
+ ui::PointerEvent tap_event(
+ ui::ET_POINTER_DOWN, gfx::Point(1, 1), gfx::Point(1, 1), ui::EF_NONE, 0,
+ ui::PointerDetails(ui::EventPointerType::POINTER_TYPE_TOUCH, 1),
+ base::TimeTicks());
+
+ OnPointerEventObserved(tap_event, kFirstDisplayId);
+ EXPECT_EQ(gfx::Point(1, 1), watcher.last_location_in_screen());
+
+ OnPointerEventObserved(tap_event, kSecondDisplayId);
+ EXPECT_EQ(gfx::Point(801, 1), watcher.last_location_in_screen());
+
+ pointer_watcher_event_router->RemovePointerWatcher(&watcher);
+}
+
} // namespace views
diff --git a/chromium/ui/views/mus/screen_mus.cc b/chromium/ui/views/mus/screen_mus.cc
index 36f721bda31..8c8dd07f7e9 100644
--- a/chromium/ui/views/mus/screen_mus.cc
+++ b/chromium/ui/views/mus/screen_mus.cc
@@ -37,7 +37,7 @@ namespace views {
using Type = display::DisplayList::Type;
ScreenMus::ScreenMus(ScreenMusDelegate* delegate)
- : delegate_(delegate), display_manager_observer_binding_(this) {
+ : delegate_(delegate), screen_provider_observer_binding_(this) {
DCHECK(delegate);
display::Screen::SetScreenInstance(this);
}
@@ -48,23 +48,23 @@ ScreenMus::~ScreenMus() {
}
void ScreenMus::Init(service_manager::Connector* connector) {
- connector->BindInterface(ui::mojom::kServiceName, &display_manager_);
+ connector->BindInterface(ui::mojom::kServiceName, &screen_provider_);
- ui::mojom::DisplayManagerObserverPtr observer;
- display_manager_observer_binding_.Bind(mojo::MakeRequest(&observer));
- display_manager_->AddObserver(std::move(observer));
+ ui::mojom::ScreenProviderObserverPtr observer;
+ screen_provider_observer_binding_.Bind(mojo::MakeRequest(&observer));
+ screen_provider_->AddObserver(std::move(observer));
// We need the set of displays before we can continue. Wait for it.
//
// TODO(rockot): Do something better here. This should not have to block tasks
// from running on the calling thread. http://crbug.com/594852.
- bool success = display_manager_observer_binding_.WaitForIncomingMethodCall();
+ bool success = screen_provider_observer_binding_.WaitForIncomingMethodCall();
// 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()) {
- DCHECK(display_manager_.encountered_error() || !success);
+ DCHECK(screen_provider_.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(
diff --git a/chromium/ui/views/mus/screen_mus.h b/chromium/ui/views/mus/screen_mus.h
index 4987b07edaa..42c4d150573 100644
--- a/chromium/ui/views/mus/screen_mus.h
+++ b/chromium/ui/views/mus/screen_mus.h
@@ -6,7 +6,7 @@
#define UI_VIEWS_MUS_SCREEN_MUS_H_
#include "mojo/public/cpp/bindings/binding.h"
-#include "services/ui/public/interfaces/display_manager.mojom.h"
+#include "services/ui/public/interfaces/screen_provider.mojom.h"
#include "ui/display/screen_base.h"
#include "ui/views/mus/mus_export.h"
@@ -18,9 +18,9 @@ namespace views {
class ScreenMusDelegate;
-// Screen implementation backed by ui::mojom::DisplayManager.
+// Screen implementation backed by ui::mojom::ScreenProvider.
class VIEWS_MUS_EXPORT ScreenMus : public display::ScreenBase,
- public ui::mojom::DisplayManagerObserver {
+ public ui::mojom::ScreenProviderObserver {
public:
explicit ScreenMus(ScreenMusDelegate* delegate);
~ScreenMus() override;
@@ -37,15 +37,15 @@ class VIEWS_MUS_EXPORT ScreenMus : public display::ScreenBase,
bool IsWindowUnderCursor(gfx::NativeWindow window) override;
aura::Window* GetWindowAtScreenPoint(const gfx::Point& point) override;
- // ui::mojom::DisplayManager:
+ // ui::mojom::ScreenProvider:
void OnDisplaysChanged(std::vector<ui::mojom::WsDisplayPtr> ws_displays,
int64_t primary_display_id,
int64_t internal_display_id) override;
ScreenMusDelegate* delegate_;
- ui::mojom::DisplayManagerPtr display_manager_;
- mojo::Binding<ui::mojom::DisplayManagerObserver>
- display_manager_observer_binding_;
+ ui::mojom::ScreenProviderPtr screen_provider_;
+ mojo::Binding<ui::mojom::ScreenProviderObserver>
+ screen_provider_observer_binding_;
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 0f36fd59da1..0fe8f7118f2 100644
--- a/chromium/ui/views/mus/screen_mus_delegate.h
+++ b/chromium/ui/views/mus/screen_mus_delegate.h
@@ -17,7 +17,7 @@ class Point;
namespace views {
-// Screen implementation backed by ui::mojom::DisplayManager.
+// Delegate for screen implementation backed by ui::mojom::ScreenProvider.
class VIEWS_MUS_EXPORT ScreenMusDelegate {
public:
virtual void OnWindowManagerFrameValuesChanged() = 0;
diff --git a/chromium/ui/views/mus/test_utils.h b/chromium/ui/views/mus/test_utils.h
deleted file mode 100644
index da72dcda000..00000000000
--- a/chromium/ui/views/mus/test_utils.h
+++ /dev/null
@@ -1,31 +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_TEST_UTILS_H_
-#define UI_VIEWS_MUS_TEST_UTILS_H_
-
-#include "base/memory/ptr_util.h"
-#include "ui/views/mus/mus_client.h"
-
-namespace views {
-namespace test {
-
-class MusClientTestApi {
- public:
- static std::unique_ptr<MusClient> Create(
- service_manager::Connector* connector,
- const service_manager::Identity& identity) {
- return base::WrapUnique(
- new MusClient(connector, identity, nullptr, true,
- MusClientTestingState::CREATE_TESTING_STATE));
- }
-
- private:
- DISALLOW_IMPLICIT_CONSTRUCTORS(MusClientTestApi);
-};
-
-} // namespace test
-} // namespace views
-
-#endif // UI_VIEWS_MUS_TEST_UTILS_H_
diff --git a/chromium/ui/views/mus/views_mus_test_suite.cc b/chromium/ui/views/mus/views_mus_test_suite.cc
index 3b2cfcd1c39..304d7625316 100644
--- a/chromium/ui/views/mus/views_mus_test_suite.cc
+++ b/chromium/ui/views/mus/views_mus_test_suite.cc
@@ -34,7 +34,6 @@
#include "ui/gl/gl_switches.h"
#include "ui/views/mus/desktop_window_tree_host_mus.h"
#include "ui/views/mus/mus_client.h"
-#include "ui/views/mus/test_utils.h"
#include "ui/views/test/platform_test_helper.h"
#include "ui/views/test/views_test_helper_aura.h"
#include "ui/views/views_delegate.h"
@@ -101,8 +100,11 @@ class ServiceManagerConnection {
}
std::unique_ptr<MusClient> CreateMusClient() {
- return test::MusClientTestApi::Create(GetConnector(),
- service_manager_identity_);
+ MusClient::InitParams params;
+ params.connector = GetConnector();
+ params.identity = service_manager_identity_;
+ params.bind_test_ws_interfaces = true;
+ return std::make_unique<MusClient>(params);
}
private:
diff --git a/chromium/ui/views/style/platform_style.cc b/chromium/ui/views/style/platform_style.cc
index 99793bc88bd..ef68fb58bbb 100644
--- a/chromium/ui/views/style/platform_style.cc
+++ b/chromium/ui/views/style/platform_style.cc
@@ -57,6 +57,10 @@ const bool PlatformStyle::kUseRipples = true;
const bool PlatformStyle::kTextfieldScrollsToStartOnFocusChange = false;
const bool PlatformStyle::kTextfieldUsesDragCursorWhenDraggable = true;
const bool PlatformStyle::kShouldElideBookmarksInBookmarksBar = false;
+const float PlatformStyle::kFocusHaloThickness = 2.f;
+const float PlatformStyle::kFocusHaloInset =
+ -PlatformStyle::kFocusHaloThickness;
+const bool PlatformStyle::kPreferFocusRings = false;
// static
std::unique_ptr<ScrollBar> PlatformStyle::CreateScrollBar(bool is_horizontal) {
diff --git a/chromium/ui/views/style/platform_style.h b/chromium/ui/views/style/platform_style.h
index 25710b7968e..b8e74bedb0b 100644
--- a/chromium/ui/views/style/platform_style.h
+++ b/chromium/ui/views/style/platform_style.h
@@ -74,6 +74,15 @@ class VIEWS_EXPORT PlatformStyle {
// tail] or fade out.
static const bool kShouldElideBookmarksInBookmarksBar;
+ // The thickness and inset amount of focus ring halos.
+ static const float kFocusHaloThickness;
+ static const float kFocusHaloInset;
+
+ // Whether "button-like" (for example, buttons in the top chrome or Omnibox
+ // decorations) UI elements should use a focus ring, rather than show
+ // hover state on focus.
+ static const bool kPreferFocusRings;
+
// Creates the default scrollbar for the given orientation.
static std::unique_ptr<ScrollBar> CreateScrollBar(bool is_horizontal);
diff --git a/chromium/ui/views/style/platform_style_mac.mm b/chromium/ui/views/style/platform_style_mac.mm
index 36cdfff642d..2106dccefa2 100644
--- a/chromium/ui/views/style/platform_style_mac.mm
+++ b/chromium/ui/views/style/platform_style_mac.mm
@@ -42,6 +42,9 @@ const bool PlatformStyle::kTextfieldUsesDragCursorWhenDraggable = false;
const bool PlatformStyle::kTreeViewSelectionPaintsEntireRow = true;
const bool PlatformStyle::kShouldElideBookmarksInBookmarksBar = true;
const bool PlatformStyle::kUseRipples = false;
+const float PlatformStyle::kFocusHaloThickness = 4.f;
+const float PlatformStyle::kFocusHaloInset = -2.f;
+const bool PlatformStyle::kPreferFocusRings = true;
const Button::NotifyAction PlatformStyle::kMenuNotifyActivationAction =
Button::NOTIFY_ON_PRESS;
diff --git a/chromium/ui/views/style/typography_provider.cc b/chromium/ui/views/style/typography_provider.cc
index a66e387eb0a..e208380993d 100644
--- a/chromium/ui/views/style/typography_provider.cc
+++ b/chromium/ui/views/style/typography_provider.cc
@@ -37,12 +37,11 @@ gfx::Font::Weight GetValueBolderThan(gfx::Font::Weight weight) {
gfx::Font::Weight TypographyProvider::MediumWeightForUI() {
#if defined(OS_MACOSX)
// System fonts are not user-configurable on Mac, so there's a simpler check.
- // However, 10.9 and 10.11 do not ship with a MEDIUM weight system font. In
- // that case, trying to use MEDIUM there will give a bold font, which will
- // look worse with the surrounding NORMAL text than just using NORMAL.
- return (base::mac::IsOS10_9() || base::mac::IsOS10_11())
- ? gfx::Font::Weight::NORMAL
- : gfx::Font::Weight::MEDIUM;
+ // However, 10.11 do not ship with a MEDIUM weight system font. In that
+ // case, trying to use MEDIUM there will give a bold font, which will look
+ // worse with the surrounding NORMAL text than just using NORMAL.
+ return base::mac::IsOS10_11() ? gfx::Font::Weight::NORMAL
+ : gfx::Font::Weight::MEDIUM;
#else
// NORMAL may already have at least MEDIUM weight. Return NORMAL in that case
// since trying to return MEDIUM would actually make the font lighter-weight
diff --git a/chromium/ui/views/view.cc b/chromium/ui/views/view.cc
index f544882a25c..655e745ee61 100644
--- a/chromium/ui/views/view.cc
+++ b/chromium/ui/views/view.cc
@@ -12,7 +12,6 @@
#include "base/containers/adapters.h"
#include "base/logging.h"
#include "base/macros.h"
-#include "base/message_loop/message_loop.h"
#include "base/stl_util.h"
#include "base/strings/stringprintf.h"
#include "base/strings/utf_string_conversions.h"
@@ -52,6 +51,7 @@
#include "ui/views/drag_controller.h"
#include "ui/views/layout/layout_manager.h"
#include "ui/views/view_observer.h"
+#include "ui/views/view_tracker.h"
#include "ui/views/views_delegate.h"
#include "ui/views/views_switches.h"
#include "ui/views/widget/native_widget_private.h"
@@ -515,7 +515,10 @@ gfx::Transform View::GetTransform() const {
gfx::Transform transform = layer()->transform();
gfx::ScrollOffset scroll_offset = layer()->CurrentScrollOffset();
- transform.Translate(-scroll_offset.x(), -scroll_offset.y());
+ // Offsets for layer-based scrolling are never negative, but the horizontal
+ // scroll direction is reversed in RTL via canvas flipping.
+ transform.Translate((base::i18n::IsRTL() ? 1 : -1) * scroll_offset.x(),
+ -scroll_offset.y());
return transform;
}
@@ -1465,13 +1468,8 @@ void View::OnAccessibilityEvent(ax::mojom::Event event_type) {}
// Scrolling -------------------------------------------------------------------
void View::ScrollRectToVisible(const gfx::Rect& rect) {
- // We must take RTL UI mirroring into account when adjusting the position of
- // the region.
- if (parent_) {
- gfx::Rect scroll_rect(rect);
- scroll_rect.Offset(GetMirroredX(), y());
- parent_->ScrollRectToVisible(scroll_rect);
- }
+ if (parent_)
+ parent_->ScrollRectToVisible(rect + bounds().OffsetFromOrigin());
}
void View::ScrollViewToVisible() {
@@ -1800,10 +1798,19 @@ void View::OnBlur() {
void View::Focus() {
OnFocus();
ScrollViewToVisible();
+
+ for (ViewObserver& observer : observers_)
+ observer.OnViewFocused(this);
}
void View::Blur() {
+ ViewTracker tracker(this);
OnBlur();
+
+ if (tracker.view()) {
+ for (ViewObserver& observer : observers_)
+ observer.OnViewBlurred(this);
+ }
}
// Tooltips --------------------------------------------------------------------
@@ -1849,112 +1856,6 @@ PaintInfo::ScaleType View::GetPaintScaleType() const {
return PaintInfo::ScaleType::kScaleWithEdgeSnapping;
}
-// Debugging -------------------------------------------------------------------
-
-#if !defined(NDEBUG)
-
-std::string View::PrintViewGraph(bool first) {
- return DoPrintViewGraph(first, this);
-}
-
-std::string View::DoPrintViewGraph(bool first, View* view_with_children) {
- // 64-bit pointer = 16 bytes of hex + "0x" + '\0' = 19.
- const size_t kMaxPointerStringLength = 19;
-
- std::string result;
-
- if (first)
- result.append("digraph {\n");
-
- // Node characteristics.
- char p[kMaxPointerStringLength];
-
- const std::string class_name(GetClassName());
- size_t base_name_index = class_name.find_last_of('/');
- if (base_name_index == std::string::npos)
- base_name_index = 0;
- else
- base_name_index++;
-
- char bounds_buffer[512];
-
- // Information about current node.
- base::snprintf(p, arraysize(bounds_buffer), "%p", view_with_children);
- result.append(" N");
- result.append(p + 2);
- result.append(" [label=\"");
-
- result.append(class_name.substr(base_name_index).c_str());
-
- base::snprintf(bounds_buffer,
- arraysize(bounds_buffer),
- "\\n bounds: (%d, %d), (%dx%d)",
- bounds().x(),
- bounds().y(),
- bounds().width(),
- bounds().height());
- result.append(bounds_buffer);
-
- gfx::DecomposedTransform decomp;
- if (!GetTransform().IsIdentity() &&
- gfx::DecomposeTransform(&decomp, GetTransform())) {
- base::snprintf(bounds_buffer,
- arraysize(bounds_buffer),
- "\\n translation: (%f, %f)",
- decomp.translate[0],
- decomp.translate[1]);
- result.append(bounds_buffer);
-
- base::snprintf(bounds_buffer, arraysize(bounds_buffer),
- "\\n rotation: %3.2f",
- gfx::RadToDeg(std::acos(decomp.quaternion.w()) * 2));
- result.append(bounds_buffer);
-
- base::snprintf(bounds_buffer,
- arraysize(bounds_buffer),
- "\\n scale: (%2.4f, %2.4f)",
- decomp.scale[0],
- decomp.scale[1]);
- result.append(bounds_buffer);
- }
-
- result.append("\"");
- if (!parent_)
- result.append(", shape=box");
- if (layer()) {
- if (layer()->has_external_content())
- result.append(", color=green");
- else
- result.append(", color=red");
-
- if (layer()->fills_bounds_opaquely())
- result.append(", style=filled");
- }
- result.append("]\n");
-
- // Link to parent.
- if (parent_) {
- char pp[kMaxPointerStringLength];
-
- base::snprintf(pp, kMaxPointerStringLength, "%p", parent_);
- result.append(" N");
- result.append(pp + 2);
- result.append(" -> N");
- result.append(p + 2);
- result.append("\n");
- }
-
- // Children.
- for (auto* child : view_with_children->children_)
- result.append(child->PrintViewGraph(false));
-
- if (first)
- result.append("}\n");
-
- return result;
-}
-#endif
-
////////////////////////////////////////////////////////////////////////////////
// View, private:
diff --git a/chromium/ui/views/view.h b/chromium/ui/views/view.h
index 8b874876b89..cbc9e1866b4 100644
--- a/chromium/ui/views/view.h
+++ b/chromium/ui/views/view.h
@@ -419,6 +419,7 @@ class VIEWS_EXPORT View : public ui::LayerDelegate,
// Views as |children_|. The default implementation returns |children_|,
// subclass if the paint order should differ from that of |children_|.
// This order is taken into account by painting and targeting implementations.
+ // NOTE: see SetPaintToLayer() for details on painting and views with layers.
virtual View::Views GetChildrenInZOrder();
// Transformations -----------------------------------------------------------
@@ -439,6 +440,10 @@ class VIEWS_EXPORT View : public ui::LayerDelegate,
// . SetPaintToLayer(ui::LayerType) has been invoked.
// View creates the Layer only when it exists in a Widget with a non-NULL
// Compositor.
+ // Enabling a view to have a layer impacts painting of sibling views.
+ // Specifically views with layers effectively paint in a z-order that is
+ // always above any sibling views that do not have layers. This happens
+ // regardless of the ordering returned by GetChildrenInZOrder().
void SetPaintToLayer(ui::LayerType layer_type = ui::LAYER_TEXTURED);
// Please refer to the comments above the DestroyLayerImpl() function for
@@ -1391,22 +1396,6 @@ class VIEWS_EXPORT View : public ui::LayerDelegate,
// hierarchy).
virtual void OnNativeThemeChanged(const ui::NativeTheme* theme) {}
- // Debugging -----------------------------------------------------------------
-
-#if !defined(NDEBUG)
- // Returns string containing a graph of the views hierarchy in graphViz DOT
- // language (http://graphviz.org/). Can be called within debugger and save
- // to a file to compile/view.
- // Note: Assumes initial call made with first = true.
- virtual std::string PrintViewGraph(bool first);
-
- // Some classes may own an object which contains the children to displayed in
- // the views hierarchy. The above function gives the class the flexibility to
- // decide which object should be used to obtain the children, but this
- // function makes the decision explicit.
- std::string DoPrintViewGraph(bool first, View* view_with_children);
-#endif
-
private:
friend class internal::PreEventDispatchHandler;
friend class internal::PostEventDispatchHandler;
diff --git a/chromium/ui/views/view_observer.h b/chromium/ui/views/view_observer.h
index 10258fa4b0d..e2ed593e061 100644
--- a/chromium/ui/views/view_observer.h
+++ b/chromium/ui/views/view_observer.h
@@ -44,6 +44,12 @@ class VIEWS_EXPORT ViewObserver {
// Called from ~View.
virtual void OnViewIsDeleting(View* observed_view) {}
+ // Called immediately after |observed_view| has gained focus.
+ virtual void OnViewFocused(View* observed_view) {}
+
+ // Called immediately after |observed_view| has lost focus.
+ virtual void OnViewBlurred(View* observed_view) {}
+
protected:
virtual ~ViewObserver() {}
};
diff --git a/chromium/ui/views/view_properties.cc b/chromium/ui/views/view_properties.cc
index fae6a1fd789..d579a16c8cb 100644
--- a/chromium/ui/views/view_properties.cc
+++ b/chromium/ui/views/view_properties.cc
@@ -5,6 +5,7 @@
#include "ui/views/view_properties.h"
#include "ui/gfx/geometry/insets.h"
+#include "ui/views/bubble/bubble_dialog_delegate.h"
#if !defined(USE_AURA)
// aura_constants.cc also declared the bool ClassProperty type.
@@ -13,8 +14,14 @@ DEFINE_EXPORTED_UI_CLASS_PROPERTY_TYPE(VIEWS_EXPORT, bool);
DEFINE_EXPORTED_UI_CLASS_PROPERTY_TYPE(VIEWS_EXPORT, gfx::Insets*);
+DEFINE_EXPORTED_UI_CLASS_PROPERTY_TYPE(VIEWS_EXPORT,
+ views::BubbleDialogDelegateView*);
+
namespace views {
DEFINE_OWNED_UI_CLASS_PROPERTY_KEY(gfx::Insets, kMarginsKey, nullptr);
+DEFINE_UI_CLASS_PROPERTY_KEY(views::BubbleDialogDelegateView*,
+ kAnchoredDialogKey,
+ nullptr);
} // namespace views
diff --git a/chromium/ui/views/view_properties.h b/chromium/ui/views/view_properties.h
index 7f8a770b0d5..4e706ec7a02 100644
--- a/chromium/ui/views/view_properties.h
+++ b/chromium/ui/views/view_properties.h
@@ -14,11 +14,18 @@ class Insets;
namespace views {
+class BubbleDialogDelegateView;
+
// A property to store margins around the outer perimeter of the view. Margins
// are outside the bounds of the view. This is used by various layout managers
// to position views with the proper spacing between them.
VIEWS_EXPORT extern const ui::ClassProperty<gfx::Insets*>* const kMarginsKey;
+// A property to store the bubble dialog anchored to this view, to
+// enable the bubble's contents to be included in the focus order.
+VIEWS_EXPORT extern const ui::ClassProperty<BubbleDialogDelegateView*>* const
+ kAnchoredDialogKey;
+
} // namespace views
// Declaring the template specialization here to make sure that the
@@ -27,5 +34,7 @@ VIEWS_EXPORT extern const ui::ClassProperty<gfx::Insets*>* const kMarginsKey;
// template instance before its specialization is declared in a
// translation unit is a C++ error.
DECLARE_EXPORTED_UI_CLASS_PROPERTY_TYPE(VIEWS_EXPORT, gfx::Insets*);
+DECLARE_EXPORTED_UI_CLASS_PROPERTY_TYPE(VIEWS_EXPORT,
+ views::BubbleDialogDelegateView*);
#endif // UI_VIEWS_VIEW_PROPERTIES_H_
diff --git a/chromium/ui/views/views_delegate.cc b/chromium/ui/views/views_delegate.cc
index a64b2cb2551..7b303dac473 100644
--- a/chromium/ui/views/views_delegate.cc
+++ b/chromium/ui/views/views_delegate.cc
@@ -100,12 +100,6 @@ void ViewsDelegate::AddRef() {
void ViewsDelegate::ReleaseRef() {
}
-content::WebContents* ViewsDelegate::CreateWebContents(
- content::BrowserContext* browser_context,
- content::SiteInstance* site_instance) {
- return nullptr;
-}
-
base::TimeDelta ViewsDelegate::GetTextfieldPasswordRevealDuration() {
return base::TimeDelta();
}
diff --git a/chromium/ui/views/views_delegate.h b/chromium/ui/views/views_delegate.h
index cfaf7b0dd8a..8f703482084 100644
--- a/chromium/ui/views/views_delegate.h
+++ b/chromium/ui/views/views_delegate.h
@@ -27,12 +27,6 @@ namespace base {
class TimeDelta;
}
-namespace content {
-class WebContents;
-class BrowserContext;
-class SiteInstance;
-}
-
namespace gfx {
class ImageSkia;
class Rect;
@@ -175,11 +169,6 @@ class VIEWS_EXPORT ViewsDelegate {
virtual void AddRef();
virtual void ReleaseRef();
- // Creates a web contents. This will return NULL unless overriden.
- virtual content::WebContents* CreateWebContents(
- content::BrowserContext* browser_context,
- content::SiteInstance* site_instance);
-
// Gives the platform a chance to modify the properties of a Widget.
virtual void OnBeforeWidgetInit(Widget::InitParams* params,
internal::NativeWidgetDelegate* delegate) = 0;
diff --git a/chromium/ui/views/views_test_suite.cc b/chromium/ui/views/views_test_suite.cc
index 44f34e279a4..bd46fca6a32 100644
--- a/chromium/ui/views/views_test_suite.cc
+++ b/chromium/ui/views/views_test_suite.cc
@@ -52,7 +52,7 @@ void ViewsTestSuite::Initialize() {
ui::RegisterPathProvider();
base::FilePath ui_test_pak_path;
- ASSERT_TRUE(PathService::Get(ui::UI_TEST_PAK, &ui_test_pak_path));
+ ASSERT_TRUE(base::PathService::Get(ui::UI_TEST_PAK, &ui_test_pak_path));
ui::ResourceBundle::InitSharedInstanceWithPakPath(ui_test_pak_path);
#if defined(USE_AURA)
InitializeEnv();
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 ad0123acabc..f1e8c4eff5a 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
@@ -10,7 +10,6 @@
#include "base/lazy_instance.h"
#include "base/macros.h"
#include "base/memory/ptr_util.h"
-#include "base/message_loop/message_loop.h"
#include "base/metrics/histogram_macros.h"
#include "third_party/skia/include/core/SkBitmap.h"
#include "ui/aura/client/capture_client.h"
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 bb391ec3b33..42c838cb065 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
@@ -16,6 +16,7 @@
#include "ui/aura/client/window_parenting_client.h"
#include "ui/aura/window.h"
#include "ui/aura/window_observer.h"
+#include "ui/aura/window_occlusion_tracker.h"
#include "ui/aura/window_tree_host.h"
#include "ui/base/class_property.h"
#include "ui/base/hit_test.h"
@@ -51,6 +52,7 @@
#include "ui/wm/core/focus_controller.h"
#include "ui/wm/core/native_cursor_manager.h"
#include "ui/wm/core/shadow_controller.h"
+#include "ui/wm/core/shadow_controller_delegate.h"
#include "ui/wm/core/shadow_types.h"
#include "ui/wm/core/visibility_controller.h"
#include "ui/wm/core/window_animations.h"
@@ -533,8 +535,8 @@ void DesktopNativeWidgetAura::InitNativeWidget(
event_client_.reset(new DesktopEventClient);
aura::client::SetEventClient(host_->window(), event_client_.get());
- shadow_controller_.reset(
- new wm::ShadowController(wm::GetActivationClient(host_->window())));
+ shadow_controller_.reset(new wm::ShadowController(
+ wm::GetActivationClient(host_->window()), nullptr));
OnSizeConstraintsChanged();
@@ -588,6 +590,11 @@ const ui::Layer* DesktopNativeWidgetAura::GetLayer() const {
}
void DesktopNativeWidgetAura::ReorderNativeViews() {
+ // Reordering native views causes multiple changes to the window tree.
+ // Instantiate a ScopedPauseOcclusionTracking to recompute occlusion once at
+ // the end of this scope rather than after each individual change.
+ // https://crbug.com/829918
+ aura::WindowOcclusionTracker::ScopedPauseOcclusionTracking pause_occlusion;
window_reorderer_->ReorderChildWindows();
}
@@ -698,6 +705,12 @@ void DesktopNativeWidgetAura::SetBounds(const gfx::Rect& bounds) {
bounds_in_pixels);
}
+void DesktopNativeWidgetAura::SetBoundsConstrained(const gfx::Rect& bounds) {
+ if (!content_window_)
+ return;
+ SetBounds(NativeWidgetPrivate::ConstrainBoundsToDisplayWorkArea(bounds));
+}
+
void DesktopNativeWidgetAura::SetSize(const gfx::Size& size) {
if (content_window_)
desktop_window_tree_host_->SetSize(size);
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 fc4d17f3285..be8fd0378c6 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
@@ -137,6 +137,7 @@ class VIEWS_EXPORT DesktopNativeWidgetAura
gfx::Rect GetRestoredBounds() const override;
std::string GetWorkspace() const override;
void SetBounds(const gfx::Rect& bounds) override;
+ void SetBoundsConstrained(const gfx::Rect& bounds) override;
void SetSize(const gfx::Size& size) override;
void StackAbove(gfx::NativeView native_view) override;
void StackAtTop() override;
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 e9ddabeeda6..1e7210e7601 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
@@ -15,7 +15,9 @@
#include "ui/aura/client/focus_client.h"
#include "ui/aura/client/window_parenting_client.h"
#include "ui/aura/test/test_window_delegate.h"
+#include "ui/aura/test/window_occlusion_tracker_test_api.h"
#include "ui/aura/window.h"
+#include "ui/aura/window_occlusion_tracker.h"
#include "ui/aura/window_tree_host.h"
#include "ui/display/screen.h"
#include "ui/events/event_processor.h"
@@ -26,6 +28,7 @@
#include "ui/views/test/test_views_delegate.h"
#include "ui/views/test/views_test_base.h"
#include "ui/views/test/widget_test.h"
+#include "ui/views/view_constants_aura.h"
#include "ui/views/widget/widget.h"
#include "ui/views/window/dialog_delegate.h"
@@ -279,6 +282,64 @@ TEST_F(DesktopNativeWidgetAuraTest, DontAccessContentWindowDuringDestruction) {
}
}
+namespace {
+
+std::unique_ptr<Widget> CreateAndShowControlWidget(aura::Window* parent) {
+ Widget::InitParams params(Widget::InitParams::TYPE_CONTROL);
+ params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
+ params.parent = parent;
+ auto widget = std::make_unique<Widget>();
+ widget->Init(params);
+ widget->Show();
+ return widget;
+}
+
+} // namespace
+
+TEST_F(DesktopNativeWidgetAuraTest, ReorderDoesntRecomputeOcclusion) {
+ // Create the parent widget.
+ Widget parent;
+ Widget::InitParams init_params =
+ CreateParams(Widget::InitParams::TYPE_WINDOW);
+ init_params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
+ DesktopNativeWidgetAura* desktop_native_widget_aura =
+ new DesktopNativeWidgetAura(&parent);
+ init_params.native_widget = desktop_native_widget_aura;
+ parent.Init(init_params);
+ parent.Show();
+
+ aura::Window* parent_window = parent.GetNativeWindow();
+ aura::WindowOcclusionTracker::Track(parent_window);
+
+ View* contents_view = parent.GetContentsView();
+
+ // Create child widgets.
+ std::unique_ptr<Widget> w1(CreateAndShowControlWidget(parent_window));
+ std::unique_ptr<Widget> w2(CreateAndShowControlWidget(parent_window));
+ std::unique_ptr<Widget> w3(CreateAndShowControlWidget(parent_window));
+
+ // Create child views.
+ View* host_view1 = new View();
+ w1->GetNativeView()->SetProperty(kHostViewKey, host_view1);
+ contents_view->AddChildView(host_view1);
+
+ View* host_view2 = new View();
+ w2->GetNativeView()->SetProperty(kHostViewKey, host_view2);
+ contents_view->AddChildView(host_view2);
+
+ View* host_view3 = new View();
+ w3->GetNativeView()->SetProperty(kHostViewKey, host_view3);
+ contents_view->AddChildView(host_view3);
+
+ // Reorder child views. Expect occlusion to only be recomputed once.
+ aura::test::WindowOcclusionTrackerTestApi window_occlusion_tracker_test_api;
+ const int num_times_occlusion_recomputed =
+ window_occlusion_tracker_test_api.GetNumTimesOcclusionRecomputed();
+ contents_view->ReorderChildView(host_view3, 0);
+ EXPECT_EQ(num_times_occlusion_recomputed + 1,
+ window_occlusion_tracker_test_api.GetNumTimesOcclusionRecomputed());
+}
+
void QuitNestedLoopAndCloseWidget(std::unique_ptr<Widget> widget,
base::Closure* quit_runloop) {
quit_runloop->Run();
diff --git a/chromium/ui/views/widget/desktop_aura/desktop_screen.cc b/chromium/ui/views/widget/desktop_aura/desktop_screen.cc
new file mode 100644
index 00000000000..b656c14e4f7
--- /dev/null
+++ b/chromium/ui/views/widget/desktop_aura/desktop_screen.cc
@@ -0,0 +1,23 @@
+// Copyright 2018 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/desktop_aura/desktop_screen.h"
+
+#include "ui/display/screen.h"
+
+namespace views {
+
+void InstallDesktopScreenIfNecessary() {
+#if defined(OS_CHROMEOS)
+ // ChromeOS ozone builds use another path instead, where display::Screen is
+ // properly set. Thus, do early return here.
+ return;
+#endif
+
+ // The screen may have already been set in test initialization.
+ if (!display::Screen::GetScreen())
+ display::Screen::SetScreenInstance(CreateDesktopScreen());
+}
+
+} // namespace views
diff --git a/chromium/ui/views/widget/desktop_aura/desktop_screen.h b/chromium/ui/views/widget/desktop_aura/desktop_screen.h
index 88d2ffee2d7..1f1ff49fe04 100644
--- a/chromium/ui/views/widget/desktop_aura/desktop_screen.h
+++ b/chromium/ui/views/widget/desktop_aura/desktop_screen.h
@@ -17,6 +17,8 @@ namespace views {
// a WindowTreeHost. Caller owns the result.
VIEWS_EXPORT display::Screen* CreateDesktopScreen();
+VIEWS_EXPORT void InstallDesktopScreenIfNecessary();
+
} // namespace views
#endif // UI_VIEWS_WIDGET_DESKTOP_AURA_DESKTOP_SCREEN_H_
diff --git a/chromium/ui/views/widget/desktop_aura/desktop_screen_ozone.cc b/chromium/ui/views/widget/desktop_aura/desktop_screen_ozone.cc
new file mode 100644
index 00000000000..a5e871e8dae
--- /dev/null
+++ b/chromium/ui/views/widget/desktop_aura/desktop_screen_ozone.cc
@@ -0,0 +1,61 @@
+// Copyright 2018 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/desktop_aura/desktop_screen_ozone.h"
+
+#include "ui/display/display.h"
+#include "ui/display/types/display_constants.h"
+#include "ui/display/types/display_snapshot.h"
+#include "ui/display/types/native_display_delegate.h"
+#include "ui/gfx/geometry/dip_util.h"
+#include "ui/ozone/public/ozone_platform.h"
+#include "ui/views/widget/desktop_aura/desktop_screen.h"
+
+namespace views {
+
+DesktopScreenOzone::DesktopScreenOzone()
+ : delegate_(
+ ui::OzonePlatform::GetInstance()->CreateNativeDisplayDelegate()) {
+ delegate_->AddObserver(this);
+ delegate_->Initialize();
+}
+
+DesktopScreenOzone::~DesktopScreenOzone() = default;
+
+void DesktopScreenOzone::OnHostDisplaysReady(
+ const std::vector<display::DisplaySnapshot*>& displays) {
+ DCHECK(!displays.empty());
+ // TODO(msisov): Add support for multiple displays.
+ display::DisplaySnapshot* display_snapshot = displays.front();
+ DCHECK(display_snapshot);
+
+ float device_scale_factor = 1.f;
+ if (display::Display::HasForceDeviceScaleFactor())
+ device_scale_factor = display::Display::GetForcedDeviceScaleFactor();
+
+ gfx::Size scaled_size = gfx::ConvertSizeToDIP(
+ device_scale_factor, display_snapshot->current_mode()->size());
+
+ display::Display display(display_snapshot->display_id());
+ display.set_bounds(gfx::Rect(scaled_size));
+ display.set_work_area(display.bounds());
+ display.set_device_scale_factor(device_scale_factor);
+
+ ProcessDisplayChanged(display, true /* is_primary */);
+}
+
+void DesktopScreenOzone::OnConfigurationChanged() {
+ delegate_->GetDisplays(base::BindOnce(
+ &DesktopScreenOzone::OnHostDisplaysReady, base::Unretained(this)));
+}
+
+void DesktopScreenOzone::OnDisplaySnapshotsInvalidated() {}
+
+//////////////////////////////////////////////////////////////////////////////
+
+display::Screen* CreateDesktopScreen() {
+ return new DesktopScreenOzone;
+}
+
+} // namespace views
diff --git a/chromium/ui/views/widget/desktop_aura/desktop_screen_ozone.h b/chromium/ui/views/widget/desktop_aura/desktop_screen_ozone.h
new file mode 100644
index 00000000000..e742f769ebd
--- /dev/null
+++ b/chromium/ui/views/widget/desktop_aura/desktop_screen_ozone.h
@@ -0,0 +1,39 @@
+// Copyright 2018 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_DESKTOP_SCREEN_OZONE_H_
+#define UI_VIEWS_WIDGET_DESKTOP_AURA_DESKTOP_SCREEN_OZONE_H_
+
+#include "ui/display/screen_base.h"
+#include "ui/display/types/native_display_observer.h"
+
+namespace display {
+class NativeDisplayDelegate;
+class DisplaySnapshot;
+} // namespace display
+
+namespace views {
+
+class DesktopScreenOzone : public display::ScreenBase,
+ public display::NativeDisplayObserver {
+ public:
+ DesktopScreenOzone();
+ ~DesktopScreenOzone() override;
+
+ private:
+ void OnHostDisplaysReady(
+ const std::vector<display::DisplaySnapshot*>& displays);
+
+ // display::NativeDisplayDelegate:
+ void OnConfigurationChanged() override;
+ void OnDisplaySnapshotsInvalidated() override;
+
+ std::unique_ptr<display::NativeDisplayDelegate> delegate_;
+
+ DISALLOW_COPY_AND_ASSIGN(DesktopScreenOzone);
+};
+
+} // namespace views
+
+#endif // UI_VIEWS_WIDGET_DESKTOP_AURA_DESKTOP_SCREEN_OZONE_H_
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 2822c8e41ef..8fd5d379d34 100644
--- a/chromium/ui/views/widget/desktop_aura/desktop_screen_x11.cc
+++ b/chromium/ui/views/widget/desktop_aura/desktop_screen_x11.cc
@@ -362,6 +362,9 @@ std::vector<display::Display> DesktopScreenX11::BuildDisplaysFromXRandRInfo() {
primary_display_index_ = 0;
RROutput primary_display_id = XRRGetOutputPrimary(xdisplay_, x_root_window_);
+ int explicit_primary_display_index = -1;
+ int monitor_order_primary_display_index = -1;
+
bool has_work_area = false;
gfx::Rect work_area_in_pixels;
std::vector<int> value;
@@ -436,10 +439,13 @@ std::vector<display::Display> DesktopScreenX11::BuildDisplaysFromXRandRInfo() {
}
if (is_primary_display)
- primary_display_index_ = displays.size();
+ explicit_primary_display_index = displays.size();
+
+ auto monitor_iter = output_to_monitor.find(output_id);
+ if (monitor_iter != output_to_monitor.end() && monitor_iter->second == 0)
+ monitor_order_primary_display_index = displays.size();
if (!display::Display::HasForceColorProfile()) {
- auto monitor_iter = output_to_monitor.find(output_id);
gfx::ICCProfile icc_profile = GetICCProfileForMonitor(
monitor_iter == output_to_monitor.end() ? 0 : monitor_iter->second);
icc_profile.HistogramDisplay(display.id());
@@ -450,6 +456,12 @@ std::vector<display::Display> DesktopScreenX11::BuildDisplaysFromXRandRInfo() {
}
}
+ if (explicit_primary_display_index != -1) {
+ primary_display_index_ = explicit_primary_display_index;
+ } else if (monitor_order_primary_display_index != -1) {
+ primary_display_index_ = monitor_order_primary_display_index;
+ }
+
if (displays.empty())
return GetFallbackDisplayList();
diff --git a/chromium/ui/views/widget/desktop_aura/desktop_window_tree_host_ozone.cc b/chromium/ui/views/widget/desktop_aura/desktop_window_tree_host_ozone.cc
deleted file mode 100644
index 5834cffc5a0..00000000000
--- a/chromium/ui/views/widget/desktop_aura/desktop_window_tree_host_ozone.cc
+++ /dev/null
@@ -1,16 +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.
-
-#include "ui/views/widget/desktop_aura/desktop_window_tree_host.h"
-
-namespace views {
-
-DesktopWindowTreeHost* DesktopWindowTreeHost::Create(
- internal::NativeWidgetDelegate* native_widget_delegate,
- DesktopNativeWidgetAura* desktop_native_widget_aura) {
- NOTREACHED() << "Ozone builds should use DesktopWindowTreeHostMus codepath.";
- return nullptr;
-}
-
-} // namespace views
diff --git a/chromium/ui/views/widget/desktop_aura/desktop_window_tree_host_platform.cc b/chromium/ui/views/widget/desktop_aura/desktop_window_tree_host_platform.cc
index 97b54ace8b5..60d2c66cca0 100644
--- a/chromium/ui/views/widget/desktop_aura/desktop_window_tree_host_platform.cc
+++ b/chromium/ui/views/widget/desktop_aura/desktop_window_tree_host_platform.cc
@@ -17,6 +17,9 @@
namespace views {
+////////////////////////////////////////////////////////////////////////////////
+// DesktopWindowTreeHostPlatform:
+
DesktopWindowTreeHostPlatform::DesktopWindowTreeHostPlatform(
internal::NativeWidgetDelegate* native_widget_delegate,
DesktopNativeWidgetAura* desktop_native_widget_aura)
@@ -33,15 +36,13 @@ void DesktopWindowTreeHostPlatform::SetBoundsInDIP(
const gfx::Rect& bounds_in_dip) {
DCHECK_NE(0, device_scale_factor());
SetBoundsInPixels(
- gfx::ConvertRectToPixel(device_scale_factor(), bounds_in_dip));
+ gfx::ConvertRectToPixel(device_scale_factor(), bounds_in_dip),
+ viz::LocalSurfaceId());
}
void DesktopWindowTreeHostPlatform::Init(const Widget::InitParams& params) {
CreateAndSetDefaultPlatformWindow();
- // TODO(sky): this should be |params.force_software_compositing|, figure out
- // why it has to be true now.
- const bool use_software_compositing = true;
- CreateCompositor(viz::FrameSinkId(), use_software_compositing);
+ CreateCompositor(viz::FrameSinkId(), params.force_software_compositing);
aura::WindowTreeHost::OnAcceleratedWidgetAvailable();
InitHost();
if (!params.bounds.IsEmpty())
@@ -76,7 +77,9 @@ void DesktopWindowTreeHostPlatform::Close() {
return;
// Hide while waiting for the close.
- platform_window()->Hide();
+ // Please note that it's better to call WindowTreeHost::Hide, which also calls
+ // PlatformWindow::Hide and Compositor::SetVisible(false).
+ Hide();
waiting_for_close_now_ = true;
base::ThreadTaskRunnerHandle::Get()->PostTask(
@@ -257,15 +260,13 @@ void DesktopWindowTreeHostPlatform::Restore() {
}
bool DesktopWindowTreeHostPlatform::IsMaximized() const {
- // TODO: needs PlatformWindow support.
- NOTIMPLEMENTED_LOG_ONCE();
- return false;
+ return platform_window()->GetPlatformWindowState() ==
+ ui::PlatformWindowState::PLATFORM_WINDOW_STATE_MAXIMIZED;
}
bool DesktopWindowTreeHostPlatform::IsMinimized() const {
- // TODO: needs PlatformWindow support.
- NOTIMPLEMENTED_LOG_ONCE();
- return false;
+ return platform_window()->GetPlatformWindowState() ==
+ ui::PlatformWindowState::PLATFORM_WINDOW_STATE_MINIMIZED;
}
bool DesktopWindowTreeHostPlatform::HasCapture() const {
@@ -340,14 +341,13 @@ bool DesktopWindowTreeHostPlatform::ShouldWindowContentsBeTransparent() const {
void DesktopWindowTreeHostPlatform::FrameTypeChanged() {}
void DesktopWindowTreeHostPlatform::SetFullscreen(bool fullscreen) {
- // TODO: needs PlatformWindow support.
- NOTIMPLEMENTED_LOG_ONCE();
+ if (IsFullscreen() != fullscreen)
+ platform_window()->ToggleFullscreen();
}
bool DesktopWindowTreeHostPlatform::IsFullscreen() const {
- // TODO: needs PlatformWindow support.
- NOTIMPLEMENTED_LOG_ONCE();
- return false;
+ return platform_window()->GetPlatformWindowState() ==
+ ui::PlatformWindowState::PLATFORM_WINDOW_STATE_FULLSCREEN;
}
void DesktopWindowTreeHostPlatform::SetOpacity(float opacity) {
@@ -409,18 +409,61 @@ void DesktopWindowTreeHostPlatform::OnClosed() {
desktop_native_widget_aura_->OnHostClosed();
}
+void DesktopWindowTreeHostPlatform::OnWindowStateChanged(
+ ui::PlatformWindowState new_state) {
+ // Propagate minimization/restore to compositor to avoid drawing 'blank'
+ // frames that could be treated as previews, which show content even if a
+ // window is minimized.
+ bool visible =
+ new_state != ui::PlatformWindowState::PLATFORM_WINDOW_STATE_MINIMIZED;
+ if (visible != compositor()->IsVisible()) {
+ compositor()->SetVisible(visible);
+ native_widget_delegate_->OnNativeWidgetVisibilityChanged(visible);
+ }
+
+ // It might require relayouting when state property has been changed.
+ if (visible)
+ Relayout();
+}
+
void DesktopWindowTreeHostPlatform::OnCloseRequest() {
GetWidget()->Close();
}
+void DesktopWindowTreeHostPlatform::OnAcceleratedWidgetDestroying() {
+ native_widget_delegate_->OnNativeWidgetDestroying();
+}
+
void DesktopWindowTreeHostPlatform::OnActivationChanged(bool active) {
is_active_ = active;
aura::WindowTreeHostPlatform::OnActivationChanged(active);
desktop_native_widget_aura_->HandleActivationChanged(active);
}
+void DesktopWindowTreeHostPlatform::Relayout() {
+ Widget* widget = native_widget_delegate_->AsWidget();
+ NonClientView* non_client_view = widget->non_client_view();
+ // non_client_view may be NULL, especially during creation.
+ if (non_client_view) {
+ non_client_view->client_view()->InvalidateLayout();
+ non_client_view->InvalidateLayout();
+ }
+ widget->GetRootView()->Layout();
+}
+
Widget* DesktopWindowTreeHostPlatform::GetWidget() {
return native_widget_delegate_->AsWidget();
}
+////////////////////////////////////////////////////////////////////////////////
+// DesktopWindowTreeHost:
+
+// static
+DesktopWindowTreeHost* DesktopWindowTreeHost::Create(
+ internal::NativeWidgetDelegate* native_widget_delegate,
+ DesktopNativeWidgetAura* desktop_native_widget_aura) {
+ return new DesktopWindowTreeHostPlatform(native_widget_delegate,
+ desktop_native_widget_aura);
+}
+
} // namespace views
diff --git a/chromium/ui/views/widget/desktop_aura/desktop_window_tree_host_platform.h b/chromium/ui/views/widget/desktop_aura/desktop_window_tree_host_platform.h
index e9bc864128e..551bcb598b8 100644
--- a/chromium/ui/views/widget/desktop_aura/desktop_window_tree_host_platform.h
+++ b/chromium/ui/views/widget/desktop_aura/desktop_window_tree_host_platform.h
@@ -90,10 +90,14 @@ class VIEWS_EXPORT DesktopWindowTreeHostPlatform
// WindowTreeHostPlatform:
void OnClosed() override;
+ void OnWindowStateChanged(ui::PlatformWindowState new_state) override;
void OnCloseRequest() override;
+ void OnAcceleratedWidgetDestroying() override;
void OnActivationChanged(bool active) override;
private:
+ void Relayout();
+
Widget* GetWidget();
internal::NativeWidgetDelegate* const native_widget_delegate_;
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 c8f868bf416..288b92c3ead 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
@@ -20,6 +20,7 @@
#include "ui/display/win/dpi.h"
#include "ui/display/win/screen_win.h"
#include "ui/events/keyboard_hook.h"
+#include "ui/events/keycodes/dom/dom_code.h"
#include "ui/gfx/geometry/insets.h"
#include "ui/gfx/geometry/vector2d.h"
#include "ui/gfx/native_widget_types.h"
@@ -521,7 +522,14 @@ gfx::Rect DesktopWindowTreeHostWin::GetBoundsInPixels() const {
return without_expansion;
}
-void DesktopWindowTreeHostWin::SetBoundsInPixels(const gfx::Rect& bounds) {
+void DesktopWindowTreeHostWin::SetBoundsInPixels(
+ const gfx::Rect& bounds,
+ const viz::LocalSurfaceId& local_surface_id) {
+ // On Windows, the callers of SetBoundsInPixels() shouldn't need to (or be
+ // able to) allocate LocalSurfaceId for the compositor. Aura itself should
+ // allocate the new ids as needed, instead.
+ DCHECK(!local_surface_id.is_valid());
+
// If the window bounds have to be expanded we need to subtract the
// window_expansion_top_left_delta_ from the origin and add the
// window_expansion_bottom_right_delta_ to the width and height
@@ -556,15 +564,16 @@ void DesktopWindowTreeHostWin::ReleaseCapture() {
}
bool DesktopWindowTreeHostWin::CaptureSystemKeyEventsImpl(
- base::Optional<base::flat_set<int>> key_codes) {
+ base::Optional<base::flat_set<ui::DomCode>> dom_codes) {
// Only one KeyboardHook should be active at a time, otherwise there will be
// problems with event routing (i.e. which Hook takes precedence) and
// destruction ordering.
DCHECK(!keyboard_hook_);
keyboard_hook_ = ui::KeyboardHook::Create(
- std::move(key_codes),
+ std::move(dom_codes), GetAcceleratedWidget(),
base::BindRepeating(&DesktopWindowTreeHostWin::HandleKeyEvent,
base::Unretained(this)));
+
return keyboard_hook_ != nullptr;
}
@@ -572,8 +581,14 @@ void DesktopWindowTreeHostWin::ReleaseSystemKeyEventCapture() {
keyboard_hook_.reset();
}
-bool DesktopWindowTreeHostWin::IsKeyLocked(int native_key_code) {
- return keyboard_hook_ && keyboard_hook_->IsKeyLocked(native_key_code);
+bool DesktopWindowTreeHostWin::IsKeyLocked(ui::DomCode dom_code) {
+ return keyboard_hook_ && keyboard_hook_->IsKeyLocked(dom_code);
+}
+
+base::flat_map<std::string, std::string>
+DesktopWindowTreeHostWin::GetKeyboardLayoutMap() {
+ NOTIMPLEMENTED();
+ return {};
}
void DesktopWindowTreeHostWin::SetCursorNative(gfx::NativeCursor cursor) {
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 4120d3ae63f..327e4fb5df8 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
@@ -23,6 +23,7 @@ class FocusClient;
}
namespace ui {
+enum class DomCode;
class InputMethod;
class KeyboardHook;
} // namespace ui
@@ -126,14 +127,17 @@ class VIEWS_EXPORT DesktopWindowTreeHostWin
void ShowImpl() override;
void HideImpl() override;
gfx::Rect GetBoundsInPixels() const override;
- void SetBoundsInPixels(const gfx::Rect& bounds) override;
+ void SetBoundsInPixels(const gfx::Rect& bounds,
+ const viz::LocalSurfaceId& local_surface_id =
+ viz::LocalSurfaceId()) override;
gfx::Point GetLocationOnScreenInPixels() const override;
void SetCapture() override;
void ReleaseCapture() override;
bool CaptureSystemKeyEventsImpl(
- base::Optional<base::flat_set<int>> keys_codes) override;
+ base::Optional<base::flat_set<ui::DomCode>> dom_codes) override;
void ReleaseSystemKeyEventCapture() override;
- bool IsKeyLocked(int native_key_code) override;
+ bool IsKeyLocked(ui::DomCode dom_code) override;
+ base::flat_map<std::string, std::string> GetKeyboardLayoutMap() override;
void SetCursorNative(gfx::NativeCursor cursor) override;
void OnCursorVisibilityChangedNative(bool show) override;
void MoveCursorToScreenLocationInPixels(
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 806360dbc69..cbfbfcbdcdb 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
@@ -37,6 +37,7 @@
#include "ui/events/devices/x11/touch_factory_x11.h"
#include "ui/events/event_utils.h"
#include "ui/events/keyboard_hook.h"
+#include "ui/events/keycodes/dom/dom_code.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"
@@ -598,7 +599,7 @@ void DesktopWindowTreeHostX11::StackAtTop() {
void DesktopWindowTreeHostX11::CenterWindow(const gfx::Size& size) {
gfx::Size size_in_pixels = ToPixelRect(gfx::Rect(size)).size();
- gfx::Rect parent_bounds_in_pixels = GetWorkAreaBoundsInPixels();
+ gfx::Rect parent_bounds_in_pixels = ToPixelRect(GetWorkAreaBoundsInScreen());
// If |window_|'s transient parent bounds are big enough to contain |size|,
// use them instead.
@@ -670,24 +671,21 @@ gfx::Rect DesktopWindowTreeHostX11::GetRestoredBounds() const {
}
std::string DesktopWindowTreeHostX11::GetWorkspace() const {
- if (workspace_.empty())
- const_cast<DesktopWindowTreeHostX11*>(this)->UpdateWorkspace();
- return workspace_;
+ return workspace_ ? base::IntToString(workspace_.value()) : std::string();
}
-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;
+void DesktopWindowTreeHostX11::UpdateWorkspace() {
+ int workspace;
+ if (ui::GetWindowDesktop(xwindow_, &workspace))
+ workspace_ = workspace;
+ else
+ workspace_ = base::nullopt;
}
gfx::Rect DesktopWindowTreeHostX11::GetWorkAreaBoundsInScreen() const {
- return ToDIPRect(GetWorkAreaBoundsInPixels());
+ return display::Screen::GetScreen()
+ ->GetDisplayNearestWindow(const_cast<aura::Window*>(window()))
+ .work_area();
}
void DesktopWindowTreeHostX11::SetShape(
@@ -891,7 +889,7 @@ void DesktopWindowTreeHostX11::SetVisibleOnAllWorkspaces(bool always_visible) {
return;
}
- workspace_ = base::IntToString(kAllDesktops);
+ workspace_ = kAllDesktops;
XEvent xevent;
memset (&xevent, 0, sizeof (xevent));
xevent.type = ClientMessage;
@@ -1199,7 +1197,13 @@ gfx::Rect DesktopWindowTreeHostX11::GetBoundsInPixels() const {
}
void DesktopWindowTreeHostX11::SetBoundsInPixels(
- const gfx::Rect& requested_bounds_in_pixel) {
+ const gfx::Rect& requested_bounds_in_pixel,
+ const viz::LocalSurfaceId& local_surface_id) {
+ // On desktop-x11, the callers of SetBoundsInPixels() shouldn't need to (or be
+ // able to) allocate LocalSurfaceId for the compositor. Aura itself should
+ // allocate the new ids as needed, instead.
+ DCHECK(!local_surface_id.is_valid());
+
gfx::Rect bounds_in_pixels(requested_bounds_in_pixel.origin(),
AdjustSize(requested_bounds_in_pixel.size()));
bool origin_changed = bounds_in_pixels_.origin() != bounds_in_pixels.origin();
@@ -1251,7 +1255,7 @@ void DesktopWindowTreeHostX11::SetBoundsInPixels(
if (origin_changed)
native_widget_delegate_->AsWidget()->OnNativeWidgetMove();
if (size_changed) {
- OnHostResizedInPixels(bounds_in_pixels.size());
+ OnHostResizedInPixels(bounds_in_pixels.size(), local_surface_id);
ResetWindowRegion();
}
}
@@ -1298,15 +1302,16 @@ void DesktopWindowTreeHostX11::ReleaseCapture() {
}
bool DesktopWindowTreeHostX11::CaptureSystemKeyEventsImpl(
- base::Optional<base::flat_set<int>> key_codes) {
+ base::Optional<base::flat_set<ui::DomCode>> dom_codes) {
// Only one KeyboardHook should be active at a time, otherwise there will be
// problems with event routing (i.e. which Hook takes precedence) and
// destruction ordering.
DCHECK(!keyboard_hook_);
keyboard_hook_ = ui::KeyboardHook::Create(
- std::move(key_codes),
+ std::move(dom_codes), GetAcceleratedWidget(),
base::BindRepeating(&DesktopWindowTreeHostX11::DispatchKeyEvent,
base::Unretained(this)));
+
return keyboard_hook_ != nullptr;
}
@@ -1314,8 +1319,8 @@ void DesktopWindowTreeHostX11::ReleaseSystemKeyEventCapture() {
keyboard_hook_.reset();
}
-bool DesktopWindowTreeHostX11::IsKeyLocked(int native_key_code) {
- return keyboard_hook_ && keyboard_hook_->IsKeyLocked(native_key_code);
+bool DesktopWindowTreeHostX11::IsKeyLocked(ui::DomCode dom_code) {
+ return keyboard_hook_ && keyboard_hook_->IsKeyLocked(dom_code);
}
void DesktopWindowTreeHostX11::SetCursorNative(gfx::NativeCursor cursor) {
@@ -1363,12 +1368,40 @@ void DesktopWindowTreeHostX11::OnFullscreenStateChanged() {}
void DesktopWindowTreeHostX11::InitX11Window(
const Widget::InitParams& params) {
- unsigned long attribute_mask = CWBackPixmap | CWBitGravity;
+ unsigned long attribute_mask = CWBackPixel | CWBitGravity;
XSetWindowAttributes swa;
memset(&swa, 0, sizeof(swa));
swa.background_pixmap = x11::None;
swa.bit_gravity = NorthWestGravity;
+ // Set the background color on startup to make the initial flickering
+ // happening between the XWindow is mapped and the first expose event
+ // is completely handled less annoying. If possible, we use the content
+ // window's background color, otherwise we fallback to white.
+ int background_color;
+
+ const views::LinuxUI* linux_ui = views::LinuxUI::instance();
+ if (linux_ui && content_window()) {
+ ui::NativeTheme::ColorId target_color;
+ switch (params.type) {
+ case Widget::InitParams::TYPE_BUBBLE:
+ target_color = ui::NativeTheme::kColorId_BubbleBackground;
+ break;
+ case Widget::InitParams::TYPE_TOOLTIP:
+ target_color = ui::NativeTheme::kColorId_TooltipBackground;
+ break;
+ default:
+ target_color = ui::NativeTheme::kColorId_WindowBackground;
+ break;
+ }
+
+ ui::NativeTheme* theme = linux_ui->GetNativeTheme(content_window());
+ background_color = theme->GetSystemColor(target_color);
+ } else {
+ background_color = WhitePixel(xdisplay_, DefaultScreen(xdisplay_));
+ }
+ swa.background_pixel = background_color;
+
::Atom window_type;
switch (params.type) {
case Widget::InitParams::TYPE_MENU:
@@ -1495,7 +1528,7 @@ void DesktopWindowTreeHostX11::InitX11Window(
if (is_always_on_top_)
state_atom_list.push_back(gfx::GetAtom("_NET_WM_STATE_ABOVE"));
- workspace_.clear();
+ workspace_ = base::nullopt;
if (params.visible_on_all_workspaces) {
state_atom_list.push_back(gfx::GetAtom("_NET_WM_STATE_STICKY"));
ui::SetIntProperty(xwindow_, "_NET_WM_DESKTOP", "CARDINAL", kAllDesktops);
@@ -2134,7 +2167,12 @@ uint32_t DesktopWindowTreeHostX11::DispatchEvent(
case ui::ET_SCROLL_FLING_CANCEL:
case ui::ET_SCROLL: {
ui::ScrollEvent scrollev(xev);
- SendEventToSink(&scrollev);
+ // We need to filter zero scroll offset here. Because
+ // MouseWheelEventQueue assumes we'll never get a zero scroll offset
+ // event and we need delta to determine which element to scroll on
+ // phaseBegan.
+ if (scrollev.x_offset() != 0.0 || scrollev.y_offset() != 0.0)
+ SendEventToSink(&scrollev);
break;
}
case ui::ET_KEY_PRESSED:
@@ -2253,7 +2291,9 @@ uint32_t DesktopWindowTreeHostX11::DispatchEvent(
} else if (changed_atom == gfx::GetAtom("_NET_FRAME_EXTENTS")) {
OnFrameExtentsUpdated();
} else if (changed_atom == gfx::GetAtom("_NET_WM_DESKTOP")) {
- if (UpdateWorkspace())
+ base::Optional<int> old_workspace = workspace_;
+ UpdateWorkspace();
+ if (workspace_ != old_workspace)
OnHostWorkspaceChanged();
}
break;
@@ -2280,27 +2320,6 @@ void DesktopWindowTreeHostX11::DelayedChangeFrameType(Widget::FrameType type) {
native_widget_delegate_->AsWidget()->non_client_view()->UpdateFrame();
}
-gfx::Rect DesktopWindowTreeHostX11::GetWorkAreaBoundsInPixels() const {
- std::vector<int> value;
- if (ui::GetIntArrayProperty(x_root_window_, "_NET_WORKAREA", &value) &&
- value.size() >= 4) {
- return gfx::Rect(value[0], value[1], value[2], value[3]);
- }
-
- // Fetch the geometry of the root window.
- Window root;
- int x, y;
- unsigned int width, height;
- unsigned int border_width, depth;
- if (!XGetGeometry(xdisplay_, x_root_window_, &root, &x, &y, &width, &height,
- &border_width, &depth)) {
- NOTIMPLEMENTED();
- return gfx::Rect(0, 0, 10, 10);
- }
-
- return gfx::Rect(x, y, width, height);
-}
-
gfx::Rect DesktopWindowTreeHostX11::ToDIPRect(
const gfx::Rect& rect_in_pixels) const {
gfx::RectF rect_in_dip = gfx::RectF(rect_in_pixels);
@@ -2350,6 +2369,13 @@ aura::Window* DesktopWindowTreeHostX11::content_window() {
return desktop_native_widget_aura_->content_window();
}
+base::flat_map<std::string, std::string>
+DesktopWindowTreeHostX11::GetKeyboardLayoutMap() {
+ if (views::LinuxUI::instance())
+ return views::LinuxUI::instance()->GetKeyboardLayoutMap();
+ return {};
+}
+
////////////////////////////////////////////////////////////////////////////////
// DesktopWindowTreeHost, public:
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 79d677baa9f..f424728b967 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
@@ -30,6 +30,7 @@ class ImageSkiaRep;
}
namespace ui {
+enum class DomCode;
class EventHandler;
class KeyboardHook;
class XScopedEventSelector;
@@ -87,6 +88,9 @@ class VIEWS_EXPORT DesktopWindowTreeHostX11
// Disables event listening to make |dialog| modal.
std::unique_ptr<base::Closure> DisableEventListening();
+ // Returns a map of KeyboardEvent code to KeyboardEvent key values.
+ base::flat_map<std::string, std::string> GetKeyboardLayoutMap() override;
+
protected:
// Overridden from DesktopWindowTreeHost:
void Init(const Widget::InitParams& params) override;
@@ -160,14 +164,16 @@ class VIEWS_EXPORT DesktopWindowTreeHostX11
void ShowImpl() override;
void HideImpl() override;
gfx::Rect GetBoundsInPixels() const override;
- void SetBoundsInPixels(const gfx::Rect& requested_bounds_in_pixels) override;
+ void SetBoundsInPixels(const gfx::Rect& requested_bounds_in_pixels,
+ const viz::LocalSurfaceId& local_surface_id =
+ viz::LocalSurfaceId()) override;
gfx::Point GetLocationOnScreenInPixels() const override;
void SetCapture() override;
void ReleaseCapture() override;
bool CaptureSystemKeyEventsImpl(
- base::Optional<base::flat_set<int>> keys_codes) override;
+ base::Optional<base::flat_set<ui::DomCode>> dom_codes) override;
void ReleaseSystemKeyEventCapture() override;
- bool IsKeyLocked(int native_key_code) override;
+ bool IsKeyLocked(ui::DomCode dom_code) override;
void SetCursorNative(gfx::NativeCursor cursor) override;
void MoveCursorToScreenLocationInPixels(
const gfx::Point& location_in_pixels) override;
@@ -222,8 +228,8 @@ class VIEWS_EXPORT DesktopWindowTreeHostX11
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();
+ // window.
+ void UpdateWorkspace();
// Updates |xwindow_|'s minimum and maximum size.
void UpdateMinAndMaxSize();
@@ -272,7 +278,6 @@ class VIEWS_EXPORT DesktopWindowTreeHostX11
void DelayedResize(const gfx::Size& size_in_pixels);
void DelayedChangeFrameType(Widget::FrameType new_type);
- gfx::Rect GetWorkAreaBoundsInPixels() const;
gfx::Rect ToDIPRect(const gfx::Rect& rect_in_pixels) const;
gfx::Rect ToPixelRect(const gfx::Rect& rect_in_dip) const;
@@ -327,8 +332,9 @@ class VIEWS_EXPORT DesktopWindowTreeHostX11
// |xwindow_|'s maximum size.
gfx::Size max_size_in_pixels_;
- // The workspace containing |xwindow_|.
- std::string workspace_;
+ // The workspace containing |xwindow_|. This will be base::nullopt when
+ // _NET_WM_DESKTOP is unset.
+ base::Optional<int> workspace_;
// The window manager state bits.
base::flat_set<::Atom> window_properties_;
diff --git a/chromium/ui/views/widget/desktop_aura/window_event_filter.h b/chromium/ui/views/widget/desktop_aura/window_event_filter.h
index 9d765cf73dd..a9bc0387c25 100644
--- a/chromium/ui/views/widget/desktop_aura/window_event_filter.h
+++ b/chromium/ui/views/widget/desktop_aura/window_event_filter.h
@@ -7,7 +7,6 @@
#include "base/compiler_specific.h"
#include "base/macros.h"
-#include "base/message_loop/message_loop.h"
#include "ui/events/event_handler.h"
#include "ui/views/views_export.h"
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 1ed79cf0122..9ac94ba132f 100644
--- a/chromium/ui/views/widget/desktop_aura/x11_desktop_handler.cc
+++ b/chromium/ui/views/widget/desktop_aura/x11_desktop_handler.cc
@@ -4,7 +4,6 @@
#include "ui/views/widget/desktop_aura/x11_desktop_handler.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"
diff --git a/chromium/ui/views/widget/desktop_aura/x11_desktop_window_move_client.cc b/chromium/ui/views/widget/desktop_aura/x11_desktop_window_move_client.cc
index 6e4dddebbee..52b2985e7ad 100644
--- a/chromium/ui/views/widget/desktop_aura/x11_desktop_window_move_client.cc
+++ b/chromium/ui/views/widget/desktop_aura/x11_desktop_window_move_client.cc
@@ -5,7 +5,6 @@
#include "ui/views/widget/desktop_aura/x11_desktop_window_move_client.h"
#include "base/debug/stack_trace.h"
-#include "base/message_loop/message_loop.h"
#include "base/run_loop.h"
#include "ui/aura/env.h"
#include "ui/aura/window.h"
diff --git a/chromium/ui/views/widget/desktop_aura/x11_desktop_window_move_client.h b/chromium/ui/views/widget/desktop_aura/x11_desktop_window_move_client.h
index 6d074456878..a2168023f3c 100644
--- a/chromium/ui/views/widget/desktop_aura/x11_desktop_window_move_client.h
+++ b/chromium/ui/views/widget/desktop_aura/x11_desktop_window_move_client.h
@@ -7,7 +7,6 @@
#include "base/callback.h"
#include "base/compiler_specific.h"
-#include "base/message_loop/message_loop.h"
#include "ui/gfx/geometry/point.h"
#include "ui/gfx/x/x11.h"
#include "ui/views/views_export.h"
diff --git a/chromium/ui/views/widget/desktop_aura/x11_window_event_filter.h b/chromium/ui/views/widget/desktop_aura/x11_window_event_filter.h
index 54a27a6afa6..f1cddd4baac 100644
--- a/chromium/ui/views/widget/desktop_aura/x11_window_event_filter.h
+++ b/chromium/ui/views/widget/desktop_aura/x11_window_event_filter.h
@@ -7,7 +7,6 @@
#include "base/compiler_specific.h"
#include "base/macros.h"
-#include "base/message_loop/message_loop.h"
#include "ui/events/event_handler.h"
#include "ui/gfx/x/x11.h"
#include "ui/gfx/x/x11_types.h"
diff --git a/chromium/ui/views/widget/desktop_widget_unittest.cc b/chromium/ui/views/widget/desktop_widget_unittest.cc
index 0004ccab265..0ab25f179c2 100644
--- a/chromium/ui/views/widget/desktop_widget_unittest.cc
+++ b/chromium/ui/views/widget/desktop_widget_unittest.cc
@@ -2,6 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
+#include "ui/display/display.h"
+#include "ui/display/screen.h"
#include "ui/views/test/native_widget_factory.h"
#include "ui/views/test/views_test_base.h"
#include "ui/views/test/widget_test.h"
@@ -29,7 +31,7 @@ TEST_F(DesktopScreenPositionClientTest, PositionDialog) {
Widget* dialog = DialogDelegate::CreateDialogWidget(
dialog_delegate_view, NULL, parent_widget.GetNativeView());
dialog->SetBounds(gfx::Rect(11, 12, 200, 200));
- EXPECT_EQ("11,12", dialog->GetWindowBoundsInScreen().origin().ToString());
+ EXPECT_EQ(gfx::Point(11, 12), dialog->GetWindowBoundsInScreen().origin());
}
// Verifies that setting the bounds of a control parented to something other
@@ -39,6 +41,8 @@ TEST_F(DesktopScreenPositionClientTest, PositionControlWithNonRootParent) {
Widget widget2;
Widget widget3;
gfx::Point origin = gfx::Point(16, 16);
+ gfx::Rect work_area =
+ display::Screen::GetScreen()->GetDisplayNearestPoint(origin).work_area();
// Use a custom frame type. By default we will choose a native frame when
// aero glass is enabled, and this complicates the logic surrounding origin
@@ -52,7 +56,9 @@ TEST_F(DesktopScreenPositionClientTest, PositionControlWithNonRootParent) {
// parented to the second, also not positioned at (0,0).
Widget::InitParams params1 =
CreateParams(Widget::InitParams::TYPE_WINDOW);
- params1.bounds = gfx::Rect(origin, gfx::Size(700, 600));
+ params1.bounds = gfx::Rect(
+ origin + work_area.OffsetFromOrigin(),
+ gfx::Size(700, work_area.height() - origin.y() - work_area.y()));
params1.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
params1.native_widget =
test::CreatePlatformDesktopNativeWidgetImpl(params1, &widget1, nullptr);
@@ -60,7 +66,7 @@ TEST_F(DesktopScreenPositionClientTest, PositionControlWithNonRootParent) {
Widget::InitParams params2 =
CreateParams(Widget::InitParams::TYPE_WINDOW);
- params2.bounds = gfx::Rect(origin, gfx::Size(600, 500));
+ params2.bounds = gfx::Rect(origin, gfx::Size(600, work_area.height() - 100));
params2.parent = widget1.GetNativeView();
params2.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
params2.child = true;
@@ -71,14 +77,94 @@ TEST_F(DesktopScreenPositionClientTest, PositionControlWithNonRootParent) {
params3.parent = widget2.GetNativeView();
params3.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
params3.child = true;
- params3.bounds = gfx::Rect(origin, gfx::Size(500, 400));
+ params3.bounds = gfx::Rect(origin, gfx::Size(500, work_area.height() - 200));
widget3.Init(params3);
// The origin of the 3rd window should be the sum of all parent origins.
- gfx::Point expected_origin(origin.x() * 3, origin.y() * 3);
- gfx::Rect expected_bounds(expected_origin, gfx::Size(500, 400));
+ gfx::Point expected_origin(origin.x() * 3 + work_area.x(),
+ origin.y() * 3 + work_area.y());
+ gfx::Rect expected_bounds(expected_origin,
+ gfx::Size(500, work_area.height() - 200));
gfx::Rect actual_bounds(widget3.GetWindowBoundsInScreen());
- EXPECT_EQ(expected_bounds.ToString(), actual_bounds.ToString());
+ EXPECT_EQ(expected_bounds, actual_bounds);
+}
+
+// Verifies that the initial bounds of the widget is fully on the screen.
+TEST_F(DesktopScreenPositionClientTest, InitialBoundsConstrainedToDesktop) {
+ Widget widget;
+ // Use the primary display for this test.
+ gfx::Rect work_area =
+ display::Screen::GetScreen()->GetPrimaryDisplay().work_area();
+ // Make the origin start at 75% of the width and height.
+ gfx::Point origin =
+ gfx::Point(work_area.width() * 3 / 4, work_area.height() * 3 / 4);
+
+ // Use a custom frame type. See above for further explanation.
+ widget.set_frame_type(Widget::FRAME_TYPE_FORCE_CUSTOM);
+
+ // Create a window that is intentionally positioned so that it is off screen.
+ Widget::InitParams params = CreateParams(Widget::InitParams::TYPE_WINDOW);
+ params.bounds = gfx::Rect(
+ origin, gfx::Size(work_area.width() / 2, work_area.height() / 2));
+ params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
+ params.native_widget =
+ test::CreatePlatformDesktopNativeWidgetImpl(params, &widget, nullptr);
+ widget.Init(params);
+
+ // The bounds of the window should be fully on the primary display.
+ gfx::Point expected_origin(work_area.right() - work_area.width() / 2,
+ work_area.bottom() - work_area.height() / 2);
+ gfx::Rect expected_bounds(expected_origin, gfx::Size(work_area.width() / 2,
+ work_area.height() / 2));
+ gfx::Rect actual_bounds(widget.GetWindowBoundsInScreen());
+ EXPECT_EQ(expected_bounds, actual_bounds);
+}
+
+// Verifies that the initial bounds of the widget is fully within the bounds of
+// the parent.
+TEST_F(DesktopScreenPositionClientTest, InitialBoundsConstrainedToParent) {
+ Widget widget1;
+ Widget widget2;
+ // Use the primary display for this test.
+ gfx::Rect work_area =
+ display::Screen::GetScreen()->GetPrimaryDisplay().work_area();
+ gfx::Point origin = gfx::Point(work_area.x() + work_area.width() / 4,
+ work_area.y() + work_area.height() / 4);
+
+ // Use a custom frame type. See above for further explanation
+ widget1.set_frame_type(Widget::FRAME_TYPE_FORCE_CUSTOM);
+ widget2.set_frame_type(Widget::FRAME_TYPE_FORCE_CUSTOM);
+
+ // Create 2 windows. A root window, and an arbitrary window parented to the
+ // root and positioned such that it extends beyond the bounds of the root.
+ Widget::InitParams params1 = CreateParams(Widget::InitParams::TYPE_WINDOW);
+ params1.bounds = gfx::Rect(
+ origin, gfx::Size(work_area.width() / 2, work_area.height() / 2));
+ params1.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
+ params1.native_widget =
+ test::CreatePlatformDesktopNativeWidgetImpl(params1, &widget1, nullptr);
+ widget1.Init(params1);
+
+ gfx::Rect widget_bounds(widget1.GetWindowBoundsInScreen());
+
+ Widget::InitParams params2 = CreateParams(Widget::InitParams::TYPE_WINDOW);
+ params2.bounds =
+ gfx::Rect(widget_bounds.width() * 3 / 4, widget_bounds.height() * 3 / 4,
+ widget_bounds.width() / 2, widget_bounds.height() / 2);
+ params2.parent = widget1.GetNativeView();
+ params2.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
+ params2.child = true;
+ widget2.Init(params2);
+
+ // The bounds of the child window should be fully in the parent.
+ gfx::Point expected_origin(
+ widget_bounds.right() - widget_bounds.width() / 2,
+ widget_bounds.bottom() - widget_bounds.height() / 2);
+ gfx::Rect expected_bounds(
+ expected_origin,
+ gfx::Size(widget_bounds.width() / 2, widget_bounds.height() / 2));
+ gfx::Rect actual_bounds(widget2.GetWindowBoundsInScreen());
+ EXPECT_EQ(expected_bounds, actual_bounds);
}
} // namespace views
diff --git a/chromium/ui/views/widget/native_widget_aura.cc b/chromium/ui/views/widget/native_widget_aura.cc
index 76f19a26a0b..8c0bbd9db9b 100644
--- a/chromium/ui/views/widget/native_widget_aura.cc
+++ b/chromium/ui/views/widget/native_widget_aura.cc
@@ -47,9 +47,11 @@
#include "ui/views/widget/widget_aura_utils.h"
#include "ui/views/widget/widget_delegate.h"
#include "ui/views/widget/window_reorderer.h"
+#include "ui/wm/core/coordinate_conversion.h"
#include "ui/wm/core/shadow_types.h"
#include "ui/wm/core/transient_window_manager.h"
#include "ui/wm/core/window_animations.h"
+#include "ui/wm/core/window_properties.h"
#include "ui/wm/core/window_util.h"
#include "ui/wm/public/activation_client.h"
#include "ui/wm/public/window_move_client.h"
@@ -187,7 +189,7 @@ void NativeWidgetAura::InitNativeWidget(const Widget::InitParams& params) {
wm::AddTransientChild(parent, window_);
if (!context)
context = parent;
- parent = NULL;
+ parent = nullptr;
// Generally transient bubbles are showing state associated to the parent
// window. Make sure the transient bubble is only visible if the parent is
@@ -483,6 +485,22 @@ void NativeWidgetAura::SetBounds(const gfx::Rect& bounds) {
window_->SetBounds(bounds);
}
+void NativeWidgetAura::SetBoundsConstrained(const gfx::Rect& bounds) {
+ if (!window_)
+ return;
+
+ gfx::Rect new_bounds(bounds);
+ if (window_->parent()) {
+ if (window_->parent()->GetProperty(wm::kUsesScreenCoordinatesKey)) {
+ new_bounds =
+ NativeWidgetPrivate::ConstrainBoundsToDisplayWorkArea(new_bounds);
+ } else {
+ new_bounds.AdjustToFit(gfx::Rect(window_->parent()->bounds().size()));
+ }
+ }
+ SetBounds(new_bounds);
+}
+
void NativeWidgetAura::SetSize(const gfx::Size& size) {
if (window_)
window_->SetBounds(gfx::Rect(window_->bounds().origin(), size));
diff --git a/chromium/ui/views/widget/native_widget_aura.h b/chromium/ui/views/widget/native_widget_aura.h
index 73ce9503ff9..74bd738bcf9 100644
--- a/chromium/ui/views/widget/native_widget_aura.h
+++ b/chromium/ui/views/widget/native_widget_aura.h
@@ -96,6 +96,7 @@ class VIEWS_EXPORT NativeWidgetAura : public internal::NativeWidgetPrivate,
gfx::Rect GetRestoredBounds() const override;
std::string GetWorkspace() const override;
void SetBounds(const gfx::Rect& bounds) override;
+ void SetBoundsConstrained(const gfx::Rect& bounds) override;
void SetSize(const gfx::Size& size) override;
void StackAbove(gfx::NativeView native_view) override;
void StackAtTop() override;
diff --git a/chromium/ui/views/widget/native_widget_mac.h b/chromium/ui/views/widget/native_widget_mac.h
index 74e3f333e57..c1d97e3ac54 100644
--- a/chromium/ui/views/widget/native_widget_mac.h
+++ b/chromium/ui/views/widget/native_widget_mac.h
@@ -37,10 +37,15 @@ class VIEWS_EXPORT NativeWidgetMac : public internal::NativeWidgetPrivate {
// a native window "sheet", and have a different lifetime to regular windows.
bool IsWindowModalSheet() const;
+ // Informs |delegate_| that the native widget is about to be destroyed.
+ // BridgedNativeWidget::OnWindowWillClose() invokes this early when the
+ // NSWindowDelegate informs the bridge that the window is being closed (later,
+ // invoking OnWindowDestroyed()).
+ void WindowDestroying();
+
// Deletes |bridge_| and informs |delegate_| that the native widget is
- // destroyed. BridgedNativeWidget::OnWindowWillClose() calls this when the
- // NSWindowDelegate informs the bridge that the window is being closed.
- void OnWindowDestroyed();
+ // destroyed.
+ void WindowDestroyed();
// Returns the vertical position that sheets should be anchored, in pixels
// from the bottom of the window.
@@ -81,6 +86,7 @@ class VIEWS_EXPORT NativeWidgetMac : public internal::NativeWidgetPrivate {
gfx::Rect GetRestoredBounds() const override;
std::string GetWorkspace() const override;
void SetBounds(const gfx::Rect& bounds) override;
+ void SetBoundsConstrained(const gfx::Rect& bounds) override;
void SetSize(const gfx::Size& size) override;
void StackAbove(gfx::NativeView native_view) override;
void StackAtTop() override;
@@ -138,6 +144,9 @@ class VIEWS_EXPORT NativeWidgetMac : public internal::NativeWidgetPrivate {
virtual NativeWidgetMacNSWindow* CreateNSWindow(
const Widget::InitParams& params);
+ // Optional hook for subclasses invoked by WindowDestroying().
+ virtual void OnWindowDestroying(NSWindow* window) {}
+
internal::NativeWidgetDelegate* delegate() { return delegate_; }
private:
diff --git a/chromium/ui/views/widget/native_widget_mac.mm b/chromium/ui/views/widget/native_widget_mac.mm
index 223eb1191d1..c0e28737b88 100644
--- a/chromium/ui/views/widget/native_widget_mac.mm
+++ b/chromium/ui/views/widget/native_widget_mac.mm
@@ -18,6 +18,8 @@
#import "ui/base/cocoa/constrained_window/constrained_window_animation.h"
#import "ui/base/cocoa/window_size_constants.h"
#include "ui/base/ui_base_switches.h"
+#include "ui/display/display.h"
+#include "ui/display/screen.h"
#include "ui/gfx/font_list.h"
#import "ui/gfx/mac/coordinate_conversion.h"
#import "ui/gfx/mac/nswindow_frame_controls.h"
@@ -109,7 +111,12 @@ bool NativeWidgetMac::IsWindowModalSheet() const {
ui::MODAL_TYPE_WINDOW;
}
-void NativeWidgetMac::OnWindowDestroyed() {
+void NativeWidgetMac::WindowDestroying() {
+ OnWindowDestroying(GetNativeWindow());
+ delegate_->OnNativeWidgetDestroying();
+}
+
+void NativeWidgetMac::WindowDestroyed() {
DCHECK(bridge_);
bridge_.reset();
delegate_->OnNativeWidgetDestroyed();
@@ -173,7 +180,10 @@ bool NativeWidgetMac::ShouldWindowContentsBeTransparent() const {
}
void NativeWidgetMac::FrameTypeChanged() {
- NOTIMPLEMENTED();
+ // This is called when the Theme has changed; forward the event to the root
+ // widget.
+ GetWidget()->ThemeChanged();
+ GetWidget()->GetRootView()->SchedulePaint();
}
Widget* NativeWidgetMac::GetWidget() {
@@ -195,7 +205,7 @@ gfx::NativeWindow NativeWidgetMac::GetNativeWindow() const {
Widget* NativeWidgetMac::GetTopLevelWidget() {
NativeWidgetPrivate* native_widget = GetTopLevelNativeWidget(GetNativeView());
- return native_widget ? native_widget->GetWidget() : NULL;
+ return native_widget ? native_widget->GetWidget() : nullptr;
}
const ui::Compositor* NativeWidgetMac::GetCompositor() const {
@@ -252,7 +262,7 @@ bool NativeWidgetMac::HasCapture() const {
}
ui::InputMethod* NativeWidgetMac::GetInputMethod() {
- return bridge_ ? bridge_->GetInputMethod() : NULL;
+ return bridge_ ? bridge_->GetInputMethod() : nullptr;
}
void NativeWidgetMac::CenterWindow(const gfx::Size& size) {
@@ -332,6 +342,24 @@ void NativeWidgetMac::SetBounds(const gfx::Rect& bounds) {
bridge_->SetBounds(bounds);
}
+void NativeWidgetMac::SetBoundsConstrained(const gfx::Rect& bounds) {
+ if (!bridge_)
+ return;
+
+ gfx::Rect new_bounds(bounds);
+ NativeWidgetPrivate* ancestor =
+ bridge_ && bridge_->parent()
+ ? GetNativeWidgetForNativeWindow(bridge_->parent()->GetNSWindow())
+ : nullptr;
+ if (!ancestor) {
+ new_bounds = ConstrainBoundsToDisplayWorkArea(new_bounds);
+ } else {
+ new_bounds.AdjustToFit(
+ gfx::Rect(ancestor->GetWindowBoundsInScreen().size()));
+ }
+ SetBounds(new_bounds);
+}
+
void NativeWidgetMac::SetSize(const gfx::Size& size) {
// Ensure the top-left corner stays in-place (rather than the bottom-left,
// which -[NSWindow setContentSize:] would do).
@@ -376,7 +404,7 @@ void NativeWidgetMac::Close() {
}
// Clear the view early to suppress repaints.
- bridge_->SetRootView(NULL);
+ bridge_->SetRootView(nullptr);
// Widget::Close() ensures [Non]ClientView::CanClose() returns true, so there
// is no need to call the NSWindow or its delegate's -windowShouldClose:
@@ -702,7 +730,7 @@ NativeWidgetPrivate* NativeWidgetPrivate::GetNativeWidgetForNativeWindow(
base::mac::ObjCCastStrict<ViewsNSWindowDelegate>(window_delegate);
return [delegate nativeWidgetMac];
}
- return NULL; // Not created by NativeWidgetMac.
+ return nullptr; // Not created by NativeWidgetMac.
}
// static
diff --git a/chromium/ui/views/widget/native_widget_mac_unittest.mm b/chromium/ui/views/widget/native_widget_mac_unittest.mm
index 6555f7ddd0f..e8c6ebeeaa7 100644
--- a/chromium/ui/views/widget/native_widget_mac_unittest.mm
+++ b/chromium/ui/views/widget/native_widget_mac_unittest.mm
@@ -12,7 +12,6 @@
#import "base/mac/scoped_nsobject.h"
#import "base/mac/scoped_objc_class_swizzler.h"
#include "base/macros.h"
-#include "base/message_loop/message_loop.h"
#include "base/run_loop.h"
#include "base/strings/sys_string_conversions.h"
#include "base/strings/utf_string_conversions.h"
@@ -109,7 +108,7 @@ class BridgedNativeWidgetTestApi {
ca_layer_params.pixel_size = size;
ca_layer_params.scale_factor = kScaleFactor;
ca_layer_frame_sink->UpdateCALayerTree(ca_layer_params);
- bridge_->AcceleratedWidgetSwapCompleted();
+ bridge_->AcceleratedWidgetCALayerParamsUpdated();
}
NSAnimation* show_animation() {
@@ -455,7 +454,7 @@ TEST_F(NativeWidgetMacTest, DISABLED_OrderFrontAfterMiniaturize) {
// Wait and check that child is really visible.
// TODO(kirr): remove the fixed delay.
base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
- FROM_HERE, base::MessageLoop::QuitWhenIdleClosure(),
+ FROM_HERE, base::RunLoop::QuitCurrentWhenIdleClosureDeprecated(),
base::TimeDelta::FromSeconds(2));
base::RunLoop().Run();
diff --git a/chromium/ui/views/widget/native_widget_private.cc b/chromium/ui/views/widget/native_widget_private.cc
new file mode 100644
index 00000000000..e8ad491578d
--- /dev/null
+++ b/chromium/ui/views/widget/native_widget_private.cc
@@ -0,0 +1,25 @@
+// Copyright 2018 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/native_widget_private.h"
+
+#include "ui/display/display.h"
+#include "ui/display/screen.h"
+
+namespace views {
+namespace internal {
+
+// static
+gfx::Rect NativeWidgetPrivate::ConstrainBoundsToDisplayWorkArea(
+ const gfx::Rect& bounds) {
+ gfx::Rect new_bounds(bounds);
+ gfx::Rect work_area =
+ display::Screen::GetScreen()->GetDisplayMatching(bounds).work_area();
+ if (!work_area.IsEmpty())
+ new_bounds.AdjustToFit(work_area);
+ return new_bounds;
+}
+
+} // namespace internal
+} // namespace views
diff --git a/chromium/ui/views/widget/native_widget_private.h b/chromium/ui/views/widget/native_widget_private.h
index ebeb4e377b9..226dcba1511 100644
--- a/chromium/ui/views/widget/native_widget_private.h
+++ b/chromium/ui/views/widget/native_widget_private.h
@@ -77,6 +77,10 @@ class VIEWS_EXPORT NativeWidgetPrivate : public NativeWidget {
// capture set, or if |native_view| has no root.
static gfx::NativeView GetGlobalCapture(gfx::NativeView native_view);
+ // Adjusts the given bounds to fit onto the display implied by the position
+ // of the given bounds.
+ static gfx::Rect ConstrainBoundsToDisplayWorkArea(const gfx::Rect& bounds);
+
// Initializes the NativeWidget.
virtual void InitNativeWidget(const Widget::InitParams& params) = 0;
@@ -173,6 +177,7 @@ class VIEWS_EXPORT NativeWidgetPrivate : public NativeWidget {
virtual gfx::Rect GetRestoredBounds() const = 0;
virtual std::string GetWorkspace() const = 0;
virtual void SetBounds(const gfx::Rect& bounds) = 0;
+ virtual void SetBoundsConstrained(const gfx::Rect& bounds) = 0;
virtual void SetSize(const gfx::Size& size) = 0;
virtual void StackAbove(gfx::NativeView native_view) = 0;
virtual void StackAtTop() = 0;
diff --git a/chromium/ui/views/widget/root_view.cc b/chromium/ui/views/widget/root_view.cc
index d07c9ba57f4..4d11e22af84 100644
--- a/chromium/ui/views/widget/root_view.cc
+++ b/chromium/ui/views/widget/root_view.cc
@@ -8,7 +8,6 @@
#include "base/logging.h"
#include "base/macros.h"
-#include "base/message_loop/message_loop.h"
#include "build/build_config.h"
#include "ui/accessibility/ax_node_data.h"
#include "ui/base/cursor/cursor.h"
diff --git a/chromium/ui/views/widget/widget.cc b/chromium/ui/views/widget/widget.cc
index 7bc25d68ab4..bf4b1d07f7f 100644
--- a/chromium/ui/views/widget/widget.cc
+++ b/chromium/ui/views/widget/widget.cc
@@ -7,7 +7,6 @@
#include "base/auto_reset.h"
#include "base/logging.h"
#include "base/macros.h"
-#include "base/message_loop/message_loop.h"
#include "base/strings/utf_string_conversions.h"
#include "base/trace_event/trace_event.h"
#include "ui/aura/window.h"
@@ -525,19 +524,7 @@ void Widget::CenterWindow(const gfx::Size& size) {
}
void Widget::SetBoundsConstrained(const gfx::Rect& bounds) {
- gfx::Rect work_area = display::Screen::GetScreen()
- ->GetDisplayNearestPoint(bounds.origin())
- .work_area();
- if (work_area.IsEmpty()) {
- SetBounds(bounds);
- } else {
- // TODO(https://crbug.com/806936): The following code doesn't actually do
- // what the comment describing this function says it should.
- // Inset the work area slightly.
- work_area.Inset(10, 10, 10, 10);
- work_area.AdjustToFit(bounds);
- SetBounds(work_area);
- }
+ native_widget_->SetBoundsConstrained(bounds);
}
void Widget::SetVisibilityChangedAnimationsEnabled(bool value) {
@@ -1510,7 +1497,7 @@ void Widget::SetInitialBounds(const gfx::Rect& bounds) {
if (bounds.origin().IsOrigin()) {
// No initial bounds supplied, so size the window to its content and
// center over its parent.
- native_widget_->CenterWindow(non_client_view_->GetPreferredSize());
+ CenterWindow(non_client_view_->GetPreferredSize());
} else {
// Use the preferred size and the supplied origin.
gfx::Rect preferred_bounds(bounds);
diff --git a/chromium/ui/views/widget/widget.h b/chromium/ui/views/widget/widget.h
index e2df1f67a8b..bc3b94fa4f3 100644
--- a/chromium/ui/views/widget/widget.h
+++ b/chromium/ui/views/widget/widget.h
@@ -449,9 +449,8 @@ class VIEWS_EXPORT Widget : public internal::NativeWidgetDelegate,
// Sizes the window to the specified size and centerizes it.
void CenterWindow(const gfx::Size& size);
- // Like SetBounds(), but ensures the Widget is fully visible on screen,
- // resizing and/or repositioning as necessary. This is only useful for
- // non-child widgets.
+ // Like SetBounds(), but ensures the Widget is fully visible on screen or
+ // parent widget, resizing and/or repositioning as necessary.
void SetBoundsConstrained(const gfx::Rect& bounds);
// Sets whether animations that occur when visibility is changed are enabled.
diff --git a/chromium/ui/views/widget/widget_interactive_uitest.cc b/chromium/ui/views/widget/widget_interactive_uitest.cc
index db44bd41b88..15a9ad10398 100644
--- a/chromium/ui/views/widget/widget_interactive_uitest.cc
+++ b/chromium/ui/views/widget/widget_interactive_uitest.cc
@@ -286,7 +286,7 @@ class WidgetTestInteractive : public WidgetTest {
gl::GLSurfaceTestSupport::InitializeOneOff();
ui::RegisterPathProvider();
base::FilePath ui_test_pak_path;
- ASSERT_TRUE(PathService::Get(ui::UI_TEST_PAK, &ui_test_pak_path));
+ ASSERT_TRUE(base::PathService::Get(ui::UI_TEST_PAK, &ui_test_pak_path));
ui::ResourceBundle::InitSharedInstanceWithPakPath(ui_test_pak_path);
}
WidgetTest::SetUp();
diff --git a/chromium/ui/views/widget/widget_unittest.cc b/chromium/ui/views/widget/widget_unittest.cc
index b978aabb83c..8e298d2f647 100644
--- a/chromium/ui/views/widget/widget_unittest.cc
+++ b/chromium/ui/views/widget/widget_unittest.cc
@@ -51,6 +51,7 @@
#include "ui/wm/core/base_focus_rules.h"
#include "ui/wm/core/focus_controller.h"
#include "ui/wm/core/shadow_controller.h"
+#include "ui/wm/core/shadow_controller_delegate.h"
#endif
namespace views {
@@ -3775,8 +3776,8 @@ class WidgetShadowTest : public WidgetTest {
focus_controller_ =
std::make_unique<wm::FocusController>(new TestFocusRules);
- shadow_controller_ =
- std::make_unique<wm::ShadowController>(focus_controller_.get());
+ shadow_controller_ = std::make_unique<wm::ShadowController>(
+ focus_controller_.get(), nullptr);
}
std::unique_ptr<wm::FocusController> focus_controller_;
diff --git a/chromium/ui/views/widget/widget_utils_mac.h b/chromium/ui/views/widget/widget_utils_mac.h
new file mode 100644
index 00000000000..2ee5ec72185
--- /dev/null
+++ b/chromium/ui/views/widget/widget_utils_mac.h
@@ -0,0 +1,16 @@
+// Copyright 2018 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_WIDGET_UTILS_MAC_H_
+#define UI_VIEWS_WIDGET_WIDGET_UTILS_MAC_H_
+
+#include "ui/views/widget/widget.h"
+
+namespace views {
+
+gfx::Size GetWindowSizeForClientSize(Widget* widget, const gfx::Size& size);
+
+} // namespace views
+
+#endif // UI_VIEWS_WIDGET_WIDGET_UTILS_MAC_H_ \ No newline at end of file
diff --git a/chromium/ui/views/widget/widget_utils_mac.mm b/chromium/ui/views/widget/widget_utils_mac.mm
new file mode 100644
index 00000000000..58d0a437fb0
--- /dev/null
+++ b/chromium/ui/views/widget/widget_utils_mac.mm
@@ -0,0 +1,17 @@
+// Copyright 2018 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/widget_utils_mac.h"
+
+#import "ui/views/cocoa/bridged_native_widget.h"
+
+namespace views {
+
+gfx::Size GetWindowSizeForClientSize(Widget* widget, const gfx::Size& size) {
+ DCHECK(widget);
+ return BridgedNativeWidget::GetWindowSizeForClientSize(
+ widget->GetNativeWindow(), size);
+}
+
+} // namespace views
diff --git a/chromium/ui/views/win/hwnd_message_handler.cc b/chromium/ui/views/win/hwnd_message_handler.cc
index 6a468941cf4..cadeb232262 100644
--- a/chromium/ui/views/win/hwnd_message_handler.cc
+++ b/chromium/ui/views/win/hwnd_message_handler.cc
@@ -17,7 +17,7 @@
#include "base/debug/alias.h"
#include "base/location.h"
#include "base/macros.h"
-#include "base/message_loop/message_loop.h"
+#include "base/message_loop/message_loop_current.h"
#include "base/single_thread_task_runner.h"
#include "base/threading/thread_task_runner_handle.h"
#include "base/time/time.h"
@@ -239,6 +239,12 @@ ui::EventType GetTouchEventType(POINTER_FLAGS pointer_flags) {
return ui::ET_TOUCH_MOVED;
}
+bool IsHitTestOnResizeHandle(LRESULT hittest) {
+ return hittest == HTRIGHT || hittest == HTLEFT || hittest == HTTOP ||
+ hittest == HTBOTTOM || hittest == HTTOPLEFT || hittest == HTTOPRIGHT ||
+ hittest == HTBOTTOMLEFT || hittest == HTBOTTOMRIGHT;
+}
+
const int kTouchDownContextResetTimeout = 500;
// Windows does not flag synthesized mouse messages from touch or pen in all
@@ -339,8 +345,7 @@ base::LazyInstance<HWNDMessageHandler::FullscreenWindowMonitorMap>::
long HWNDMessageHandler::last_touch_or_pen_message_time_ = 0;
HWNDMessageHandler::HWNDMessageHandler(HWNDMessageHandlerDelegate* delegate)
- : msg_handled_(FALSE),
- delegate_(delegate),
+ : delegate_(delegate),
fullscreen_handler_(new FullscreenHandler),
waiting_for_close_now_(false),
use_system_default_icon_(false),
@@ -370,8 +375,7 @@ HWNDMessageHandler::HWNDMessageHandler(HWNDMessageHandlerDelegate* delegate)
pointer_events_for_touch_(features::IsUsingWMPointerForTouch()),
precision_touchpad_scroll_phase_enabled_(base::FeatureList::IsEnabled(
features::kPrecisionTouchpadScrollPhase)),
- autohide_factory_(this),
- weak_factory_(this) {}
+ autohide_factory_(this) {}
HWNDMessageHandler::~HWNDMessageHandler() {
DCHECK(delegate_->GetHWNDMessageDelegateInputMethod());
@@ -451,7 +455,7 @@ void HWNDMessageHandler::Close() {
waiting_for_close_now_ = true;
base::ThreadTaskRunnerHandle::Get()->PostTask(
FROM_HERE, base::BindOnce(&HWNDMessageHandler::CloseNow,
- weak_factory_.GetWeakPtr()));
+ msg_handler_weak_factory_.GetWeakPtr()));
}
}
@@ -737,8 +741,7 @@ bool HWNDMessageHandler::RunMoveLoop(const gfx::Vector2d& drag_offset,
MoveLoopMouseWatcher watcher(this, hide_on_escape);
// In Aura, we handle touch events asynchronously. So we need to allow nested
// tasks while in windows move loop.
- base::MessageLoop::ScopedNestableTaskAllower allow_nested(
- base::MessageLoop::current());
+ base::MessageLoopCurrent::ScopedNestableTaskAllower allow_nested;
SendMessage(hwnd(), WM_SYSCOMMAND, SC_MOVE | 0x0002, GetMessagePos());
// Windows doesn't appear to offer a way to determine whether the user
@@ -927,7 +930,7 @@ LRESULT HWNDMessageHandler::OnWndProc(UINT message,
// NOTE: We inline ProcessWindowMessage() as 'this' may be destroyed during
// dispatch and ProcessWindowMessage() doesn't deal with that well.
const BOOL old_msg_handled = msg_handled_;
- base::WeakPtr<HWNDMessageHandler> ref(weak_factory_.GetWeakPtr());
+ base::WeakPtr<HWNDMessageHandler> ref(msg_handler_weak_factory_.GetWeakPtr());
const BOOL processed =
_ProcessWindowMessage(window, message, w_param, l_param, result, 0);
if (!ref)
@@ -992,9 +995,9 @@ LRESULT HWNDMessageHandler::HandleMouseMessage(unsigned int message,
bool* handled) {
// Don't track forwarded mouse messages. We expect the caller to track the
// mouse.
- base::WeakPtr<HWNDMessageHandler> ref(weak_factory_.GetWeakPtr());
+ base::WeakPtr<HWNDMessageHandler> ref(msg_handler_weak_factory_.GetWeakPtr());
LRESULT ret = HandleMouseEventInternal(message, w_param, l_param, false);
- *handled = IsMsgHandled();
+ *handled = !ref.get() || msg_handled_;
return ret;
}
@@ -1002,13 +1005,13 @@ LRESULT HWNDMessageHandler::HandleKeyboardMessage(unsigned int message,
WPARAM w_param,
LPARAM l_param,
bool* handled) {
- base::WeakPtr<HWNDMessageHandler> ref(weak_factory_.GetWeakPtr());
+ base::WeakPtr<HWNDMessageHandler> ref(msg_handler_weak_factory_.GetWeakPtr());
LRESULT ret = 0;
if ((message == WM_CHAR) || (message == WM_SYSCHAR))
ret = OnImeMessages(message, w_param, l_param);
else
ret = OnKeyEvent(message, w_param, l_param);
- *handled = IsMsgHandled();
+ *handled = !ref.get() || msg_handled_;
return ret;
}
@@ -1016,9 +1019,9 @@ LRESULT HWNDMessageHandler::HandleTouchMessage(unsigned int message,
WPARAM w_param,
LPARAM l_param,
bool* handled) {
- base::WeakPtr<HWNDMessageHandler> ref(weak_factory_.GetWeakPtr());
+ base::WeakPtr<HWNDMessageHandler> ref(msg_handler_weak_factory_.GetWeakPtr());
LRESULT ret = OnTouchEvent(message, w_param, l_param);
- *handled = IsMsgHandled();
+ *handled = !ref.get() || msg_handled_;
return ret;
}
@@ -1026,9 +1029,9 @@ LRESULT HWNDMessageHandler::HandlePointerMessage(unsigned int message,
WPARAM w_param,
LPARAM l_param,
bool* handled) {
- base::WeakPtr<HWNDMessageHandler> ref(weak_factory_.GetWeakPtr());
+ base::WeakPtr<HWNDMessageHandler> ref(msg_handler_weak_factory_.GetWeakPtr());
LRESULT ret = OnPointerEvent(message, w_param, l_param);
- *handled = IsMsgHandled();
+ *handled = !ref.get() || msg_handled_;
return ret;
}
@@ -1036,9 +1039,9 @@ LRESULT HWNDMessageHandler::HandleScrollMessage(unsigned int message,
WPARAM w_param,
LPARAM l_param,
bool* handled) {
- base::WeakPtr<HWNDMessageHandler> ref(weak_factory_.GetWeakPtr());
+ base::WeakPtr<HWNDMessageHandler> ref(msg_handler_weak_factory_.GetWeakPtr());
LRESULT ret = OnScrollMessage(message, w_param, l_param);
- *handled = IsMsgHandled();
+ *handled = !ref.get() || msg_handled_;
return ret;
}
@@ -1046,10 +1049,10 @@ LRESULT HWNDMessageHandler::HandleNcHitTestMessage(unsigned int message,
WPARAM w_param,
LPARAM l_param,
bool* handled) {
- base::WeakPtr<HWNDMessageHandler> ref(weak_factory_.GetWeakPtr());
+ base::WeakPtr<HWNDMessageHandler> ref(msg_handler_weak_factory_.GetWeakPtr());
LRESULT ret = OnNCHitTest(
gfx::Point(CR_GET_X_LPARAM(l_param), CR_GET_Y_LPARAM(l_param)));
- *handled = IsMsgHandled();
+ *handled = !ref.get() || msg_handled_;
return ret;
}
@@ -1410,7 +1413,7 @@ LRESULT HWNDMessageHandler::DefWindowProcWithRedrawLock(UINT message,
ScopedRedrawLock lock(this);
// The Widget and HWND can be destroyed in the call to DefWindowProc, so use
// the WeakPtrFactory to avoid unlocking (and crashing) after destruction.
- base::WeakPtr<HWNDMessageHandler> ref(weak_factory_.GetWeakPtr());
+ base::WeakPtr<HWNDMessageHandler> ref(msg_handler_weak_factory_.GetWeakPtr());
LRESULT result = DefWindowProc(hwnd(), message, w_param, l_param);
if (!ref)
lock.CancelUnlockOperation();
@@ -1441,7 +1444,7 @@ void HWNDMessageHandler::ForceRedrawWindow(int attempts) {
base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
FROM_HERE,
base::BindOnce(&HWNDMessageHandler::ForceRedrawWindow,
- weak_factory_.GetWeakPtr(), attempts),
+ msg_handler_weak_factory_.GetWeakPtr(), attempts),
base::TimeDelta::FromMilliseconds(500));
return;
}
@@ -1708,7 +1711,7 @@ LRESULT HWNDMessageHandler::OnImeMessages(UINT message,
WPARAM w_param,
LPARAM l_param) {
LRESULT result = 0;
- base::WeakPtr<HWNDMessageHandler> ref(weak_factory_.GetWeakPtr());
+ base::WeakPtr<HWNDMessageHandler> ref(msg_handler_weak_factory_.GetWeakPtr());
const bool msg_handled =
delegate_->HandleIMEMessage(message, w_param, l_param, &result);
if (ref.get())
@@ -1749,7 +1752,7 @@ LRESULT HWNDMessageHandler::OnKeyEvent(UINT message,
MSG msg = {
hwnd(), message, w_param, l_param, static_cast<DWORD>(GetMessageTime())};
ui::KeyEvent key(msg);
- base::WeakPtr<HWNDMessageHandler> ref(weak_factory_.GetWeakPtr());
+ base::WeakPtr<HWNDMessageHandler> ref(msg_handler_weak_factory_.GetWeakPtr());
delegate_->HandleKeyEvent(&key);
if (!ref)
return 0;
@@ -2336,21 +2339,28 @@ void HWNDMessageHandler::OnSysCommand(UINT notification_code,
((notification_code & sc_mask) == SC_MOVE) ||
((notification_code & sc_mask) == SC_MAXIMIZE)))
return;
+
+ const bool window_control_action =
+ (notification_code & sc_mask) == SC_MINIMIZE ||
+ (notification_code & sc_mask) == SC_MAXIMIZE ||
+ (notification_code & sc_mask) == SC_RESTORE;
+ const bool custom_controls_frame_mode =
+ delegate_->GetFrameMode() == FrameMode::SYSTEM_DRAWN_NO_CONTROLS ||
+ delegate_->GetFrameMode() == FrameMode::CUSTOM_DRAWN;
+ if (custom_controls_frame_mode && window_control_action)
+ delegate_->ResetWindowControls();
+
if (delegate_->GetFrameMode() == FrameMode::CUSTOM_DRAWN) {
- if ((notification_code & sc_mask) == SC_MINIMIZE ||
- (notification_code & sc_mask) == SC_MAXIMIZE ||
- (notification_code & sc_mask) == SC_RESTORE) {
- delegate_->ResetWindowControls();
- DestroyAXSystemCaret();
- } else if ((notification_code & sc_mask) == SC_MOVE ||
- (notification_code & sc_mask) == SC_SIZE) {
- if (!IsVisible()) {
- // Circumvent ScopedRedrawLocks and force visibility before entering a
- // resize or move modal loop to get continuous sizing/moving feedback.
- SetWindowLong(hwnd(), GWL_STYLE,
- GetWindowLong(hwnd(), GWL_STYLE) | WS_VISIBLE);
- }
+ const bool window_bounds_change =
+ (notification_code & sc_mask) == SC_MOVE ||
+ (notification_code & sc_mask) == SC_SIZE;
+ if (window_bounds_change || window_control_action)
DestroyAXSystemCaret();
+ if (window_bounds_change && !IsVisible()) {
+ // Circumvent ScopedRedrawLocks and force visibility before entering a
+ // resize or move modal loop to get continuous sizing/moving feedback.
+ SetWindowLong(hwnd(), GWL_STYLE,
+ GetWindowLong(hwnd(), GWL_STYLE) | WS_VISIBLE);
}
}
@@ -2376,7 +2386,8 @@ void HWNDMessageHandler::OnSysCommand(UINT notification_code,
// with the mouse/touch/keyboard, we flag as being in a size loop.
if ((notification_code & sc_mask) == SC_SIZE)
in_size_loop_ = true;
- base::WeakPtr<HWNDMessageHandler> ref(weak_factory_.GetWeakPtr());
+ base::WeakPtr<HWNDMessageHandler> ref(
+ msg_handler_weak_factory_.GetWeakPtr());
DefWindowProc(hwnd(), WM_SYSCOMMAND, notification_code,
MAKELPARAM(point.x(), point.y()));
@@ -2455,7 +2466,7 @@ LRESULT HWNDMessageHandler::OnTouchEvent(UINT message,
base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
FROM_HERE,
base::BindOnce(&HWNDMessageHandler::ResetTouchDownContext,
- weak_factory_.GetWeakPtr()),
+ msg_handler_weak_factory_.GetWeakPtr()),
base::TimeDelta::FromMilliseconds(kTouchDownContextResetTimeout));
} else {
if (input[i].dwFlags & TOUCHEVENTF_MOVE) {
@@ -2488,8 +2499,9 @@ LRESULT HWNDMessageHandler::OnTouchEvent(UINT message,
// events on windows don't fire if we enter a modal loop in the context of
// a touch event.
base::ThreadTaskRunnerHandle::Get()->PostTask(
- FROM_HERE, base::BindOnce(&HWNDMessageHandler::HandleTouchEvents,
- weak_factory_.GetWeakPtr(), touch_events));
+ FROM_HERE,
+ base::BindOnce(&HWNDMessageHandler::HandleTouchEvents,
+ msg_handler_weak_factory_.GetWeakPtr(), touch_events));
}
CloseTouchInputHandle(reinterpret_cast<HTOUCHINPUT>(l_param));
SetMsgHandled(FALSE);
@@ -2596,7 +2608,7 @@ void HWNDMessageHandler::OnWindowPosChanging(WINDOWPOS* window_pos) {
base::ThreadTaskRunnerHandle::Get()->PostTask(
FROM_HERE,
base::BindOnce(&HWNDMessageHandler::StopIgnoringPosChanges,
- weak_factory_.GetWeakPtr()));
+ msg_handler_weak_factory_.GetWeakPtr()));
}
last_monitor_ = monitor;
last_monitor_rect_ = monitor_rect;
@@ -2678,7 +2690,7 @@ void HWNDMessageHandler::OnSessionChange(WPARAM status_code) {
}
void HWNDMessageHandler::HandleTouchEvents(const TouchEvents& touch_events) {
- base::WeakPtr<HWNDMessageHandler> ref(weak_factory_.GetWeakPtr());
+ base::WeakPtr<HWNDMessageHandler> ref(msg_handler_weak_factory_.GetWeakPtr());
for (size_t i = 0; i < touch_events.size() && ref; ++i) {
ui::TouchEvent* touch_event = const_cast<ui::TouchEvent*>(&touch_events[i]);
delegate_->HandleTouchEvent(touch_event);
@@ -2693,25 +2705,28 @@ LRESULT HWNDMessageHandler::HandleMouseEventInternal(UINT message,
WPARAM w_param,
LPARAM l_param,
bool track_mouse) {
- // We handle touch events on Windows Aura. Windows generates synthesized
- // mouse messages in response to touch which we should ignore. However touch
- // messages are only received for the client area. We need to ignore the
- // synthesized mouse messages for all points in the client area and places
- // which return HTNOWHERE.
- // 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 ((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.
- 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)
+ // We handle touch events in Aura. Windows generates synthesized mouse
+ // messages whenever there's a touch, but it doesn't give us the actual touch
+ // messages if it thinks the touch point is in non-client space.
+ if (message != WM_MOUSEWHEEL && message != WM_MOUSEHWHEEL &&
+ ui::IsMouseEventFromTouch(message)) {
+ LRESULT hittest = SendMessage(hwnd(), WM_NCHITTEST, 0, l_param);
+ // Always DefWindowProc on the titlebar. We could let the event fall through
+ // and the special handling in HandleMouseInputForCaption would take care of
+ // this, but in the touch case Windows does a better job.
+ if (hittest == HTCAPTION || hittest == HTSYSMENU)
+ SetMsgHandled(FALSE);
+ // We must let Windows handle the caption buttons if it's drawing them, or
+ // they won't work.
+ if (delegate_->GetFrameMode() == FrameMode::SYSTEM_DRAWN &&
+ (hittest == HTCLOSE || hittest == HTMINBUTTON ||
+ hittest == HTMAXBUTTON)) {
+ SetMsgHandled(FALSE);
+ }
+ // Let resize events fall through. Ignore everything else, as we're either
+ // letting Windows handle it above or we've already handled the equivalent
+ // touch message.
+ if (!IsHitTestOnResizeHandle(hittest))
return 0;
}
@@ -2802,7 +2817,7 @@ LRESULT HWNDMessageHandler::HandleMouseEventInternal(UINT message,
// There are cases where the code handling the message destroys the window,
// so use the weak ptr to check if destruction occured or not.
- base::WeakPtr<HWNDMessageHandler> ref(weak_factory_.GetWeakPtr());
+ base::WeakPtr<HWNDMessageHandler> ref(msg_handler_weak_factory_.GetWeakPtr());
bool handled = delegate_->HandleMouseEvent(&event);
if (!ref.get())
@@ -2861,7 +2876,7 @@ LRESULT HWNDMessageHandler::HandlePointerEventTypeTouch(UINT message,
base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
FROM_HERE,
base::BindOnce(&HWNDMessageHandler::ResetTouchDownContext,
- weak_factory_.GetWeakPtr()),
+ msg_handler_weak_factory_.GetWeakPtr()),
base::TimeDelta::FromMilliseconds(kTouchDownContextResetTimeout));
}
@@ -2897,11 +2912,11 @@ LRESULT HWNDMessageHandler::HandlePointerEventTypeTouch(UINT message,
ui::GetModifiersFromKeyState(), rotation_angle);
event.latency()->AddLatencyNumberWithTimestamp(
- ui::INPUT_EVENT_LATENCY_ORIGINAL_COMPONENT, 0, 0, event_time, 1);
+ ui::INPUT_EVENT_LATENCY_ORIGINAL_COMPONENT, 0, event_time, 1);
// There are cases where the code handling the message destroys the
// window, so use the weak ptr to check if destruction occurred or not.
- base::WeakPtr<HWNDMessageHandler> ref(weak_factory_.GetWeakPtr());
+ base::WeakPtr<HWNDMessageHandler> ref(msg_handler_weak_factory_.GetWeakPtr());
delegate_->HandleTouchEvent(&event);
if (event_type == ui::ET_TOUCH_RELEASED)
@@ -2935,7 +2950,7 @@ LRESULT HWNDMessageHandler::HandlePointerEventTypePen(UINT message,
// There are cases where the code handling the message destroys the
// window, so use the weak ptr to check if destruction occured or not.
- base::WeakPtr<HWNDMessageHandler> ref(weak_factory_.GetWeakPtr());
+ base::WeakPtr<HWNDMessageHandler> ref(msg_handler_weak_factory_.GetWeakPtr());
if (event) {
if (event->IsTouchEvent()) {
delegate_->HandleTouchEvent(event->AsTouchEvent());
@@ -3018,7 +3033,6 @@ void HWNDMessageHandler::GenerateTouchEvent(ui::EventType event_type,
event.latency()->AddLatencyNumberWithTimestamp(
ui::INPUT_EVENT_LATENCY_ORIGINAL_COMPONENT,
0,
- 0,
time_stamp,
1);
diff --git a/chromium/ui/views/win/hwnd_message_handler.h b/chromium/ui/views/win/hwnd_message_handler.h
index 11c76b89e38..5afb32aa043 100644
--- a/chromium/ui/views/win/hwnd_message_handler.h
+++ b/chromium/ui/views/win/hwnd_message_handler.h
@@ -27,6 +27,7 @@
#include "ui/events/event.h"
#include "ui/gfx/geometry/rect.h"
#include "ui/gfx/sequential_id_generator.h"
+#include "ui/gfx/win/msg_util.h"
#include "ui/gfx/win/window_impl.h"
#include "ui/views/views_export.h"
#include "ui/views/win/pen_event_processor.h"
@@ -65,54 +66,6 @@ const int WM_NCUAHDRAWFRAME = 0xAF;
// 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
-// IsMsgHandled() from a member function to a define that checks if the weak
-// factory is still valid in addition to the member. Together these allow for
-// |this| to be deleted during dispatch.
-#define IsMsgHandled() !ref.get() || msg_handled_
-
-#define BEGIN_SAFE_MSG_MAP_EX(weak_factory) \
- private: \
- BOOL msg_handled_; \
-\
- public: \
- /* "handled" management for cracked handlers */ \
- void SetMsgHandled(BOOL handled) { \
- msg_handled_ = handled; \
- } \
- BOOL ProcessWindowMessage(HWND hwnd, \
- UINT msg, \
- WPARAM w_param, \
- LPARAM l_param, \
- LRESULT& l_result, \
- DWORD msg_map_id = 0) override { \
- base::WeakPtr<HWNDMessageHandler> ref(weak_factory.GetWeakPtr()); \
- BOOL old_msg_handled = msg_handled_; \
- BOOL ret = _ProcessWindowMessage(hwnd, msg, w_param, l_param, l_result, \
- msg_map_id); \
- if (ref.get()) \
- msg_handled_ = old_msg_handled; \
- return ret; \
- } \
- BOOL _ProcessWindowMessage(HWND hWnd, \
- UINT uMsg, \
- WPARAM wParam, \
- LPARAM lParam, \
- LRESULT& lResult, \
- DWORD dwMsgMapID) { \
- base::WeakPtr<HWNDMessageHandler> ref(weak_factory.GetWeakPtr()); \
- BOOL bHandled = TRUE; \
- hWnd; \
- uMsg; \
- wParam; \
- lParam; \
- lResult; \
- bHandled; \
- switch(dwMsgMapID) { \
- case 0:
-
// An object that handles messages for a HWND that implements the views
// "Custom Frame" look. The purpose of this class is to isolate the windows-
// specific message handling from the code that wraps it. It is intended to be
@@ -360,11 +313,10 @@ class VIEWS_EXPORT HWNDMessageHandler : public gfx::WindowImpl,
// Message Handlers ----------------------------------------------------------
- BEGIN_SAFE_MSG_MAP_EX(weak_factory_)
+ CR_BEGIN_MSG_MAP_EX(HWNDMessageHandler)
// Range handlers must go first!
CR_MESSAGE_RANGE_HANDLER_EX(WM_MOUSEFIRST, WM_MOUSELAST, OnMouseRange)
- CR_MESSAGE_RANGE_HANDLER_EX(WM_NCMOUSEMOVE,
- WM_NCXBUTTONDBLCLK,
+ CR_MESSAGE_RANGE_HANDLER_EX(WM_NCMOUSEMOVE, WM_NCXBUTTONDBLCLK,
OnMouseRange)
// CustomFrameWindow hacks
@@ -771,15 +723,15 @@ class VIEWS_EXPORT HWNDMessageHandler : public gfx::WindowImpl,
static base::LazyInstance<FullscreenWindowMonitorMap>::DestructorAtExit
fullscreen_monitor_map_;
- // The WeakPtrFactories below must occur last in the class definition so they
- // get destroyed last.
+ // The WeakPtrFactories below (one inside the
+ // CR_MSG_MAP_CLASS_DECLARATIONS macro and autohide_factory_) must
+ // occur last in the class definition so they get destroyed last.
+
+ CR_MSG_MAP_CLASS_DECLARATIONS(HWNDMessageHandler)
// The factory used to lookup appbar autohide edges.
base::WeakPtrFactory<HWNDMessageHandler> autohide_factory_;
- // The factory used with BEGIN_SAFE_MSG_MAP_EX.
- base::WeakPtrFactory<HWNDMessageHandler> weak_factory_;
-
DISALLOW_COPY_AND_ASSIGN(HWNDMessageHandler);
};
diff --git a/chromium/ui/views/win/pen_event_processor.cc b/chromium/ui/views/win/pen_event_processor.cc
index 5de8173f343..95089ddb7fa 100644
--- a/chromium/ui/views/win/pen_event_processor.cc
+++ b/chromium/ui/views/win/pen_event_processor.cc
@@ -18,12 +18,6 @@ int GetFlagsFromPointerMessage(UINT message, const POINTER_INFO& pointer_info) {
if (pointer_info.pointerFlags & POINTER_FLAG_SECONDBUTTON)
flags |= ui::EF_RIGHT_MOUSE_BUTTON;
- if (message == WM_POINTERUP) {
- if (pointer_info.ButtonChangeType == POINTER_CHANGE_SECONDBUTTON_UP)
- flags |= ui::EF_RIGHT_MOUSE_BUTTON;
- else
- flags |= ui::EF_LEFT_MOUSE_BUTTON;
- }
return flags;
}
@@ -121,10 +115,13 @@ std::unique_ptr<ui::Event> PenEventProcessor::GenerateMouseEvent(
break;
case WM_POINTERUP:
event_type = ui::ET_MOUSE_RELEASED;
- if (pointer_info.ButtonChangeType == POINTER_CHANGE_FIRSTBUTTON_UP)
+ if (pointer_info.ButtonChangeType == POINTER_CHANGE_FIRSTBUTTON_UP) {
+ flag |= ui::EF_LEFT_MOUSE_BUTTON;
changed_flag = ui::EF_LEFT_MOUSE_BUTTON;
- else
+ } else {
+ flag |= ui::EF_RIGHT_MOUSE_BUTTON;
changed_flag = ui::EF_RIGHT_MOUSE_BUTTON;
+ }
id_generator_->ReleaseNumber(pointer_id);
click_count = 1;
if (!sent_mouse_down_)
@@ -191,7 +188,7 @@ std::unique_ptr<ui::Event> PenEventProcessor::GenerateTouchEvent(
flags | ui::GetModifiersFromKeyState(), rotation_angle);
event->set_hovering(event_type == ui::ET_TOUCH_RELEASED);
event->latency()->AddLatencyNumberWithTimestamp(
- ui::INPUT_EVENT_LATENCY_ORIGINAL_COMPONENT, 0, 0, event_time, 1);
+ ui::INPUT_EVENT_LATENCY_ORIGINAL_COMPONENT, 0, event_time, 1);
return event;
}
diff --git a/chromium/ui/views/win/pen_event_processor_unittest.cc b/chromium/ui/views/win/pen_event_processor_unittest.cc
index 614e2e78260..1fa9319f1c9 100644
--- a/chromium/ui/views/win/pen_event_processor_unittest.cc
+++ b/chromium/ui/views/win/pen_event_processor_unittest.cc
@@ -164,4 +164,67 @@ TEST(PenProcessorTest, UnpairedPointerDownMouseDMEnabled) {
EXPECT_EQ(nullptr, event.get());
}
+TEST(PenProcessorTest, TouchFlagDMEnabled) {
+ ui::SequentialIDGenerator id_generator(0);
+ PenEventProcessor processor(&id_generator,
+ /*direct_manipulation_enabled*/ true);
+
+ POINTER_PEN_INFO pen_info;
+ memset(&pen_info, 0, sizeof(POINTER_PEN_INFO));
+ gfx::Point point(100, 100);
+
+ pen_info.pointerInfo.pointerFlags =
+ POINTER_FLAG_INCONTACT | POINTER_FLAG_FIRSTBUTTON;
+ pen_info.pointerInfo.ButtonChangeType = POINTER_CHANGE_FIRSTBUTTON_DOWN;
+
+ std::unique_ptr<ui::Event> event =
+ processor.GenerateEvent(WM_POINTERDOWN, 0, pen_info, point);
+ ASSERT_TRUE(event);
+ ASSERT_TRUE(event->IsTouchEvent());
+ EXPECT_EQ(ui::ET_TOUCH_PRESSED, event->AsTouchEvent()->type());
+ EXPECT_TRUE(event->flags() & ui::EF_LEFT_MOUSE_BUTTON);
+
+ pen_info.pointerInfo.pointerFlags = POINTER_FLAG_UP;
+ pen_info.pointerInfo.ButtonChangeType = POINTER_CHANGE_FIRSTBUTTON_UP;
+
+ event = processor.GenerateEvent(WM_POINTERUP, 0, pen_info, point);
+ ASSERT_TRUE(event);
+ ASSERT_TRUE(event->IsTouchEvent());
+ EXPECT_EQ(ui::ET_TOUCH_RELEASED, event->AsTouchEvent()->type());
+ EXPECT_FALSE(event->flags() & ui::EF_LEFT_MOUSE_BUTTON);
+}
+
+TEST(PenProcessorTest, MouseFlagDMEnabled) {
+ ui::SequentialIDGenerator id_generator(0);
+ PenEventProcessor processor(&id_generator,
+ /*direct_manipulation_enabled*/ true);
+
+ POINTER_PEN_INFO pen_info;
+ memset(&pen_info, 0, sizeof(POINTER_PEN_INFO));
+ gfx::Point point(100, 100);
+
+ pen_info.pointerInfo.pointerFlags = POINTER_FLAG_FIRSTBUTTON;
+ pen_info.pointerInfo.ButtonChangeType = POINTER_CHANGE_FIRSTBUTTON_DOWN;
+
+ std::unique_ptr<ui::Event> event =
+ processor.GenerateEvent(WM_POINTERDOWN, 0, pen_info, point);
+ ASSERT_TRUE(event);
+ ASSERT_TRUE(event->IsMouseEvent());
+ EXPECT_EQ(ui::ET_MOUSE_PRESSED, event->AsMouseEvent()->type());
+ EXPECT_TRUE(event->flags() & ui::EF_LEFT_MOUSE_BUTTON);
+ EXPECT_EQ(ui::EF_LEFT_MOUSE_BUTTON,
+ event->AsMouseEvent()->changed_button_flags());
+
+ pen_info.pointerInfo.pointerFlags = POINTER_FLAG_NONE;
+ pen_info.pointerInfo.ButtonChangeType = POINTER_CHANGE_FIRSTBUTTON_UP;
+
+ event = processor.GenerateEvent(WM_POINTERUP, 0, pen_info, point);
+ ASSERT_TRUE(event);
+ ASSERT_TRUE(event->IsMouseEvent());
+ EXPECT_EQ(ui::ET_MOUSE_RELEASED, event->AsMouseEvent()->type());
+ EXPECT_TRUE(event->flags() & ui::EF_LEFT_MOUSE_BUTTON);
+ EXPECT_EQ(ui::EF_LEFT_MOUSE_BUTTON,
+ event->AsMouseEvent()->changed_button_flags());
+}
+
} // namespace views
diff --git a/chromium/ui/views/window/dialog_delegate.cc b/chromium/ui/views/window/dialog_delegate.cc
index 3e88b8db4c8..739e5e548b2 100644
--- a/chromium/ui/views/window/dialog_delegate.cc
+++ b/chromium/ui/views/window/dialog_delegate.cc
@@ -42,11 +42,6 @@ DialogDelegate::DialogDelegate()
creation_time_ = base::TimeTicks::Now();
}
-DialogDelegate::~DialogDelegate() {
- UMA_HISTOGRAM_LONG_TIMES("Dialog.DialogDelegate.Duration",
- base::TimeTicks::Now() - creation_time_);
-}
-
// static
Widget* DialogDelegate::CreateDialogWidget(WidgetDelegate* delegate,
gfx::NativeWindow context,
@@ -249,6 +244,11 @@ void DialogDelegate::DialogModelChanged() {
observer.OnDialogModelChanged();
}
+DialogDelegate::~DialogDelegate() {
+ UMA_HISTOGRAM_LONG_TIMES("Dialog.DialogDelegate.Duration",
+ base::TimeTicks::Now() - creation_time_);
+}
+
ax::mojom::Role DialogDelegate::GetAccessibleWindowRole() const {
return ax::mojom::Role::kDialog;
}
diff --git a/chromium/ui/views/window/dialog_delegate.h b/chromium/ui/views/window/dialog_delegate.h
index 69add25e1a0..83019bb77d9 100644
--- a/chromium/ui/views/window/dialog_delegate.h
+++ b/chromium/ui/views/window/dialog_delegate.h
@@ -35,7 +35,6 @@ class VIEWS_EXPORT DialogDelegate : public ui::DialogModel,
public WidgetDelegate {
public:
DialogDelegate();
- ~DialogDelegate() override;
// Creates a widget at a default location.
static Widget* CreateDialogWidget(WidgetDelegate* delegate,
@@ -129,6 +128,8 @@ class VIEWS_EXPORT DialogDelegate : public ui::DialogModel,
void DialogModelChanged();
protected:
+ ~DialogDelegate() override;
+
// Overridden from WidgetDelegate:
ax::mojom::Role GetAccessibleWindowRole() const override;