diff options
author | Allan Sandfeld Jensen <allan.jensen@qt.io> | 2020-10-06 12:48:11 +0200 |
---|---|---|
committer | Allan Sandfeld Jensen <allan.jensen@qt.io> | 2020-10-13 09:33:43 +0000 |
commit | 7b5b123ac58f58ffde0f4f6e488bcd09aa4decd3 (patch) | |
tree | fa14ba0ca8d2683ba2efdabd246dc9b18a1229c6 /chromium/ui/views | |
parent | 79b4f909db1049fca459c07cca55af56a9b54fe3 (diff) | |
download | qtwebengine-chromium-7b5b123ac58f58ffde0f4f6e488bcd09aa4decd3.tar.gz |
BASELINE: Update Chromium to 84.0.4147.141
Change-Id: Ib85eb4cfa1cbe2b2b81e5022c8cad5c493969535
Reviewed-by: Allan Sandfeld Jensen <allan.jensen@qt.io>
Diffstat (limited to 'chromium/ui/views')
282 files changed, 7173 insertions, 3894 deletions
diff --git a/chromium/ui/views/BUILD.gn b/chromium/ui/views/BUILD.gn index d3fa4cd08df..bc19dfd88f9 100644 --- a/chromium/ui/views/BUILD.gn +++ b/chromium/ui/views/BUILD.gn @@ -36,7 +36,6 @@ aggregate_vector_icons("views_vector_icons") { "launch.icon", "linux_high_density.icon", "linux_low_density.icon", - "linux_shutdown.icon", "menu_check.icon", "menu_drop_arrow.icon", "menu_radio_empty.icon", @@ -648,10 +647,6 @@ jumbo_component("views") { } if (use_x11) { - configs += [ - "//build/config/linux:x11", - "//build/config/linux:xrandr", - ] deps += [ "//ui/events/devices", "//ui/events/devices/x11", @@ -755,26 +750,21 @@ jumbo_component("views") { "widget/desktop_aura/desktop_screen_position_client.cc", "widget/desktop_aura/desktop_window_tree_host.cc", ] - public_deps += [ "//ui/base/mojom:cursor_type" ] + public_deps += [ "//ui/base/cursor/mojom:cursor_type" ] if (use_x11) { - deps += [ "//ui/platform_window/x11" ] + public_deps += [ + "//ui/base/x", + "//ui/platform_window/x11", + ] public += [ "widget/desktop_aura/desktop_drag_drop_client_aurax11.h", "widget/desktop_aura/desktop_screen_x11.h", "widget/desktop_aura/desktop_window_tree_host_x11.h", - "widget/desktop_aura/x11_desktop_window_move_client.h", - "widget/desktop_aura/x11_move_loop.h", - "widget/desktop_aura/x11_move_loop_delegate.h", - "widget/desktop_aura/x11_topmost_window_finder.h", - "widget/desktop_aura/x11_whole_screen_move_loop.h", ] sources += [ "widget/desktop_aura/desktop_drag_drop_client_aurax11.cc", "widget/desktop_aura/desktop_screen_x11.cc", "widget/desktop_aura/desktop_window_tree_host_x11.cc", - "widget/desktop_aura/x11_desktop_window_move_client.cc", - "widget/desktop_aura/x11_topmost_window_finder.cc", - "widget/desktop_aura/x11_whole_screen_move_loop.cc", ] } else if (is_win) { public += [ "widget/desktop_aura/desktop_window_tree_host_win.h" ] @@ -788,6 +778,7 @@ jumbo_component("views") { ] deps += [ "//ui/events:dom_keyboard_layout" ] } else if (use_ozone) { + public += [ "widget/desktop_aura/desktop_screen_ozone.h" ] sources += [ "widget/desktop_aura/desktop_drag_drop_client_ozone.cc", "widget/desktop_aura/desktop_drag_drop_client_ozone.h", @@ -808,9 +799,14 @@ jumbo_component("views") { ] } if (is_linux || is_fuchsia) { - public += [ "widget/desktop_aura/desktop_window_tree_host_platform.h" ] - sources += - [ "widget/desktop_aura/desktop_window_tree_host_platform.cc" ] + public += [ + "widget/desktop_aura/desktop_window_tree_host_platform.h", + "widget/desktop_aura/window_move_client_platform.h", + ] + sources += [ + "widget/desktop_aura/desktop_window_tree_host_platform.cc", + "widget/desktop_aura/window_move_client_platform.cc", + ] deps += [ "//ui/platform_window/extensions" ] } if (use_atk) { @@ -856,6 +852,10 @@ jumbo_component("views") { if (is_fuchsia) { sources += [ "controls/menu/menu_config_fuchsia.cc" ] } + + if (use_ozone) { + deps += [ "//ui/ozone" ] + } } jumbo_source_set("test_support") { @@ -917,8 +917,6 @@ jumbo_source_set("test_support") { "test/test_views_delegate.h", "test/test_widget_observer.cc", "test/test_widget_observer.h", - "test/views_interactive_ui_test_base.cc", - "test/views_interactive_ui_test_base.h", "test/views_test_base.cc", "test/views_test_base.h", "test/views_test_helper.cc", @@ -927,8 +925,6 @@ jumbo_source_set("test_support") { "test/widget_test.h", "test/widget_test_api.cc", "test/widget_test_api.h", - "test/x11_property_change_waiter.cc", - "test/x11_property_change_waiter.h", "view_test_api.h", "views_test_suite.cc", "views_test_suite.h", @@ -1023,12 +1019,6 @@ jumbo_source_set("test_support") { if (ozone_platform_x11) { deps += [ "//ui/base/x" ] } - if (use_ozone || !use_x11) { - sources -= [ - "test/x11_property_change_waiter.cc", - "test/x11_property_change_waiter.h", - ] - } } test("views_unittests") { @@ -1173,6 +1163,8 @@ test("views_unittests") { "//third_party/wtl", ] + deps += [ "//ui/base:data_exchange" ] + libs = [ "imm32.lib", "oleacc.lib", @@ -1181,6 +1173,8 @@ test("views_unittests") { ] sources += [ + "accessibility/test_list_grid_view.cc", + "accessibility/test_list_grid_view.h", "accessibility/view_ax_platform_node_delegate_win_unittest.cc", "win/pen_event_processor_unittest.cc", ] @@ -1240,7 +1234,7 @@ test("views_unittests") { "//ui/accessibility:test_support", "//ui/aura", "//ui/aura:test_support", - "//ui/base/mojom:cursor_type", + "//ui/base/cursor/mojom:cursor_type", "//ui/touch_selection", "//ui/wm", "//ui/wm/public", @@ -1257,10 +1251,6 @@ test("views_unittests") { } if (use_x11) { - configs += [ - "//build/config/linux:x11", - "//build/config/linux:xext", - ] deps += [ "//ui/events/devices", "//ui/events/platform/x11", @@ -1280,6 +1270,7 @@ test("views_unittests") { "widget/desktop_aura/desktop_screen_x11_unittest.cc", "widget/desktop_aura/desktop_window_tree_host_x11_unittest.cc", ] + deps += [ "//ui/base/x:test_support" ] } if (is_linux || is_fuchsia) { sources += [ @@ -1304,6 +1295,10 @@ test("views_unittests") { [ "accessibility/view_ax_platform_node_delegate_auralinux_unittest.cc" ] configs += [ "//build/config/linux/atk" ] } + + if (use_ozone) { + deps += [ "//ui/ozone" ] + } } # This target is added as a dependency of browser interactive_ui_tests. It must @@ -1372,7 +1367,11 @@ source_set("views_interactive_ui_tests") { "widget/desktop_aura/desktop_window_tree_host_x11_interactive_uitest.cc", "widget/desktop_aura/x11_topmost_window_finder_interactive_uitest.cc", ] - deps += [ "//ui/events/platform/x11:x11" ] + deps += [ + "//ui/base/x:test_support", + "//ui/events/platform/x11:x11", + "//ui/platform_window/x11", + ] } if (is_chromeos) { diff --git a/chromium/ui/views/accessibility/accessibility_alert_window.cc b/chromium/ui/views/accessibility/accessibility_alert_window.cc index 358b295e012..4c5b0e47f60 100644 --- a/chromium/ui/views/accessibility/accessibility_alert_window.cc +++ b/chromium/ui/views/accessibility/accessibility_alert_window.cc @@ -5,7 +5,7 @@ #include "ui/views/accessibility/accessibility_alert_window.h" #include "base/strings/utf_string_conversions.h" -#include "ui/accessibility/platform/aura_window_properties.h" +#include "ui/accessibility/aura/aura_window_properties.h" #include "ui/aura/window.h" #include "ui/compositor/layer_type.h" #include "ui/views/accessibility/ax_aura_obj_cache.h" diff --git a/chromium/ui/views/accessibility/ax_aura_obj_cache.cc b/chromium/ui/views/accessibility/ax_aura_obj_cache.cc index 3197d2eec5c..b8f102137de 100644 --- a/chromium/ui/views/accessibility/ax_aura_obj_cache.cc +++ b/chromium/ui/views/accessibility/ax_aura_obj_cache.cc @@ -105,15 +105,19 @@ void AXAuraObjCache::GetTopLevelWindows( AXAuraObjWrapper* AXAuraObjCache::GetFocus() { View* focused_view = GetFocusedView(); - if (focused_view) { - const ViewAccessibility& view_accessibility = - focused_view->GetViewAccessibility(); - if (view_accessibility.FocusedVirtualChild()) - return view_accessibility.FocusedVirtualChild()->GetOrCreateWrapper(this); + while (focused_view && focused_view->GetViewAccessibility().IsIgnored()) + focused_view = focused_view->parent(); - return GetOrCreate(focused_view); + if (!focused_view) + return nullptr; + + if (focused_view->GetViewAccessibility().FocusedVirtualChild()) { + return focused_view->GetViewAccessibility() + .FocusedVirtualChild() + ->GetOrCreateWrapper(this); } - return nullptr; + + return GetOrCreate(focused_view); } void AXAuraObjCache::OnFocusedViewChanged() { diff --git a/chromium/ui/views/accessibility/ax_aura_obj_cache_unittest.cc b/chromium/ui/views/accessibility/ax_aura_obj_cache_unittest.cc index 4008179a8bc..dcdd7515034 100644 --- a/chromium/ui/views/accessibility/ax_aura_obj_cache_unittest.cc +++ b/chromium/ui/views/accessibility/ax_aura_obj_cache_unittest.cc @@ -17,7 +17,9 @@ #include "ui/aura/window.h" #include "ui/gfx/geometry/rect.h" #include "ui/gfx/geometry/size.h" +#include "ui/views/accessibility/ax_aura_obj_wrapper.h" #include "ui/views/accessibility/ax_tree_source_views.h" +#include "ui/views/accessibility/view_accessibility.h" #include "ui/views/controls/button/label_button.h" #include "ui/views/test/widget_test.h" @@ -51,6 +53,12 @@ class AXAuraObjCacheTest : public WidgetTest { public: AXAuraObjCacheTest() = default; ~AXAuraObjCacheTest() override = default; + + ui::AXNodeData GetData(AXAuraObjWrapper* wrapper) { + ui::AXNodeData data; + wrapper->Serialize(&data); + return data; + } }; TEST_F(AXAuraObjCacheTest, TestViewRemoval) { @@ -133,6 +141,68 @@ TEST_F(AXAuraObjCacheTest, ValidTree) { EXPECT_TRUE(HasNodeWithName(ax_tree, "ChildButton")); } +TEST_F(AXAuraObjCacheTest, GetFocusIsUnignoredAncestor) { + AXAuraObjCache cache; + auto widget = std::make_unique<Widget>(); + Widget::InitParams params = CreateParams(Widget::InitParams::TYPE_WINDOW); + params.bounds = gfx::Rect(0, 0, 200, 200); + params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; + params.activatable = views::Widget::InitParams::ACTIVATABLE_YES; + widget->Init(std::move(params)); + widget->Show(); + + // Note that AXAuraObjCache::GetFocusedView has some logic to force focus on + // the first child of the client view when one cannot be found from the + // FocusManager. + auto* client = widget->non_client_view()->client_view(); + ASSERT_NE(nullptr, client); + auto* client_child = client->children().front(); + ASSERT_NE(nullptr, client_child); + client_child->GetViewAccessibility().OverrideRole(ax::mojom::Role::kDialog); + + View* parent = new View(); + widget->GetRootView()->AddChildView(parent); + parent->GetViewAccessibility().OverrideRole(ax::mojom::Role::kTextField); + parent->SetFocusBehavior(View::FocusBehavior::ALWAYS); + + View* child = new View(); + parent->AddChildView(child); + child->GetViewAccessibility().OverrideRole(ax::mojom::Role::kGroup); + child->SetFocusBehavior(View::FocusBehavior::ALWAYS); + + auto* ax_widget = cache.GetOrCreate(widget.get()); + ASSERT_NE(nullptr, ax_widget); + auto* ax_client_child = cache.GetOrCreate(client_child); + ASSERT_NE(nullptr, ax_client_child); + auto* ax_parent = cache.GetOrCreate(parent); + ASSERT_NE(nullptr, ax_parent); + auto* ax_child = cache.GetOrCreate(child); + ASSERT_NE(nullptr, ax_child); + + ASSERT_EQ(nullptr, cache.GetFocus()); + cache.OnRootWindowObjCreated(widget->GetNativeWindow()); + ASSERT_EQ(ax::mojom::Role::kDialog, GetData(cache.GetFocus()).role); + ASSERT_EQ(ax_client_child, cache.GetFocus()); + + parent->RequestFocus(); + ASSERT_EQ(ax::mojom::Role::kTextField, GetData(cache.GetFocus()).role); + ASSERT_EQ(ax_parent, cache.GetFocus()); + + child->RequestFocus(); + ASSERT_EQ(ax::mojom::Role::kGroup, GetData(cache.GetFocus()).role); + ASSERT_EQ(ax_child, cache.GetFocus()); + + child->GetViewAccessibility().OverrideIsIgnored(true); + ASSERT_EQ(ax::mojom::Role::kTextField, GetData(cache.GetFocus()).role); + ASSERT_EQ(ax_parent, cache.GetFocus()); + + parent->GetViewAccessibility().OverrideIsIgnored(true); + ASSERT_EQ(ax::mojom::Role::kWindow, GetData(cache.GetFocus()).role); + ASSERT_EQ(cache.GetOrCreate(widget->GetRootView()), cache.GetFocus()); + + cache.OnRootWindowObjDestroyed(widget->GetNativeWindow()); +} + } // namespace } // namespace test } // namespace views diff --git a/chromium/ui/views/accessibility/ax_aura_obj_wrapper.h b/chromium/ui/views/accessibility/ax_aura_obj_wrapper.h index 39ef97a6c04..6410416ce2b 100644 --- a/chromium/ui/views/accessibility/ax_aura_obj_wrapper.h +++ b/chromium/ui/views/accessibility/ax_aura_obj_wrapper.h @@ -37,6 +37,7 @@ class VIEWS_EXPORT AXAuraObjWrapper { virtual void GetChildren(std::vector<AXAuraObjWrapper*>* out_children) = 0; virtual void Serialize(ui::AXNodeData* out_node_data) = 0; virtual int32_t GetUniqueId() const = 0; + virtual std::string ToString() const = 0; // Actions. virtual bool HandleAccessibleAction(const ui::AXActionData& action); diff --git a/chromium/ui/views/accessibility/ax_root_obj_wrapper.cc b/chromium/ui/views/accessibility/ax_root_obj_wrapper.cc index d741387d039..f79b6c0ec10 100644 --- a/chromium/ui/views/accessibility/ax_root_obj_wrapper.cc +++ b/chromium/ui/views/accessibility/ax_root_obj_wrapper.cc @@ -75,6 +75,10 @@ int32_t AXRootObjWrapper::GetUniqueId() const { return unique_id_.Get(); } +std::string AXRootObjWrapper::ToString() const { + return "root"; +} + void AXRootObjWrapper::OnDisplayMetricsChanged(const display::Display& display, uint32_t changed_metrics) { delegate_->OnEvent(this, ax::mojom::Event::kLoadComplete); diff --git a/chromium/ui/views/accessibility/ax_root_obj_wrapper.h b/chromium/ui/views/accessibility/ax_root_obj_wrapper.h index d259c7071d5..168138b9314 100644 --- a/chromium/ui/views/accessibility/ax_root_obj_wrapper.h +++ b/chromium/ui/views/accessibility/ax_root_obj_wrapper.h @@ -35,6 +35,7 @@ class VIEWS_EXPORT AXRootObjWrapper : public views::AXAuraObjWrapper, std::vector<views::AXAuraObjWrapper*>* out_children) override; void Serialize(ui::AXNodeData* out_node_data) override; int32_t GetUniqueId() const override; + std::string ToString() const override; private: // display::DisplayObserver: 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 f4315d4fb58..60729545314 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 @@ -196,7 +196,7 @@ WinAccessibilityCaretEventMonitor::WinEventHookThunk(HWINEVENTHOOK handle, } } // namespace -TEST_F(AXSystemCaretWinTest, DISABLED_TestOnCaretBoundsChangeInTextField) { +TEST_F(AXSystemCaretWinTest, TestOnCaretBoundsChangeInTextField) { TextfieldTestApi textfield_test_api(textfield_); Microsoft::WRL::ComPtr<IAccessible> caret_accessible; gfx::NativeWindow native_window = widget_->GetNativeWindow(); @@ -205,9 +205,12 @@ TEST_F(AXSystemCaretWinTest, DISABLED_TestOnCaretBoundsChangeInTextField) { EXPECT_HRESULT_SUCCEEDED(AccessibleObjectFromWindow( hwnd, static_cast<DWORD>(OBJID_CARET), IID_PPV_ARGS(&caret_accessible))); + gfx::Rect window_bounds = native_window->GetBoundsInScreen(); + textfield_test_api.ExecuteTextEditCommand( ui::TextEditCommand::MOVE_TO_BEGINNING_OF_DOCUMENT); - gfx::Point caret_position = textfield_test_api.GetCursorViewRect().origin(); + gfx::Point caret_position = textfield_test_api.GetCursorViewRect().origin() + + window_bounds.OffsetFromOrigin(); LONG x, y, width, height; EXPECT_EQ(S_OK, caret_accessible->accLocation(&x, &y, &width, &height, self_)); @@ -217,7 +220,8 @@ TEST_F(AXSystemCaretWinTest, DISABLED_TestOnCaretBoundsChangeInTextField) { textfield_test_api.ExecuteTextEditCommand( ui::TextEditCommand::MOVE_TO_END_OF_DOCUMENT); - gfx::Point caret_position2 = textfield_test_api.GetCursorViewRect().origin(); + gfx::Point caret_position2 = textfield_test_api.GetCursorViewRect().origin() + + window_bounds.OffsetFromOrigin(); EXPECT_NE(caret_position, caret_position2); EXPECT_EQ(S_OK, caret_accessible->accLocation(&x, &y, &width, &height, self_)); @@ -226,7 +230,7 @@ TEST_F(AXSystemCaretWinTest, DISABLED_TestOnCaretBoundsChangeInTextField) { EXPECT_EQ(1, width); } -TEST_F(AXSystemCaretWinTest, DISABLED_TestOnInputTypeChangeInTextField) { +TEST_F(AXSystemCaretWinTest, TestOnInputTypeChangeInTextField) { Microsoft::WRL::ComPtr<IAccessible> caret_accessible; gfx::NativeWindow native_window = widget_->GetNativeWindow(); ASSERT_NE(nullptr, native_window); @@ -255,7 +259,7 @@ TEST_F(AXSystemCaretWinTest, DISABLED_TestOnInputTypeChangeInTextField) { EXPECT_EQ(height, height2); } -TEST_F(AXSystemCaretWinTest, DISABLED_TestMovingWindow) { +TEST_F(AXSystemCaretWinTest, TestMovingWindow) { Microsoft::WRL::ComPtr<IAccessible> caret_accessible; gfx::NativeWindow native_window = widget_->GetNativeWindow(); ASSERT_NE(nullptr, native_window); @@ -268,9 +272,6 @@ TEST_F(AXSystemCaretWinTest, DISABLED_TestMovingWindow) { widget_->SetBounds(gfx::Rect(100, 100, 500, 500)); LONG x2, y2, width2, height2; - caret_accessible.Reset(); - EXPECT_HRESULT_SUCCEEDED(AccessibleObjectFromWindow( - hwnd, static_cast<DWORD>(OBJID_CARET), IID_PPV_ARGS(&caret_accessible))); EXPECT_EQ(S_OK, caret_accessible->accLocation(&x2, &y2, &width2, &height2, self_)); EXPECT_NE(x, x2); @@ -281,13 +282,14 @@ TEST_F(AXSystemCaretWinTest, DISABLED_TestMovingWindow) { // Try maximizing the window. SendMessage(hwnd, WM_SYSCOMMAND, SC_MAXIMIZE, 0); - LONG x3, y3, width3, height3; - EXPECT_HRESULT_FAILED( - caret_accessible->accLocation(&x3, &y3, &width3, &height3, self_)); - caret_accessible.Reset(); + // On Win7, maximizing the window causes our caret object to get destroyed and + // re-created, so re-acquire it. + caret_accessible.Reset(); EXPECT_HRESULT_SUCCEEDED(AccessibleObjectFromWindow( hwnd, static_cast<DWORD>(OBJID_CARET), IID_PPV_ARGS(&caret_accessible))); + + LONG x3, y3, width3, height3; EXPECT_EQ(S_OK, caret_accessible->accLocation(&x3, &y3, &width3, &height3, self_)); EXPECT_NE(x2, x3); @@ -297,7 +299,7 @@ TEST_F(AXSystemCaretWinTest, DISABLED_TestMovingWindow) { EXPECT_EQ(height, height3); } -TEST_F(AXSystemCaretWinTest, DISABLED_TestCaretMSAAEvents) { +TEST_F(AXSystemCaretWinTest, TestCaretMSAAEvents) { TextfieldTestApi textfield_test_api(textfield_); Microsoft::WRL::ComPtr<IAccessible> caret_accessible; gfx::NativeWindow native_window = widget_->GetNativeWindow(); diff --git a/chromium/ui/views/accessibility/ax_tree_source_views.cc b/chromium/ui/views/accessibility/ax_tree_source_views.cc index fbbca160b51..8733f5e1ebe 100644 --- a/chromium/ui/views/accessibility/ax_tree_source_views.cc +++ b/chromium/ui/views/accessibility/ax_tree_source_views.cc @@ -117,6 +117,10 @@ AXAuraObjWrapper* AXTreeSourceViews::GetNull() const { return nullptr; } +std::string AXTreeSourceViews::GetDebugString(AXAuraObjWrapper* node) const { + return node->ToString(); +} + void AXTreeSourceViews::SerializeNode(AXAuraObjWrapper* node, ui::AXNodeData* out_data) const { node->Serialize(out_data); diff --git a/chromium/ui/views/accessibility/ax_tree_source_views.h b/chromium/ui/views/accessibility/ax_tree_source_views.h index 2640ea24442..37317b530b5 100644 --- a/chromium/ui/views/accessibility/ax_tree_source_views.h +++ b/chromium/ui/views/accessibility/ax_tree_source_views.h @@ -54,6 +54,7 @@ class VIEWS_EXPORT AXTreeSourceViews bool IsValid(AXAuraObjWrapper* node) const override; bool IsEqual(AXAuraObjWrapper* node1, AXAuraObjWrapper* node2) const override; AXAuraObjWrapper* GetNull() const override; + std::string GetDebugString(AXAuraObjWrapper* node) const override; void SerializeNode(AXAuraObjWrapper* node, ui::AXNodeData* out_data) const override; diff --git a/chromium/ui/views/accessibility/ax_view_obj_wrapper.cc b/chromium/ui/views/accessibility/ax_view_obj_wrapper.cc index 64b0303bef6..53845347297 100644 --- a/chromium/ui/views/accessibility/ax_view_obj_wrapper.cc +++ b/chromium/ui/views/accessibility/ax_view_obj_wrapper.cc @@ -92,6 +92,10 @@ bool AXViewObjWrapper::HandleAccessibleAction(const ui::AXActionData& action) { return view_ ? view_->HandleAccessibleAction(action) : false; } +std::string AXViewObjWrapper::ToString() const { + return std::string(view_ ? view_->GetClassName() : "Null view"); +} + void AXViewObjWrapper::OnViewIsDeleting(View* observed_view) { observer_.RemoveAll(); view_ = nullptr; diff --git a/chromium/ui/views/accessibility/ax_view_obj_wrapper.h b/chromium/ui/views/accessibility/ax_view_obj_wrapper.h index 02717a0ebc1..758381fbf49 100644 --- a/chromium/ui/views/accessibility/ax_view_obj_wrapper.h +++ b/chromium/ui/views/accessibility/ax_view_obj_wrapper.h @@ -35,6 +35,7 @@ class AXViewObjWrapper : public AXAuraObjWrapper, public ViewObserver { void Serialize(ui::AXNodeData* out_node_data) override; int32_t GetUniqueId() const final; bool HandleAccessibleAction(const ui::AXActionData& action) override; + std::string ToString() const override; // ViewObserver overrides. void OnViewIsDeleting(View* observed_view) override; diff --git a/chromium/ui/views/accessibility/ax_virtual_view.cc b/chromium/ui/views/accessibility/ax_virtual_view.cc index cf273bc4d05..a222443d37e 100644 --- a/chromium/ui/views/accessibility/ax_virtual_view.cc +++ b/chromium/ui/views/accessibility/ax_virtual_view.cc @@ -11,11 +11,13 @@ #include <utility> #include "base/callback.h" +#include "base/containers/adapters.h" #include "base/no_destructor.h" #include "build/build_config.h" #include "ui/accessibility/ax_action_data.h" #include "ui/accessibility/ax_tree_data.h" #include "ui/accessibility/platform/ax_platform_node.h" +#include "ui/base/layout.h" #include "ui/base/ui_base_types.h" #include "ui/gfx/geometry/rect_conversions.h" #include "ui/views/accessibility/view_accessibility.h" @@ -236,6 +238,15 @@ const ui::AXNodeData& AXVirtualView::GetData() const { if (populate_data_callback_ && GetOwnerView()) populate_data_callback_.Run(&node_data); + + // According to the ARIA spec, the node should not be ignored if it is + // focusable. This is to ensure that the focusable node is both understandable + // and operable. + if (node_data.HasState(ax::mojom::State::kIgnored) && + node_data.HasState(ax::mojom::State::kFocusable)) { + node_data.RemoveState(ax::mojom::State::kIgnored); + } + return node_data; } @@ -302,12 +313,24 @@ gfx::Rect AXVirtualView::GetBoundsRect( const ui::AXCoordinateSystem coordinate_system, const ui::AXClippingBehavior clipping_behavior, ui::AXOffscreenResult* offscreen_result) const { + // We could optionally add clipping here if ever needed. + // TODO(nektar): Implement bounds that are relative to the parent. + gfx::Rect bounds = gfx::ToEnclosingRect(GetData().relative_bounds.bounds); + View* owner_view = GetOwnerView(); + if (owner_view && owner_view->GetWidget()) + View::ConvertRectToScreen(owner_view, &bounds); switch (coordinate_system) { case ui::AXCoordinateSystem::kScreenDIPs: - // We could optionally add clipping here if ever needed. - // TODO(nektar): Implement bounds that are relative to the parent. - return gfx::ToEnclosingRect(custom_data_.relative_bounds.bounds); - case ui::AXCoordinateSystem::kScreenPhysicalPixels: + return bounds; + case ui::AXCoordinateSystem::kScreenPhysicalPixels: { + float scale_factor = 1.0; + if (owner_view && owner_view->GetWidget()) { + gfx::NativeView native_view = owner_view->GetWidget()->GetNativeView(); + if (native_view) + scale_factor = ui::GetScaleFactorForNativeView(native_view); + } + return gfx::ScaleToEnclosingRect(bounds, scale_factor); + } case ui::AXCoordinateSystem::kRootFrame: case ui::AXCoordinateSystem::kFrame: NOTIMPLEMENTED(); @@ -318,27 +341,39 @@ gfx::Rect AXVirtualView::GetBoundsRect( gfx::NativeViewAccessible AXVirtualView::HitTestSync( int screen_physical_pixel_x, int screen_physical_pixel_y) const { - if (custom_data_.relative_bounds.bounds.Contains( - static_cast<float>(screen_physical_pixel_x), - static_cast<float>(screen_physical_pixel_y))) { - if (!IsIgnored()) - return GetNativeObject(); - } + const ui::AXNodeData& node_data = GetData(); + if (node_data.HasState(ax::mojom::State::kInvisible)) + return nullptr; // Check if the point is within any of the virtual children of this view. // AXVirtualView's HitTestSync is a recursive function that will return the // deepest child, since it does not support relative bounds. - for (const std::unique_ptr<AXVirtualView>& child : children_) { + // Search the greater indices first, since they're on top in the z-order. + for (const std::unique_ptr<AXVirtualView>& child : + base::Reversed(children_)) { gfx::NativeViewAccessible result = child->HitTestSync(screen_physical_pixel_x, screen_physical_pixel_y); if (result) return result; } + + // If it's not inside any of our virtual children, and it's inside the bounds + // of this virtual view, then it's inside this virtual view. + gfx::Rect bounds_in_screen_physical_pixels = + GetBoundsRect(ui::AXCoordinateSystem::kScreenPhysicalPixels, + ui::AXClippingBehavior::kUnclipped); + if (bounds_in_screen_physical_pixels.Contains( + static_cast<float>(screen_physical_pixel_x), + static_cast<float>(screen_physical_pixel_y)) && + !node_data.IsIgnored()) { + return GetNativeObject(); + } + return nullptr; } gfx::NativeViewAccessible AXVirtualView::GetFocus() { - auto* owner_view = GetOwnerView(); + View* owner_view = GetOwnerView(); if (owner_view) { if (!(owner_view->HasFocus())) { return nullptr; @@ -389,15 +424,7 @@ gfx::AcceleratedWidget AXVirtualView::GetTargetForNativeAccessibilityEvent() { } bool AXVirtualView::IsIgnored() const { - const ui::AXNodeData& node_data = GetData(); - - // According to the ARIA spec, the node should not be ignored if it is - // focusable. This is to ensure that the focusable node is both understandable - // and operable. - if (node_data.HasState(ax::mojom::State::kFocusable)) - return false; - - return node_data.IsIgnored(); + return GetData().IsIgnored(); } bool AXVirtualView::HandleAccessibleAction( diff --git a/chromium/ui/views/accessibility/ax_virtual_view.h b/chromium/ui/views/accessibility/ax_virtual_view.h index c74273c6ac8..105c961b102 100644 --- a/chromium/ui/views/accessibility/ax_virtual_view.h +++ b/chromium/ui/views/accessibility/ax_virtual_view.h @@ -142,7 +142,7 @@ class VIEWS_EXPORT AXVirtualView : public ui::AXPlatformNodeDelegateBase { gfx::Rect GetBoundsRect( const ui::AXCoordinateSystem coordinate_system, const ui::AXClippingBehavior clipping_behavior, - ui::AXOffscreenResult* offscreen_result) const override; + ui::AXOffscreenResult* offscreen_result = nullptr) const override; gfx::NativeViewAccessible HitTestSync( int screen_physical_pixel_x, int screen_physical_pixel_y) const override; diff --git a/chromium/ui/views/accessibility/ax_virtual_view_unittest.cc b/chromium/ui/views/accessibility/ax_virtual_view_unittest.cc index 3f1cb20f077..b48ada53153 100644 --- a/chromium/ui/views/accessibility/ax_virtual_view_unittest.cc +++ b/chromium/ui/views/accessibility/ax_virtual_view_unittest.cc @@ -626,6 +626,51 @@ TEST_F(AXVirtualViewTest, Navigation) { EXPECT_EQ(0, virtual_child_4->GetIndexInParent()); } +TEST_F(AXVirtualViewTest, HitTesting) { + ASSERT_EQ(0, virtual_label_->GetChildCount()); + + const gfx::Vector2d offset_from_origin = + button_->GetBoundsInScreen().OffsetFromOrigin(); + + // Test that hit testing is recursive. + AXVirtualView* virtual_child_1 = new AXVirtualView; + virtual_child_1->GetCustomData().relative_bounds.bounds = + gfx::RectF(0, 0, 10, 10); + virtual_label_->AddChildView(base::WrapUnique(virtual_child_1)); + AXVirtualView* virtual_child_2 = new AXVirtualView; + virtual_child_2->GetCustomData().relative_bounds.bounds = + gfx::RectF(5, 5, 5, 5); + virtual_child_1->AddChildView(base::WrapUnique(virtual_child_2)); + gfx::Point point_1 = gfx::Point(2, 2) + offset_from_origin; + EXPECT_EQ(virtual_child_1->GetNativeObject(), + virtual_child_1->HitTestSync(point_1.x(), point_1.y())); + gfx::Point point_2 = gfx::Point(7, 7) + offset_from_origin; + EXPECT_EQ(virtual_child_2->GetNativeObject(), + virtual_label_->HitTestSync(point_2.x(), point_2.y())); + + // Test that hit testing follows the z-order. + AXVirtualView* virtual_child_3 = new AXVirtualView; + virtual_child_3->GetCustomData().relative_bounds.bounds = + gfx::RectF(5, 5, 10, 10); + virtual_label_->AddChildView(base::WrapUnique(virtual_child_3)); + AXVirtualView* virtual_child_4 = new AXVirtualView; + virtual_child_4->GetCustomData().relative_bounds.bounds = + gfx::RectF(10, 10, 10, 10); + virtual_child_3->AddChildView(base::WrapUnique(virtual_child_4)); + EXPECT_EQ(virtual_child_3->GetNativeObject(), + virtual_label_->HitTestSync(point_2.x(), point_2.y())); + gfx::Point point_3 = gfx::Point(12, 12) + offset_from_origin; + EXPECT_EQ(virtual_child_4->GetNativeObject(), + virtual_label_->HitTestSync(point_3.x(), point_3.y())); + + // Test that hit testing skips ignored nodes but not their descendants. + virtual_child_3->GetCustomData().AddState(ax::mojom::State::kIgnored); + EXPECT_EQ(virtual_child_2->GetNativeObject(), + virtual_label_->HitTestSync(point_2.x(), point_2.y())); + EXPECT_EQ(virtual_child_4->GetNativeObject(), + virtual_label_->HitTestSync(point_3.x(), point_3.y())); +} + // Test for GetTargetForNativeAccessibilityEvent(). #if defined(OS_WIN) TEST_F(AXVirtualViewTest, GetTargetForEvents) { diff --git a/chromium/ui/views/accessibility/ax_virtual_view_wrapper.cc b/chromium/ui/views/accessibility/ax_virtual_view_wrapper.cc index 65a7b57edac..dffc373da1a 100644 --- a/chromium/ui/views/accessibility/ax_virtual_view_wrapper.cc +++ b/chromium/ui/views/accessibility/ax_virtual_view_wrapper.cc @@ -51,4 +51,9 @@ bool AXVirtualViewWrapper::HandleAccessibleAction( return virtual_view_->HandleAccessibleAction(action); } +std::string AXVirtualViewWrapper::ToString() const { + std::string description = "Virtual view child of "; + return description + virtual_view_->GetOwnerView()->GetClassName(); +} + } // namespace views diff --git a/chromium/ui/views/accessibility/ax_virtual_view_wrapper.h b/chromium/ui/views/accessibility/ax_virtual_view_wrapper.h index f6121f041b3..7588a5ffbba 100644 --- a/chromium/ui/views/accessibility/ax_virtual_view_wrapper.h +++ b/chromium/ui/views/accessibility/ax_virtual_view_wrapper.h @@ -31,6 +31,7 @@ class AXVirtualViewWrapper : public AXAuraObjWrapper { void Serialize(ui::AXNodeData* out_node_data) override; int32_t GetUniqueId() const override; bool HandleAccessibleAction(const ui::AXActionData& action) override; + std::string ToString() const override; private: // Weak. diff --git a/chromium/ui/views/accessibility/ax_widget_obj_wrapper.cc b/chromium/ui/views/accessibility/ax_widget_obj_wrapper.cc index e9a4012c6e8..9aeeb45a0bc 100644 --- a/chromium/ui/views/accessibility/ax_widget_obj_wrapper.cc +++ b/chromium/ui/views/accessibility/ax_widget_obj_wrapper.cc @@ -62,6 +62,10 @@ int32_t AXWidgetObjWrapper::GetUniqueId() const { return unique_id_.Get(); } +std::string AXWidgetObjWrapper::ToString() const { + return "Widget"; +} + void AXWidgetObjWrapper::OnWidgetDestroying(Widget* widget) { aura_obj_cache_->Remove(widget); } diff --git a/chromium/ui/views/accessibility/ax_widget_obj_wrapper.h b/chromium/ui/views/accessibility/ax_widget_obj_wrapper.h index e5671607a61..2f6cc93f1ef 100644 --- a/chromium/ui/views/accessibility/ax_widget_obj_wrapper.h +++ b/chromium/ui/views/accessibility/ax_widget_obj_wrapper.h @@ -36,6 +36,7 @@ class AXWidgetObjWrapper : public AXAuraObjWrapper, void GetChildren(std::vector<AXAuraObjWrapper*>* out_children) override; void Serialize(ui::AXNodeData* out_node_data) override; int32_t GetUniqueId() const final; + std::string ToString() const override; // WidgetObserver overrides. void OnWidgetDestroying(Widget* widget) override; diff --git a/chromium/ui/views/accessibility/ax_window_obj_wrapper.cc b/chromium/ui/views/accessibility/ax_window_obj_wrapper.cc index 8a6a8c6a83d..5e79b94b22c 100644 --- a/chromium/ui/views/accessibility/ax_window_obj_wrapper.cc +++ b/chromium/ui/views/accessibility/ax_window_obj_wrapper.cc @@ -10,10 +10,10 @@ #include <vector> #include "base/strings/utf_string_conversions.h" +#include "ui/accessibility/aura/aura_window_properties.h" #include "ui/accessibility/ax_enums.mojom.h" #include "ui/accessibility/ax_node_data.h" #include "ui/accessibility/ax_tree_id.h" -#include "ui/accessibility/platform/aura_window_properties.h" #include "ui/aura/client/focus_client.h" #include "ui/views/accessibility/ax_aura_obj_cache.h" #include "ui/views/widget/widget.h" @@ -66,6 +66,13 @@ void FireLocationChangesRecursively(aura::Window* window, FireLocationChangesRecursively(child, cache); } +std::string GetWindowName(aura::Window* window) { + std::string class_name = window->GetName(); + if (class_name.empty()) + class_name = "aura::Window"; + return class_name; +} + } // namespace AXWindowObjWrapper::AXWindowObjWrapper(AXAuraObjCache* aura_obj_cache, @@ -136,17 +143,18 @@ void AXWindowObjWrapper::Serialize(ui::AXNodeData* out_node_data) { *child_ax_tree_id_ptr); } - std::string class_name = window_->GetName(); - if (class_name.empty()) - class_name = "aura::Window"; out_node_data->AddStringAttribute(ax::mojom::StringAttribute::kClassName, - class_name); + GetWindowName(window_)); } int32_t AXWindowObjWrapper::GetUniqueId() const { return unique_id_.Get(); } +std::string AXWindowObjWrapper::ToString() const { + return GetWindowName(window_); +} + void AXWindowObjWrapper::OnWindowDestroyed(aura::Window* window) { aura_obj_cache_->Remove(window, nullptr); } diff --git a/chromium/ui/views/accessibility/ax_window_obj_wrapper.h b/chromium/ui/views/accessibility/ax_window_obj_wrapper.h index 1877e2ebc86..841a3d03710 100644 --- a/chromium/ui/views/accessibility/ax_window_obj_wrapper.h +++ b/chromium/ui/views/accessibility/ax_window_obj_wrapper.h @@ -35,6 +35,7 @@ class AXWindowObjWrapper : public AXAuraObjWrapper, void GetChildren(std::vector<AXAuraObjWrapper*>* out_children) override; void Serialize(ui::AXNodeData* out_node_data) override; int32_t GetUniqueId() const final; + std::string ToString() const override; // WindowObserver overrides. void OnWindowDestroyed(aura::Window* window) override; diff --git a/chromium/ui/views/accessibility/test_list_grid_view.cc b/chromium/ui/views/accessibility/test_list_grid_view.cc new file mode 100644 index 00000000000..031a1194dbf --- /dev/null +++ b/chromium/ui/views/accessibility/test_list_grid_view.cc @@ -0,0 +1,57 @@ +// Copyright 2020 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/accessibility/test_list_grid_view.h" + +#include "ui/accessibility/ax_enums.mojom.h" +#include "ui/accessibility/ax_node_data.h" + +namespace views { +namespace test { + +TestListGridView::TestListGridView() = default; +TestListGridView::~TestListGridView() = default; + +void TestListGridView::GetAccessibleNodeData(ui::AXNodeData* node_data) { + node_data->role = ax::mojom::Role::kListGrid; + if (aria_row_count) { + node_data->AddIntAttribute(ax::mojom::IntAttribute::kAriaRowCount, + *aria_row_count); + } + if (aria_column_count) { + node_data->AddIntAttribute(ax::mojom::IntAttribute::kAriaColumnCount, + *aria_column_count); + } + if (table_row_count) { + node_data->AddIntAttribute(ax::mojom::IntAttribute::kTableRowCount, + *table_row_count); + } + if (table_column_count) { + node_data->AddIntAttribute(ax::mojom::IntAttribute::kTableColumnCount, + *table_column_count); + } +} + +void TestListGridView::SetAriaTableSize(int row_count, int column_count) { + aria_row_count = base::make_optional(row_count); + aria_column_count = base::make_optional(column_count); +} + +void TestListGridView::SetTableSize(int row_count, int column_count) { + table_row_count = base::make_optional(row_count); + table_column_count = base::make_optional(column_count); +} + +void TestListGridView::UnsetAriaTableSize() { + aria_row_count = base::nullopt; + aria_column_count = base::nullopt; +} + +void TestListGridView::UnsetTableSize() { + table_row_count = base::nullopt; + table_column_count = base::nullopt; +} + +} // namespace test +} // namespace views diff --git a/chromium/ui/views/accessibility/test_list_grid_view.h b/chromium/ui/views/accessibility/test_list_grid_view.h new file mode 100644 index 00000000000..8c742ba1010 --- /dev/null +++ b/chromium/ui/views/accessibility/test_list_grid_view.h @@ -0,0 +1,40 @@ +// Copyright 2020 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_ACCESSIBILITY_TEST_LIST_GRID_VIEW_H_ +#define UI_VIEWS_ACCESSIBILITY_TEST_LIST_GRID_VIEW_H_ + +#include "ui/views/view.h" + +namespace ui { +struct AXNodeData; +} // namespace ui + +namespace views { +namespace test { + +// Class used for testing row and column count accessibility APIs. +class TestListGridView : public View { + public: + TestListGridView(); + ~TestListGridView() override; + + void GetAccessibleNodeData(ui::AXNodeData* node_data) override; + + void SetAriaTableSize(int row_count, int column_count); + void SetTableSize(int row_count, int column_count); + void UnsetAriaTableSize(); + void UnsetTableSize(); + + private: + base::Optional<int> aria_row_count = base::nullopt; + base::Optional<int> aria_column_count = base::nullopt; + base::Optional<int> table_row_count = base::nullopt; + base::Optional<int> table_column_count = base::nullopt; +}; + +} // namespace test +} // namespace views + +#endif // UI_VIEWS_ACCESSIBILITY_TEST_LIST_GRID_VIEW_H_ diff --git a/chromium/ui/views/accessibility/view_ax_platform_node_delegate_win_unittest.cc b/chromium/ui/views/accessibility/view_ax_platform_node_delegate_win_unittest.cc index 8061724c841..797c0eb5482 100644 --- a/chromium/ui/views/accessibility/view_ax_platform_node_delegate_win_unittest.cc +++ b/chromium/ui/views/accessibility/view_ax_platform_node_delegate_win_unittest.cc @@ -12,6 +12,9 @@ #include "base/win/scoped_bstr.h" #include "base/win/scoped_variant.h" #include "third_party/iaccessible2/ia2_api_all.h" +#include "ui/accessibility/ax_constants.mojom.h" +#include "ui/accessibility/platform/ax_platform_node_win.h" +#include "ui/views/accessibility/test_list_grid_view.h" #include "ui/views/controls/label.h" #include "ui/views/controls/scroll_view.h" #include "ui/views/controls/textfield/textfield.h" @@ -407,5 +410,82 @@ TEST_F(ViewAXPlatformNodeDelegateWinTest, Overrides) { alert_accessible->get_accChild(child_index, &child_dispatch)); ASSERT_EQ(child_dispatch.Get(), nullptr); } + +TEST_F(ViewAXPlatformNodeDelegateWinTest, GridRowColumnCount) { + Widget widget; + Widget::InitParams init_params = CreateParams(Widget::InitParams::TYPE_POPUP); + init_params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; + widget.Init(std::move(init_params)); + + View* content = new View; + widget.SetContentsView(content); + TestListGridView* grid = new TestListGridView(); + content->AddChildView(grid); + + Microsoft::WRL::ComPtr<IGridProvider> grid_provider; + EXPECT_HRESULT_SUCCEEDED( + grid->GetViewAccessibility().GetNativeObject()->QueryInterface( + __uuidof(IGridProvider), &grid_provider)); + + // If set, aria row/column count takes precedence over table row/column count. + // Expect E_UNEXPECTED if the result is kUnknownAriaColumnOrRowCount (-1) or + // if neither is set. + int row_count; + int column_count; + + // aria row/column count = not set + // table row/column count = not set + grid->UnsetAriaTableSize(); + grid->UnsetTableSize(); + EXPECT_HRESULT_SUCCEEDED(grid_provider->get_RowCount(&row_count)); + EXPECT_HRESULT_SUCCEEDED(grid_provider->get_ColumnCount(&column_count)); + EXPECT_EQ(0, row_count); + EXPECT_EQ(0, column_count); + // To do still: When nothing is set, currently + // AXPlatformNodeDelegateBase::GetTable{Row/Col}Count() returns 0 Should it + // return base::nullopt if the attribute is not set? Like + // GetTableAria{Row/Col}Count() + // EXPECT_EQ(E_UNEXPECTED, grid_provider->get_RowCount(&row_count)); + + // aria row/column count = 2 + // table row/column count = not set + grid->SetAriaTableSize(2, 2); + EXPECT_HRESULT_SUCCEEDED(grid_provider->get_RowCount(&row_count)); + EXPECT_HRESULT_SUCCEEDED(grid_provider->get_ColumnCount(&column_count)); + EXPECT_EQ(2, row_count); + EXPECT_EQ(2, column_count); + + // aria row/column count = kUnknownAriaColumnOrRowCount + // table row/column count = not set + grid->SetAriaTableSize(ax::mojom::kUnknownAriaColumnOrRowCount, + ax::mojom::kUnknownAriaColumnOrRowCount); + EXPECT_EQ(E_UNEXPECTED, grid_provider->get_RowCount(&row_count)); + EXPECT_EQ(E_UNEXPECTED, grid_provider->get_ColumnCount(&column_count)); + + // aria row/column count = 3 + // table row/column count = 4 + grid->SetAriaTableSize(3, 3); + grid->SetTableSize(4, 4); + EXPECT_HRESULT_SUCCEEDED(grid_provider->get_RowCount(&row_count)); + EXPECT_HRESULT_SUCCEEDED(grid_provider->get_ColumnCount(&column_count)); + EXPECT_EQ(3, row_count); + EXPECT_EQ(3, column_count); + + // aria row/column count = not set + // table row/column count = 4 + grid->UnsetAriaTableSize(); + grid->SetTableSize(4, 4); + EXPECT_HRESULT_SUCCEEDED(grid_provider->get_RowCount(&row_count)); + EXPECT_HRESULT_SUCCEEDED(grid_provider->get_ColumnCount(&column_count)); + EXPECT_EQ(4, row_count); + EXPECT_EQ(4, column_count); + + // aria row/column count = not set + // table row/column count = kUnknownAriaColumnOrRowCount + grid->SetTableSize(ax::mojom::kUnknownAriaColumnOrRowCount, + ax::mojom::kUnknownAriaColumnOrRowCount); + EXPECT_EQ(E_UNEXPECTED, grid_provider->get_RowCount(&row_count)); + EXPECT_EQ(E_UNEXPECTED, grid_provider->get_ColumnCount(&column_count)); +} } // namespace test } // namespace views diff --git a/chromium/ui/views/accessibility/views_ax_tree_manager.cc b/chromium/ui/views/accessibility/views_ax_tree_manager.cc index 15954cf453f..2b30e23ceef 100644 --- a/chromium/ui/views/accessibility/views_ax_tree_manager.cc +++ b/chromium/ui/views/accessibility/views_ax_tree_manager.cc @@ -8,8 +8,9 @@ #include "base/bind.h" #include "base/callback.h" +#include "base/check.h" #include "base/location.h" -#include "base/logging.h" +#include "base/notreached.h" #include "base/threading/sequenced_task_runner_handle.h" #include "ui/accessibility/ax_action_data.h" #include "ui/accessibility/ax_enums.mojom.h" diff --git a/chromium/ui/views/animation/bounds_animator.cc b/chromium/ui/views/animation/bounds_animator.cc index 64fda4d79b2..cf2598680d4 100644 --- a/chromium/ui/views/animation/bounds_animator.cc +++ b/chromium/ui/views/animation/bounds_animator.cc @@ -44,9 +44,21 @@ void BoundsAnimator::AnimateViewTo( Data existing_data; if (IsAnimating(view)) { - // Don't immediately delete the animation, that might trigger a callback - // from the animation container. - existing_data = RemoveFromMaps(view); + DCHECK(base::Contains(data_, view)); + const bool used_transforms = data_[view].target_transform.has_value(); + if (used_transforms) { + // Using transforms means a view does not have the proper bounds until an + // animation is complete or canceled. So here we cancel the animation so + // that the bounds can be updated. Note that this means that callers who + // want to set bounds (i.e. View::SetBoundsRect()) directly before calling + // this function will have to explicitly call StopAnimatingView() before + // doing so. + StopAnimatingView(view); + } else { + // Don't immediately delete the animation, that might trigger a callback + // from the animation container. + existing_data = RemoveFromMaps(view); + } } // NOTE: we don't check if the view is already at the target location. Doing @@ -60,12 +72,20 @@ void BoundsAnimator::AnimateViewTo( data.animation = CreateAnimation(); data.delegate = std::move(delegate); - if (use_transforms_ && !data.start_bounds.IsEmpty()) { + // If the start bounds are empty we cannot derive a transform from start to + // target. Views with existing transforms are not supported. Default back to + // using the bounds update animation in these cases. + if (use_transforms_ && !data.start_bounds.IsEmpty() && + view->GetTransform().IsIdentity()) { // Calculate the target transform. Note that we don't reset the transform if // there already was one, otherwise users will end up with visual bounds // different than what they set. + // Note that View::SetTransform() does not handle RTL, which is different + // from View::SetBounds(). So mirror the start bounds and target bounds + // manually if necessary. const gfx::Transform target_transform = gfx::TransformBetweenRects( - gfx::RectF(data.start_bounds), gfx::RectF(data.target_bounds)); + gfx::RectF(parent_->GetMirroredRect(data.start_bounds)), + gfx::RectF(parent_->GetMirroredRect(data.target_bounds))); data.target_transform = target_transform; } @@ -89,28 +109,6 @@ gfx::Rect BoundsAnimator::GetTargetBounds(const View* view) const { return (i == data_.end()) ? view->bounds() : i->second.target_bounds; } -void BoundsAnimator::SetAnimationForView( - View* view, - std::unique_ptr<gfx::SlideAnimation> animation) { - DCHECK(animation); - - const auto i = data_.find(view); - if (i == data_.end()) - return; - - // We delay deleting the animation until the end so that we don't prematurely - // send out notification that we're done. - std::unique_ptr<gfx::Animation> old_animation = ResetAnimationForView(view); - - gfx::SlideAnimation* animation_ptr = animation.get(); - i->second.animation = std::move(animation); - animation_to_view_[animation_ptr] = view; - - animation_ptr->set_delegate(this); - animation_ptr->SetContainer(container_.get()); - animation_ptr->Show(); -} - const gfx::SlideAnimation* BoundsAnimator::GetAnimationForView(View* view) { const auto i = data_.find(view); return (i == data_.end()) ? nullptr : i->second.animation.get(); @@ -224,10 +222,20 @@ void BoundsAnimator::AnimationEndedOrCanceled(const gfx::Animation* animation, Data data = RemoveFromMaps(view); if (data.target_transform) { - // Set the bounds at the end of the animation and reset the transform. - view->SetTransform(gfx::Transform()); - if (type == AnimationEndType::kEnded) + if (type == AnimationEndType::kEnded) { + // Set the bounds at the end of the animation and reset the transform. view->SetBoundsRect(data.target_bounds); + } else { + DCHECK_EQ(AnimationEndType::kCanceled, type); + // Get the existing transform and apply it to the start bounds which is + // the current bounds of the view. This will place the bounds at the place + // where the animation stopped. + const gfx::Transform transform = view->GetTransform(); + gfx::RectF bounds_f(view->bounds()); + transform.TransformRect(&bounds_f); + view->SetBoundsRect(gfx::ToRoundedRect(bounds_f)); + } + view->SetTransform(gfx::Transform()); } if (data.delegate) { diff --git a/chromium/ui/views/animation/bounds_animator.h b/chromium/ui/views/animation/bounds_animator.h index 620145bce60..dff13110422 100644 --- a/chromium/ui/views/animation/bounds_animator.h +++ b/chromium/ui/views/animation/bounds_animator.h @@ -67,10 +67,6 @@ class VIEWS_EXPORT BoundsAnimator : public AnimationDelegateViews { // animating its current bounds is returned. gfx::Rect GetTargetBounds(const View* view) const; - // Sets the animation for the specified view. - void SetAnimationForView(View* view, - std::unique_ptr<gfx::SlideAnimation> animation); - // Returns the animation for the specified view. BoundsAnimator owns the // returned Animation. const gfx::SlideAnimation* GetAnimationForView(View* view); diff --git a/chromium/ui/views/animation/bounds_animator_unittest.cc b/chromium/ui/views/animation/bounds_animator_unittest.cc index 790c74759e7..c09736c4572 100644 --- a/chromium/ui/views/animation/bounds_animator_unittest.cc +++ b/chromium/ui/views/animation/bounds_animator_unittest.cc @@ -6,6 +6,7 @@ #include "base/macros.h" #include "base/run_loop.h" +#include "base/test/icu_test_util.h" #include "base/test/task_environment.h" #include "testing/gtest/include/gtest/gtest.h" #include "ui/gfx/animation/slide_animation.h" @@ -78,6 +79,56 @@ class TestView : public View { DISALLOW_COPY_AND_ASSIGN(TestView); }; +class RTLAnimationTestDelegate : public gfx::AnimationDelegate { + public: + RTLAnimationTestDelegate(const gfx::Rect& start, + const gfx::Rect& target, + View* view, + base::RepeatingClosure quit_closure) + : start_(start), + target_(target), + view_(view), + quit_closure_(std::move(quit_closure)) {} + ~RTLAnimationTestDelegate() override = default; + + private: + // gfx::AnimationDelegate: + void AnimationProgressed(const Animation* animation) override { + gfx::Transform transform = view_->GetTransform(); + ASSERT_TRUE(!transform.IsIdentity()); + + // In this test, assume that |parent| is root view. + View* parent = view_->parent(); + + const gfx::Rect start_rect_in_screen = parent->GetMirroredRect(start_); + const gfx::Rect target_rect_in_screen = parent->GetMirroredRect(target_); + + gfx::RectF current_bounds_in_screen( + parent->GetMirroredRect(view_->bounds())); + transform.TransformRect(¤t_bounds_in_screen); + + // Verify that |view_|'s current bounds in screen are valid. + EXPECT_GE(current_bounds_in_screen.x(), + std::min(start_rect_in_screen.x(), target_rect_in_screen.x())); + EXPECT_LE( + current_bounds_in_screen.right(), + std::max(start_rect_in_screen.right(), target_rect_in_screen.right())); + + quit_closure_.Run(); + } + + // Animation initial bounds. + gfx::Rect start_; + + // Animation target bounds. + gfx::Rect target_; + + // view to be animated. + View* view_; + + base::RepeatingClosure quit_closure_; +}; + } // namespace class BoundsAnimatorTest : public testing::Test { @@ -268,4 +319,64 @@ TEST_F(BoundsAnimatorTest, UseTransformsAnimateViewToEmptySrc) { EXPECT_EQ(target_bounds, child()->bounds()); } +// Tests that when using the transform option on the bounds animator, cancelling +// the animation part way results in the correct bounds applied. +TEST_F(BoundsAnimatorTest, UseTransformsCancelAnimation) { + RecreateAnimator(/*use_transforms=*/true); + + gfx::Rect initial_bounds(0, 0, 10, 10); + child()->SetBoundsRect(initial_bounds); + gfx::Rect target_bounds(10, 10, 20, 20); + + const base::TimeDelta duration = base::TimeDelta::FromMilliseconds(200); + animator()->SetAnimationDuration(duration); + // Use a linear tween so we can estimate the expected bounds. + animator()->set_tween_type(gfx::Tween::LINEAR); + animator()->AnimateViewTo(child(), target_bounds); + animator()->SetAnimationDelegate(child(), + std::make_unique<TestAnimationDelegate>()); + EXPECT_TRUE(animator()->IsAnimating()); + EXPECT_TRUE(animator()->IsAnimating(child())); + + // Stop halfway and cancel. The child should have its bounds updated to + // exactly halfway between |initial_bounds| and |target_bounds|. + const gfx::Rect expected_bounds(5, 5, 15, 15); + task_environment_.FastForwardBy(base::TimeDelta::FromMilliseconds(100)); + EXPECT_EQ(initial_bounds, child()->bounds()); + animator()->Cancel(); + EXPECT_EQ(expected_bounds, child()->bounds()); +} + +// Verify that the bounds animation which updates the transform of views work +// as expected under RTL (https://crbug.com/1067033). +TEST_F(BoundsAnimatorTest, VerifyBoundsAnimatorUnderRTL) { + // Enable RTL. + base::test::ScopedRestoreICUDefaultLocale scoped_locale("he"); + + RecreateAnimator(/*use_transform=*/true); + parent()->SetBounds(0, 0, 40, 40); + + const gfx::Rect initial_bounds(0, 0, 10, 10); + child()->SetBoundsRect(initial_bounds); + const gfx::Rect target_bounds(10, 10, 10, 10); + + const base::TimeDelta animation_duration = + base::TimeDelta::FromMilliseconds(10); + animator()->SetAnimationDuration(animation_duration); + child()->set_repaint_count(0); + animator()->AnimateViewTo(child(), target_bounds); + base::RunLoop run_loop; + animator()->SetAnimationDelegate( + child(), + std::make_unique<RTLAnimationTestDelegate>( + initial_bounds, target_bounds, child(), run_loop.QuitClosure())); + + // The animator should be animating now. + EXPECT_TRUE(animator()->IsAnimating()); + EXPECT_TRUE(animator()->IsAnimating(child())); + + run_loop.Run(); + EXPECT_FALSE(animator()->IsAnimating(child())); +} + } // namespace views diff --git a/chromium/ui/views/animation/ink_drop.h b/chromium/ui/views/animation/ink_drop.h index e0b95f64e99..b8692ae1330 100644 --- a/chromium/ui/views/animation/ink_drop.h +++ b/chromium/ui/views/animation/ink_drop.h @@ -30,6 +30,9 @@ class VIEWS_EXPORT InkDrop { // Called by ink drop hosts when their size is changed. virtual void HostSizeChanged(const gfx::Size& new_size) = 0; + // Called by ink drop hosts when their transform is changed. + virtual void HostTransformChanged(const gfx::Transform& new_transform) = 0; + // Gets the target state of the ink drop. virtual InkDropState GetTargetInkDropState() const = 0; diff --git a/chromium/ui/views/animation/ink_drop_animation_ended_reason.cc b/chromium/ui/views/animation/ink_drop_animation_ended_reason.cc index 8499995cd34..f1e4c5553f5 100644 --- a/chromium/ui/views/animation/ink_drop_animation_ended_reason.cc +++ b/chromium/ui/views/animation/ink_drop_animation_ended_reason.cc @@ -4,7 +4,9 @@ #include "ui/views/animation/ink_drop_animation_ended_reason.h" -#include "base/logging.h" +#include <ostream> + +#include "base/notreached.h" 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 cb4df9bda33..48e61528b74 100644 --- a/chromium/ui/views/animation/ink_drop_host_view.cc +++ b/chromium/ui/views/animation/ink_drop_host_view.cc @@ -182,13 +182,26 @@ void InkDropHostView::ResetInkDropMask() { } bool InkDropHostView::AddInkDropClip(ui::Layer* ink_drop_layer) { - base::Optional<HighlightPathGenerator::RoundRect> clipping_data = + base::Optional<gfx::RRectF> clipping_data = HighlightPathGenerator::GetRoundRectForView(this); if (!clipping_data) return false; - ink_drop_layer->SetClipRect(gfx::ToEnclosingRect(clipping_data->bounds)); - ink_drop_layer->SetRoundedCornerRadius( - gfx::RoundedCornersF(clipping_data->corner_radius)); + + ink_drop_layer->SetClipRect(gfx::ToEnclosingRect(clipping_data->rect())); + auto get_corner_radii = + [&clipping_data](gfx::RRectF::Corner corner) -> float { + return clipping_data.value().GetCornerRadii(corner).x(); + }; + gfx::RoundedCornersF rounded_corners; + rounded_corners.set_upper_left( + get_corner_radii(gfx::RRectF::Corner::kUpperLeft)); + rounded_corners.set_upper_right( + get_corner_radii(gfx::RRectF::Corner::kUpperRight)); + rounded_corners.set_lower_right( + get_corner_radii(gfx::RRectF::Corner::kLowerRight)); + rounded_corners.set_lower_left( + get_corner_radii(gfx::RRectF::Corner::kLowerLeft)); + ink_drop_layer->SetRoundedCornerRadius(rounded_corners); ink_drop_layer->SetIsFastRoundedCorner(true); return true; } diff --git a/chromium/ui/views/animation/ink_drop_host_view_unittest.cc b/chromium/ui/views/animation/ink_drop_host_view_unittest.cc index 390e1a991a9..303547f1bed 100644 --- a/chromium/ui/views/animation/ink_drop_host_view_unittest.cc +++ b/chromium/ui/views/animation/ink_drop_host_view_unittest.cc @@ -220,7 +220,7 @@ TEST_F(InkDropHostViewTest, NoInkDropOnTouchOrGestureEvents) { ui::TouchEvent touch_event( ui::ET_TOUCH_PRESSED, gfx::Point(5, 6), ui::EventTimeForNow(), - ui::PointerDetails(ui::EventPointerType::POINTER_TYPE_TOUCH, 1)); + ui::PointerDetails(ui::EventPointerType::kTouch, 1)); test_api_.AnimateInkDrop(InkDropState::ACTION_PENDING, &touch_event); EXPECT_EQ(test_api_.GetInkDrop()->GetTargetInkDropState(), @@ -268,7 +268,7 @@ TEST_F(InkDropHostViewTest, DismissInkDropOnTouchOrGestureEvents) { ui::TouchEvent touch_event( ui::ET_TOUCH_PRESSED, gfx::Point(5, 6), ui::EventTimeForNow(), - ui::PointerDetails(ui::EventPointerType::POINTER_TYPE_TOUCH, 1)); + ui::PointerDetails(ui::EventPointerType::kTouch, 1)); test_api_.AnimateInkDrop(InkDropState::ACTION_TRIGGERED, &touch_event); EXPECT_EQ(test_api_.GetInkDrop()->GetTargetInkDropState(), diff --git a/chromium/ui/views/animation/ink_drop_impl.cc b/chromium/ui/views/animation/ink_drop_impl.cc index c5d93d3d4c4..631a69e84be 100644 --- a/chromium/ui/views/animation/ink_drop_impl.cc +++ b/chromium/ui/views/animation/ink_drop_impl.cc @@ -628,6 +628,12 @@ void InkDropImpl::HostSizeChanged(const gfx::Size& new_size) { } } +void InkDropImpl::HostTransformChanged(const gfx::Transform& new_transform) { + // If the host has a transform applied, the root and its children layers + // should be affected too. + root_layer_->SetTransform(new_transform); +} + InkDropState InkDropImpl::GetTargetInkDropState() const { if (!ink_drop_ripple_) return InkDropState::HIDDEN; diff --git a/chromium/ui/views/animation/ink_drop_impl.h b/chromium/ui/views/animation/ink_drop_impl.h index 46b1b305456..971f3548adc 100644 --- a/chromium/ui/views/animation/ink_drop_impl.h +++ b/chromium/ui/views/animation/ink_drop_impl.h @@ -70,6 +70,7 @@ class VIEWS_EXPORT InkDropImpl : public InkDrop, // InkDrop: void HostSizeChanged(const gfx::Size& new_size) override; + void HostTransformChanged(const gfx::Transform& new_transform) override; InkDropState GetTargetInkDropState() const override; void AnimateToState(InkDropState ink_drop_state) override; void SetHoverHighlightFadeDuration(base::TimeDelta duration) override; diff --git a/chromium/ui/views/animation/ink_drop_state.cc b/chromium/ui/views/animation/ink_drop_state.cc index adbefae1a22..f06a305ca59 100644 --- a/chromium/ui/views/animation/ink_drop_state.cc +++ b/chromium/ui/views/animation/ink_drop_state.cc @@ -4,9 +4,10 @@ #include "ui/views/animation/ink_drop_state.h" +#include <ostream> #include <string> -#include "base/logging.h" +#include "base/notreached.h" namespace views { diff --git a/chromium/ui/views/animation/ink_drop_stub.cc b/chromium/ui/views/animation/ink_drop_stub.cc index 24e6245a68e..c15af9618d1 100644 --- a/chromium/ui/views/animation/ink_drop_stub.cc +++ b/chromium/ui/views/animation/ink_drop_stub.cc @@ -12,6 +12,8 @@ InkDropStub::~InkDropStub() = default; void InkDropStub::HostSizeChanged(const gfx::Size& new_size) {} +void InkDropStub::HostTransformChanged(const gfx::Transform& new_transform) {} + InkDropState InkDropStub::GetTargetInkDropState() const { return InkDropState::HIDDEN; } diff --git a/chromium/ui/views/animation/ink_drop_stub.h b/chromium/ui/views/animation/ink_drop_stub.h index ba8985cc5c7..849bc196e03 100644 --- a/chromium/ui/views/animation/ink_drop_stub.h +++ b/chromium/ui/views/animation/ink_drop_stub.h @@ -20,6 +20,7 @@ class VIEWS_EXPORT InkDropStub : public InkDrop { // InkDrop: void HostSizeChanged(const gfx::Size& new_size) override; + void HostTransformChanged(const gfx::Transform& new_transform) override; InkDropState GetTargetInkDropState() const override; void AnimateToState(InkDropState state) override; void SetHoverHighlightFadeDuration(base::TimeDelta duration) override; diff --git a/chromium/ui/views/animation/ink_drop_util.cc b/chromium/ui/views/animation/ink_drop_util.cc index 1e5bbb18ace..72ce6649793 100644 --- a/chromium/ui/views/animation/ink_drop_util.cc +++ b/chromium/ui/views/animation/ink_drop_util.cc @@ -6,7 +6,7 @@ #include <math.h> -#include "base/logging.h" +#include "base/check_op.h" #include "ui/gfx/geometry/point3_f.h" #include "ui/gfx/geometry/safe_integer_conversions.h" #include "ui/gfx/geometry/vector2d_f.h" diff --git a/chromium/ui/views/animation/installable_ink_drop.cc b/chromium/ui/views/animation/installable_ink_drop.cc index e0708a8ce9d..ee67a05be7f 100644 --- a/chromium/ui/views/animation/installable_ink_drop.cc +++ b/chromium/ui/views/animation/installable_ink_drop.cc @@ -6,8 +6,9 @@ #include <algorithm> -#include "base/logging.h" +#include "base/check_op.h" #include "base/memory/ptr_util.h" +#include "base/notreached.h" #include "cc/paint/paint_flags.h" #include "third_party/skia/include/core/SkColor.h" #include "third_party/skia/include/core/SkPath.h" @@ -106,6 +107,9 @@ void InstallableInkDrop::HostSizeChanged(const gfx::Size& new_size) { animator_.SetSize(layer_->size()); } +void InstallableInkDrop::HostTransformChanged( + const gfx::Transform& new_transform) {} + InkDropState InstallableInkDrop::GetTargetInkDropState() const { return animator_.target_state(); } diff --git a/chromium/ui/views/animation/installable_ink_drop.h b/chromium/ui/views/animation/installable_ink_drop.h index 65eb6489883..1fa261a776d 100644 --- a/chromium/ui/views/animation/installable_ink_drop.h +++ b/chromium/ui/views/animation/installable_ink_drop.h @@ -67,6 +67,7 @@ class VIEWS_EXPORT InstallableInkDrop : public InkDrop, // InkDrop: void HostSizeChanged(const gfx::Size& new_size) override; + void HostTransformChanged(const gfx::Transform& new_transform) override; InkDropState GetTargetInkDropState() const override; void AnimateToState(InkDropState ink_drop_state) override; void SetHoverHighlightFadeDuration(base::TimeDelta duration) override; diff --git a/chromium/ui/views/animation/installable_ink_drop_animator.cc b/chromium/ui/views/animation/installable_ink_drop_animator.cc index 54c014b6508..4acf8cfcbf5 100644 --- a/chromium/ui/views/animation/installable_ink_drop_animator.cc +++ b/chromium/ui/views/animation/installable_ink_drop_animator.cc @@ -7,7 +7,8 @@ #include <algorithm> #include "base/bind.h" -#include "base/logging.h" +#include "base/check_op.h" +#include "base/notreached.h" #include "base/trace_event/trace_event.h" #include "ui/gfx/animation/tween.h" #include "ui/gfx/geometry/point_f.h" diff --git a/chromium/ui/views/animation/scroll_animator.cc b/chromium/ui/views/animation/scroll_animator.cc index 64a270d69ab..7f5a66f1e4f 100644 --- a/chromium/ui/views/animation/scroll_animator.cc +++ b/chromium/ui/views/animation/scroll_animator.cc @@ -7,7 +7,7 @@ #include <algorithm> #include <cmath> -#include "base/logging.h" +#include "base/check.h" #include "ui/gfx/animation/slide_animation.h" namespace { diff --git a/chromium/ui/views/background.cc b/chromium/ui/views/background.cc index d8fc6304194..ed6a43ff33e 100644 --- a/chromium/ui/views/background.cc +++ b/chromium/ui/views/background.cc @@ -6,7 +6,7 @@ #include <utility> -#include "base/logging.h" +#include "base/check.h" #include "base/macros.h" #include "base/scoped_observer.h" #include "build/build_config.h" diff --git a/chromium/ui/views/border.cc b/chromium/ui/views/border.cc index e7b20c6f2e6..041214dc717 100644 --- a/chromium/ui/views/border.cc +++ b/chromium/ui/views/border.cc @@ -7,7 +7,7 @@ #include <memory> #include <utility> -#include "base/logging.h" +#include "base/check.h" #include "base/macros.h" #include "base/memory/ptr_util.h" #include "cc/paint/paint_flags.h" diff --git a/chromium/ui/views/bubble/bubble_border.cc b/chromium/ui/views/bubble/bubble_border.cc index 9b278304422..22b609a91c5 100644 --- a/chromium/ui/views/bubble/bubble_border.cc +++ b/chromium/ui/views/bubble/bubble_border.cc @@ -10,8 +10,9 @@ #include <utility> #include <vector> -#include "base/logging.h" +#include "base/check_op.h" #include "base/no_destructor.h" +#include "base/notreached.h" #include "cc/paint/paint_flags.h" #include "third_party/skia/include/core/SkDrawLooper.h" #include "third_party/skia/include/core/SkPath.h" diff --git a/chromium/ui/views/bubble/bubble_dialog_delegate_view.cc b/chromium/ui/views/bubble/bubble_dialog_delegate_view.cc index 308adac42ea..be71a1d4243 100644 --- a/chromium/ui/views/bubble/bubble_dialog_delegate_view.cc +++ b/chromium/ui/views/bubble/bubble_dialog_delegate_view.cc @@ -12,6 +12,7 @@ #include "build/build_config.h" #include "ui/accessibility/ax_enums.mojom.h" #include "ui/accessibility/ax_node_data.h" +#include "ui/accessibility/ax_role_properties.h" #include "ui/base/default_style.h" #include "ui/base/resource/resource_bundle.h" #include "ui/gfx/color_utils.h" @@ -153,10 +154,6 @@ class BubbleDialogDelegateView::AnchorViewObserver : public ViewObserver { parent_->OnAnchorBoundsChanged(); } - void OnViewAddedToWidget(View* observed_view) override { - parent_->SetAnchorWidget(observed_view->GetWidget()); - } - // TODO(pbos): Consider observing View visibility changes and only updating // view bounds when the anchor is visible. @@ -169,6 +166,8 @@ class BubbleDialogDelegateView::AnchorViewObserver : public ViewObserver { Widget* BubbleDialogDelegateView::CreateBubble( BubbleDialogDelegateView* bubble_delegate) { bubble_delegate->Init(); + // Get the latest anchor widget from the anchor view at bubble creation time. + bubble_delegate->SetAnchorView(bubble_delegate->GetAnchorView()); Widget* bubble_widget = CreateBubbleWidget(bubble_delegate); #if (defined(OS_LINUX) && !defined(OS_CHROMEOS)) || defined(OS_MACOSX) @@ -189,6 +188,8 @@ BubbleDialogDelegateView::BubbleDialogDelegateView(View* anchor_view, BubbleBorder::Arrow arrow, BubbleBorder::Shadow shadow) : shadow_(shadow) { + WidgetDelegate::SetShowCloseButton(false); + SetArrow(arrow); LayoutProvider* provider = LayoutProvider::Get(); // An individual bubble should override these margins if its layout differs @@ -210,10 +211,6 @@ BubbleDialogDelegateView* BubbleDialogDelegateView::AsBubbleDialogDelegate() { return this; } -bool BubbleDialogDelegateView::ShouldShowCloseButton() const { - return false; -} - NonClientFrameView* BubbleDialogDelegateView::CreateNonClientFrameView( Widget* widget) { BubbleFrameView* frame = new BubbleDialogFrameView(title_margins_); @@ -456,18 +453,49 @@ void BubbleDialogDelegateView::SetAnchorView(View* anchor_view) { anchor_view_observer_.reset(); } - SetAnchorWidget(anchor_view ? anchor_view->GetWidget() : nullptr); - if (!anchor_view) - return; + // When the anchor view gets set the associated anchor widget might + // change as well. + if (!anchor_view || anchor_widget() != anchor_view->GetWidget()) { + if (anchor_widget()) { + if (GetWidget() && GetWidget()->IsVisible()) + UpdateHighlightedButton(false); + paint_as_active_lock_.reset(); + anchor_widget_->RemoveObserver(this); + anchor_widget_ = nullptr; + } + if (anchor_view) { + anchor_widget_ = anchor_view->GetWidget(); + if (anchor_widget_) { + anchor_widget_->AddObserver(this); + const bool visible = GetWidget() && GetWidget()->IsVisible(); + UpdateHighlightedButton(visible); + // Have the anchor widget's paint-as-active state track this view's + // widget - lock is only required if the bubble widget is active. + if (anchor_widget_->GetTopLevelWidget() && GetWidget() && + GetWidget()->ShouldPaintAsActive()) { + paint_as_active_lock_ = + anchor_widget_->GetTopLevelWidget()->LockPaintAsActive(); + } + } + } + } - anchor_view_observer_ = - std::make_unique<AnchorViewObserver>(this, anchor_view); - OnAnchorBoundsChanged(); + if (anchor_view) { + anchor_view_observer_ = + std::make_unique<AnchorViewObserver>(this, anchor_view); + // Do not update anchoring for NULL views; this could indicate + // that our NativeWindow is being destroyed, so it would be + // dangerous for us to update our anchor bounds at that + // point. (It's safe to skip this, since if we were to update the + // bounds when |anchor_view| is NULL, the bubble won't move.) + OnAnchorBoundsChanged(); + } - // Make sure that focus can move into here from the anchor view (but not out, - // focus will cycle inside the dialog once it gets here). - if (focus_traversable_from_anchor_view_) + if (anchor_view && focus_traversable_from_anchor_view_) { + // Make sure that focus can move into here from the anchor view (but not + // out, focus will cycle inside the dialog once it gets here). anchor_view->SetProperty(kAnchoredDialogKey, this); + } } void BubbleDialogDelegateView::SetAnchorRect(const gfx::Rect& rect) { @@ -520,8 +548,7 @@ void BubbleDialogDelegateView::HandleVisibilityChanged(Widget* widget, // the bubble in its entirety rather than just its title and initially focused // view. See http://crbug.com/474622 for details. if (widget == GetWidget() && visible) { - if (GetAccessibleWindowRole() == ax::mojom::Role::kAlert || - GetAccessibleWindowRole() == ax::mojom::Role::kAlertDialog) { + if (ui::IsAlert(GetAccessibleWindowRole())) { widget->GetRootView()->NotifyAccessibilityEvent(ax::mojom::Event::kAlert, true); } @@ -533,28 +560,6 @@ void BubbleDialogDelegateView::OnDeactivate() { GetWidget()->CloseWithReason(views::Widget::ClosedReason::kLostFocus); } -void BubbleDialogDelegateView::SetAnchorWidget(Widget* anchor_widget) { - if (anchor_widget_ == anchor_widget) - return; - - if (anchor_widget_) - anchor_widget_->RemoveObserver(this); - - UpdateHighlightedButton(GetWidget() && GetWidget()->IsVisible() && - anchor_widget); - // Have the anchor widget's paint-as-active state track this view's widget. - // Lock is only required if the bubble widget is active. - paint_as_active_lock_ = - (GetWidget() && GetWidget()->ShouldPaintAsActive() && anchor_widget && - anchor_widget->GetTopLevelWidget()) - ? anchor_widget->GetTopLevelWidget()->LockPaintAsActive() - : nullptr; - anchor_widget_ = anchor_widget; - - if (anchor_widget_) - anchor_widget_->AddObserver(this); -} - void BubbleDialogDelegateView::UpdateHighlightedButton(bool highlighted) { Button* button = Button::AsButton(highlighted_button_tracker_.view()); button = button ? button : Button::AsButton(GetAnchorView()); diff --git a/chromium/ui/views/bubble/bubble_dialog_delegate_view.h b/chromium/ui/views/bubble/bubble_dialog_delegate_view.h index 862b3d89dec..c56ab1fa993 100644 --- a/chromium/ui/views/bubble/bubble_dialog_delegate_view.h +++ b/chromium/ui/views/bubble/bubble_dialog_delegate_view.h @@ -67,7 +67,6 @@ class VIEWS_EXPORT BubbleDialogDelegateView : public DialogDelegateView, // DialogDelegateView: BubbleDialogDelegateView* AsBubbleDialogDelegate() override; - bool ShouldShowCloseButton() const override; NonClientFrameView* CreateNonClientFrameView(Widget* widget) override; bool AcceleratorPressed(const ui::Accelerator& accelerator) override; @@ -213,9 +212,6 @@ class VIEWS_EXPORT BubbleDialogDelegateView : public DialogDelegateView, // Called when a deactivation is detected. void OnDeactivate(); - // Updates the anchoring widget. - void SetAnchorWidget(Widget* widget); - // Update the button highlight, which may be the anchor view or an explicit // view set in |highlighted_button_tracker_|. This can be overridden to // provide different highlight effects. diff --git a/chromium/ui/views/bubble/footnote_container_view.cc b/chromium/ui/views/bubble/footnote_container_view.cc index 68c08db02f3..1478fc8cbbb 100644 --- a/chromium/ui/views/bubble/footnote_container_view.cc +++ b/chromium/ui/views/bubble/footnote_container_view.cc @@ -57,7 +57,6 @@ FootnoteContainerView::FootnoteContainerView(const gfx::Insets& margins, SetLayoutManager(std::make_unique<BoxLayout>( BoxLayout::Orientation::kVertical, margins, 0)); SetCornerRadius(corner_radius); - ResetBorder(); auto* child_view_ptr = AddChildView(std::move(child_view)); SetVisible(child_view_ptr->GetVisible()); } @@ -88,10 +87,9 @@ void FootnoteContainerView::ResetBackground() { } void FootnoteContainerView::ResetBorder() { - SetBorder(CreateSolidSidedBorder(1, 0, 0, 0, - GetNativeTheme()->ShouldUseDarkColors() - ? gfx::kGoogleGrey900 - : gfx::kGoogleGrey200)); + SetBorder(CreateSolidSidedBorder( + 1, 0, 0, 0, GetNativeTheme()->GetSystemColor( + ui::NativeTheme::kColorId_FootnoteContainerBorder))); } BEGIN_METADATA(FootnoteContainerView) diff --git a/chromium/ui/views/color_chooser/color_chooser_view.cc b/chromium/ui/views/color_chooser/color_chooser_view.cc index 2cd1303c808..2459d40c9e2 100644 --- a/chromium/ui/views/color_chooser/color_chooser_view.cc +++ b/chromium/ui/views/color_chooser/color_chooser_view.cc @@ -11,7 +11,7 @@ #include <string> #include <utility> -#include "base/logging.h" +#include "base/check.h" #include "base/macros.h" #include "base/numerics/ranges.h" #include "base/strings/string_number_conversions.h" @@ -382,10 +382,10 @@ ColorChooserView::ColorChooserView(ColorChooserListener* listener, container2->SetLayoutManager(std::make_unique<views::GridLayout>()); ColumnSet* columns = layout->AddColumnSet(0); columns->AddColumn(GridLayout::LEADING, GridLayout::FILL, 0, - GridLayout::USE_PREF, 0, 0); + GridLayout::ColumnSize::kUsePreferred, 0, 0); columns->AddPaddingColumn(0, kMarginWidth); columns->AddColumn(GridLayout::FILL, GridLayout::FILL, 1, - GridLayout::USE_PREF, 0, 0); + GridLayout::ColumnSize::kUsePreferred, 0, 0); layout->StartRow(0, 0); auto textfield = std::make_unique<Textfield>(); textfield->set_controller(this); diff --git a/chromium/ui/views/controls/button/button_controller.cc b/chromium/ui/views/controls/button/button_controller.cc index 3278b27d162..f639cf93c55 100644 --- a/chromium/ui/views/controls/button/button_controller.cc +++ b/chromium/ui/views/controls/button/button_controller.cc @@ -116,6 +116,9 @@ bool ButtonController::OnKeyReleased(const ui::KeyEvent& event) { } void ButtonController::OnGestureEvent(ui::GestureEvent* event) { + if (button_->state() == Button::STATE_DISABLED) + return; + if (event->type() == ui::ET_GESTURE_TAP && button_controller_delegate_->IsTriggerableEvent(*event)) { // A GESTURE_END event is issued immediately after ET_GESTURE_TAP and will diff --git a/chromium/ui/views/controls/button/button_unittest.cc b/chromium/ui/views/controls/button/button_unittest.cc index 54e0cad33d1..3dfeba259fd 100644 --- a/chromium/ui/views/controls/button/button_unittest.cc +++ b/chromium/ui/views/controls/button/button_unittest.cc @@ -7,6 +7,8 @@ #include <memory> #include <utility> +#include "base/bind.h" +#include "base/callback_forward.h" #include "base/macros.h" #include "base/memory/ptr_util.h" #include "base/run_loop.h" @@ -80,6 +82,9 @@ class TestButton : public Button, public ButtonListener { void ButtonPressed(Button* sender, const ui::Event& event) override { pressed_ = true; + + if (!on_button_pressed_handler_.is_null()) + on_button_pressed_handler_.Run(); } void OnClickCanceled(const ui::Event& event) override { canceled_ = true; } @@ -103,6 +108,10 @@ class TestButton : public Button, public ButtonListener { custom_key_click_action_ = custom_key_click_action; } + void set_on_button_pressed_handler(const base::RepeatingClosure& callback) { + on_button_pressed_handler_ = callback; + } + void Reset() { pressed_ = false; canceled_ = false; @@ -120,6 +129,9 @@ class TestButton : public Button, public ButtonListener { KeyClickAction custom_key_click_action_ = KeyClickAction::kNone; + // If available, will be triggered when the button is pressed. + base::RepeatingClosure on_button_pressed_handler_; + DISALLOW_COPY_AND_ASSIGN(TestButton); }; @@ -159,6 +171,22 @@ class TestButtonObserver : public ButtonObserver { DISALLOW_COPY_AND_ASSIGN(TestButtonObserver); }; +TestInkDrop* AddTestInkDrop(TestButton* button) { + auto owned_ink_drop = std::make_unique<TestInkDrop>(); + TestInkDrop* ink_drop = owned_ink_drop.get(); + InkDropHostViewTestApi(button).SetInkDrop(std::move(owned_ink_drop)); + return ink_drop; +} + +// TODO(tluk): remove when the appropriate ownership APIs have been added for +// Widget's SetContentsView(). +template <typename T> +T* AddContentsView(Widget* widget, std::unique_ptr<T> view) { + T* view_ptr = view.get(); + widget->SetContentsView(view.release()); + return view_ptr; +} + } // namespace class ButtonTest : public ViewsTestBase { @@ -179,8 +207,7 @@ class ButtonTest : public ViewsTestBase { widget_->Init(std::move(params)); widget_->Show(); - button_ = std::make_unique<TestButton>(false); - widget_->SetContentsView(button_.get()); + button_ = AddContentsView(widget(), std::make_unique<TestButton>(false)); event_generator_ = std::make_unique<ui::test::EventGenerator>(GetRootWindow(widget())); @@ -192,37 +219,35 @@ class ButtonTest : public ViewsTestBase { button_->RemoveButtonObserver(button_observer_.get()); button_observer_.reset(); - button_.reset(); widget_.reset(); ViewsTestBase::TearDown(); } - void CreateButtonWithInkDrop(std::unique_ptr<InkDrop> ink_drop, - bool has_ink_drop_action_on_click) { - button_ = std::make_unique<TestButton>(has_ink_drop_action_on_click); - InkDropHostViewTestApi(button_.get()).SetInkDrop(std::move(ink_drop)); - widget_->SetContentsView(button_.get()); + TestInkDrop* CreateButtonWithInkDrop(bool has_ink_drop_action_on_click) { + button_ = AddContentsView( + widget(), std::make_unique<TestButton>(has_ink_drop_action_on_click)); + widget_->SetContentsView(button_); + return AddTestInkDrop(button_); } void CreateButtonWithRealInkDrop() { - button_ = std::make_unique<TestButton>(false); - InkDropHostViewTestApi(button_.get()) - .SetInkDrop( - std::make_unique<InkDropImpl>(button_.get(), button_->size())); - widget_->SetContentsView(button_.get()); + button_ = AddContentsView(widget(), std::make_unique<TestButton>(false)); + InkDropHostViewTestApi(button_).SetInkDrop( + std::make_unique<InkDropImpl>(button_, button_->size())); + widget_->SetContentsView(button_); } void CreateButtonWithObserver() { - button_ = std::make_unique<TestButton>(false); + button_ = AddContentsView(widget(), std::make_unique<TestButton>(false)); button_observer_ = std::make_unique<TestButtonObserver>(); button_->AddButtonObserver(button_observer_.get()); - widget_->SetContentsView(button_.get()); + widget_->SetContentsView(button_); } protected: Widget* widget() { return widget_.get(); } - TestButton* button() { return button_.get(); } + TestButton* button() { return button_; } TestButtonObserver* button_observer() { return button_observer_.get(); } ui::test::EventGenerator* event_generator() { return event_generator_.get(); } void SetDraggedView(View* dragged_view) { @@ -231,7 +256,7 @@ class ButtonTest : public ViewsTestBase { private: std::unique_ptr<Widget> widget_; - std::unique_ptr<TestButton> button_; + TestButton* button_; std::unique_ptr<TestButtonObserver> button_observer_; std::unique_ptr<ui::test::EventGenerator> event_generator_; @@ -419,6 +444,18 @@ TEST_F(ButtonTest, GestureEventsSetState) { EXPECT_EQ(Button::STATE_NORMAL, button()->state()); } +// Tests that if the button was disabled in its button press handler, gesture +// events will not revert the disabled state back to normal. +// https://crbug.com/1084241. +TEST_F(ButtonTest, GestureEventsRespectDisabledState) { + button()->set_on_button_pressed_handler(base::BindRepeating( + [](TestButton* button) { button->SetEnabled(false); }, button())); + + EXPECT_EQ(Button::STATE_NORMAL, button()->state()); + event_generator()->GestureTapAt(button()->GetBoundsInScreen().CenterPoint()); + EXPECT_EQ(Button::STATE_DISABLED, button()->state()); +} + #endif // !defined(OS_MACOSX) || defined(USE_AURA) // Ensure subclasses of Button are correctly recognized as Button. @@ -458,8 +495,7 @@ TEST_F(ButtonTest, AsButton) { // Note: Ink drop is not hidden upon release because Button descendants // may enter a different ink drop state. TEST_F(ButtonTest, ButtonClickTogglesInkDrop) { - TestInkDrop* ink_drop = new TestInkDrop(); - CreateButtonWithInkDrop(base::WrapUnique(ink_drop), false); + TestInkDrop* ink_drop = CreateButtonWithInkDrop(false); event_generator()->MoveMouseTo(button()->GetBoundsInScreen().CenterPoint()); event_generator()->PressLeftButton(); @@ -472,8 +508,7 @@ TEST_F(ButtonTest, ButtonClickTogglesInkDrop) { // Tests that pressing a button shows and releasing capture hides ink drop. // Releasing capture should also reset PRESSED button state to NORMAL. TEST_F(ButtonTest, CaptureLossHidesInkDrop) { - TestInkDrop* ink_drop = new TestInkDrop(); - CreateButtonWithInkDrop(base::WrapUnique(ink_drop), false); + TestInkDrop* ink_drop = CreateButtonWithInkDrop(false); event_generator()->MoveMouseTo(button()->GetBoundsInScreen().CenterPoint()); event_generator()->PressLeftButton(); @@ -489,8 +524,7 @@ TEST_F(ButtonTest, CaptureLossHidesInkDrop) { } TEST_F(ButtonTest, HideInkDropWhenShowingContextMenu) { - TestInkDrop* ink_drop = new TestInkDrop(); - CreateButtonWithInkDrop(base::WrapUnique(ink_drop), false); + TestInkDrop* ink_drop = CreateButtonWithInkDrop(false); TestContextMenuController context_menu_controller; button()->set_context_menu_controller(&context_menu_controller); button()->set_hide_ink_drop_when_showing_context_menu(true); @@ -505,8 +539,7 @@ TEST_F(ButtonTest, HideInkDropWhenShowingContextMenu) { } TEST_F(ButtonTest, DontHideInkDropWhenShowingContextMenu) { - TestInkDrop* ink_drop = new TestInkDrop(); - CreateButtonWithInkDrop(base::WrapUnique(ink_drop), false); + TestInkDrop* ink_drop = CreateButtonWithInkDrop(false); TestContextMenuController context_menu_controller; button()->set_context_menu_controller(&context_menu_controller); button()->set_hide_ink_drop_when_showing_context_menu(false); @@ -523,8 +556,7 @@ TEST_F(ButtonTest, DontHideInkDropWhenShowingContextMenu) { TEST_F(ButtonTest, HideInkDropOnBlur) { gfx::Point center(10, 10); - TestInkDrop* ink_drop = new TestInkDrop(); - CreateButtonWithInkDrop(base::WrapUnique(ink_drop), false); + TestInkDrop* ink_drop = CreateButtonWithInkDrop(false); button()->OnFocus(); @@ -543,8 +575,7 @@ TEST_F(ButtonTest, HideInkDropOnBlur) { } TEST_F(ButtonTest, HideInkDropHighlightOnDisable) { - TestInkDrop* ink_drop = new TestInkDrop(); - CreateButtonWithInkDrop(base::WrapUnique(ink_drop), false); + TestInkDrop* ink_drop = CreateButtonWithInkDrop(false); event_generator()->MoveMouseTo(button()->GetBoundsInScreen().CenterPoint()); EXPECT_TRUE(ink_drop->is_hovered()); @@ -555,8 +586,7 @@ TEST_F(ButtonTest, HideInkDropHighlightOnDisable) { } TEST_F(ButtonTest, InkDropAfterTryingToShowContextMenu) { - TestInkDrop* ink_drop = new TestInkDrop(); - CreateButtonWithInkDrop(base::WrapUnique(ink_drop), false); + TestInkDrop* ink_drop = CreateButtonWithInkDrop(false); button()->set_context_menu_controller(nullptr); ink_drop->SetHovered(true); @@ -569,34 +599,31 @@ TEST_F(ButtonTest, InkDropAfterTryingToShowContextMenu) { } TEST_F(ButtonTest, HideInkDropHighlightWhenRemoved) { - views::View test_container; - test_container.set_owned_by_client(); - TestInkDrop* ink_drop = new TestInkDrop(); - CreateButtonWithInkDrop(base::WrapUnique(ink_drop), false); - // Mark the button as owned by client so we can remove it from widget() - // without it being deleted. - button()->set_owned_by_client(); + View* contents_view = AddContentsView(widget(), std::make_unique<View>()); + + TestButton* button = + contents_view->AddChildView(std::make_unique<TestButton>(false)); + button->SetBounds(0, 0, 200, 200); + TestInkDrop* ink_drop = AddTestInkDrop(button); // Make sure that the button ink drop is hidden after the button gets removed. - widget()->SetContentsView(&test_container); - test_container.AddChildView(button()); - event_generator()->MoveMouseTo(button()->GetBoundsInScreen().origin()); + event_generator()->MoveMouseTo(button->GetBoundsInScreen().origin()); event_generator()->MoveMouseBy(2, 2); EXPECT_TRUE(ink_drop->is_hovered()); // Set ink-drop state to ACTIVATED to make sure that removing the container // sets it back to HIDDEN. ink_drop->AnimateToState(InkDropState::ACTIVATED); - test_container.RemoveAllChildViews(false); + auto owned_button = contents_view->RemoveChildViewT(button); + button = nullptr; + EXPECT_FALSE(ink_drop->is_hovered()); EXPECT_EQ(InkDropState::HIDDEN, ink_drop->GetTargetInkDropState()); // Make sure hiding the ink drop happens even if the button is indirectly // being removed. - views::View parent_test_container; - parent_test_container.set_owned_by_client(); - widget()->SetContentsView(&parent_test_container); - parent_test_container.AddChildView(&test_container); - test_container.AddChildView(button()); + View* parent_view = contents_view->AddChildView(std::make_unique<View>()); + parent_view->SetBounds(0, 0, 400, 400); + button = parent_view->AddChildView(std::move(owned_button)); // Trigger hovering and then remove from the indirect parent. This should // propagate down to Button which should remove the highlight effect. @@ -606,17 +633,9 @@ TEST_F(ButtonTest, HideInkDropHighlightWhenRemoved) { // Set ink-drop state to ACTIVATED to make sure that removing the container // sets it back to HIDDEN. ink_drop->AnimateToState(InkDropState::ACTIVATED); - parent_test_container.RemoveAllChildViews(false); + auto owned_parent = contents_view->RemoveChildViewT(parent_view); EXPECT_EQ(InkDropState::HIDDEN, ink_drop->GetTargetInkDropState()); EXPECT_FALSE(ink_drop->is_hovered()); - - // Remove references to and delete button() which cannot be removed by owned - // containers as it's permanently set as owned by client. - test_container.RemoveAllChildViews(false); - - // Set the widget contents view to a new View so widget() doesn't contain a - // stale reference to the test containers that are about to go out of scope. - widget()->SetContentsView(new View()); } // Tests that when button is set to notify on release, dragging mouse out and @@ -625,8 +644,7 @@ TEST_F(ButtonTest, InkDropShowHideOnMouseDraggedNotifyOnRelease) { gfx::Point center(10, 10); gfx::Point oob(-1, -1); - TestInkDrop* ink_drop = new TestInkDrop(); - CreateButtonWithInkDrop(base::WrapUnique(ink_drop), false); + TestInkDrop* ink_drop = CreateButtonWithInkDrop(false); button()->button_controller()->set_notify_action( ButtonController::NotifyAction::kOnRelease); @@ -667,8 +685,7 @@ TEST_F(ButtonTest, InkDropShowHideOnMouseDraggedNotifyOnPress) { gfx::Point center(10, 10); gfx::Point oob(-1, -1); - TestInkDrop* ink_drop = new TestInkDrop(); - CreateButtonWithInkDrop(base::WrapUnique(ink_drop), true); + TestInkDrop* ink_drop = CreateButtonWithInkDrop(true); button()->button_controller()->set_notify_action( ButtonController::NotifyAction::kOnPress); @@ -708,8 +725,7 @@ TEST_F(ButtonTest, InkDropStaysHiddenWhileDragging) { gfx::Point center(10, 10); gfx::Point oob(-1, -1); - TestInkDrop* ink_drop = new TestInkDrop(); - CreateButtonWithInkDrop(base::WrapUnique(ink_drop), false); + TestInkDrop* ink_drop = CreateButtonWithInkDrop(false); button()->OnMousePressed(ui::MouseEvent( ui::ET_MOUSE_PRESSED, center, center, ui::EventTimeForNow(), @@ -738,35 +754,51 @@ TEST_F(ButtonTest, InkDropStaysHiddenWhileDragging) { SetDraggedView(nullptr); } +// VisibilityTestButton tests to see if an ink drop or a layer has been added to +// the button at any point during the visibility state changes of its Widget. +class VisibilityTestButton : public TestButton { + public: + VisibilityTestButton() : TestButton(false) {} + ~VisibilityTestButton() override { + if (layer()) + ADD_FAILURE(); + } + + // TestButton: + void AddInkDropLayer(ui::Layer* ink_drop_layer) override { + ADD_FAILURE(); + TestButton::AddInkDropLayer(ink_drop_layer); + } + void RemoveInkDropLayer(ui::Layer* ink_drop_layer) override { + ADD_FAILURE(); + TestButton::RemoveInkDropLayer(ink_drop_layer); + } +}; + // Test that hiding or closing a Widget doesn't attempt to add a layer due to // changed visibility states. TEST_F(ButtonTest, NoLayerAddedForWidgetVisibilityChanges) { - CreateButtonWithRealInkDrop(); + VisibilityTestButton* button = + AddContentsView(widget(), std::make_unique<VisibilityTestButton>()); - EXPECT_TRUE(button()->GetVisible()); - EXPECT_FALSE(button()->layer()); + // Ensure no layers are created during construction. + EXPECT_TRUE(button->GetVisible()); + EXPECT_FALSE(button->layer()); + // Ensure no layers are created when hiding the widget. widget()->Hide(); - EXPECT_FALSE(button()->layer()); - EXPECT_EQ(0, button()->ink_drop_layer_add_count()); - EXPECT_EQ(0, button()->ink_drop_layer_remove_count()); + EXPECT_FALSE(button->layer()); + // Ensure no layers are created when the widget is reshown. widget()->Show(); - EXPECT_FALSE(button()->layer()); - EXPECT_EQ(0, button()->ink_drop_layer_add_count()); - EXPECT_EQ(0, button()->ink_drop_layer_remove_count()); + EXPECT_FALSE(button->layer()); - // Allow the button to be interrogated after the view hierarchy is torn down. - button()->set_owned_by_client(); + // Ensure no layers are created during the closing of the Widget. widget()->Close(); // Start an asynchronous close. - EXPECT_FALSE(button()->layer()); - EXPECT_EQ(0, button()->ink_drop_layer_add_count()); - EXPECT_EQ(0, button()->ink_drop_layer_remove_count()); + EXPECT_FALSE(button->layer()); + // Ensure no layers are created following the Widget's destruction. base::RunLoop().RunUntilIdle(); // Complete the Close(). - EXPECT_FALSE(button()->layer()); - EXPECT_EQ(0, button()->ink_drop_layer_add_count()); - EXPECT_EQ(0, button()->ink_drop_layer_remove_count()); } // Verify that the Space key clicks the button on key-press on Mac, and diff --git a/chromium/ui/views/controls/button/checkbox.cc b/chromium/ui/views/controls/button/checkbox.cc index 8788ff2e07e..06050cc2479 100644 --- a/chromium/ui/views/controls/button/checkbox.cc +++ b/chromium/ui/views/controls/button/checkbox.cc @@ -185,7 +185,7 @@ SkPath Checkbox::GetFocusRingPath() const { SkColor Checkbox::GetIconImageColor(int icon_state) const { const SkColor active_color = GetNativeTheme()->GetSystemColor( (icon_state & IconState::CHECKED) - ? ui::NativeTheme::kColorId_ButtonEnabledColor + ? ui::NativeTheme::kColorId_ButtonCheckedColor : ui::NativeTheme::kColorId_ButtonUncheckedColor); return (icon_state & IconState::ENABLED) ? active_color diff --git a/chromium/ui/views/controls/button/image_button_factory.cc b/chromium/ui/views/controls/button/image_button_factory.cc index 3b492d1e9b5..5ac49262cfe 100644 --- a/chromium/ui/views/controls/button/image_button_factory.cc +++ b/chromium/ui/views/controls/button/image_button_factory.cc @@ -5,6 +5,7 @@ #include <memory> +#include "ui/gfx/color_palette.h" #include "ui/gfx/color_utils.h" #include "ui/gfx/paint_vector_icon.h" #include "ui/gfx/vector_icon_types.h" diff --git a/chromium/ui/views/controls/button/image_button_factory.h b/chromium/ui/views/controls/button/image_button_factory.h index d09ae6de08a..38d7c70ad52 100644 --- a/chromium/ui/views/controls/button/image_button_factory.h +++ b/chromium/ui/views/controls/button/image_button_factory.h @@ -8,7 +8,6 @@ #include <memory> #include "third_party/skia/include/core/SkColor.h" -#include "ui/gfx/color_palette.h" #include "ui/views/views_export.h" namespace gfx { @@ -68,14 +67,8 @@ VIEWS_EXPORT void SetImageFromVectorIconWithColor(ImageButton* button, int dip_size, SkColor icon_color); -// As above, but sets the toggled images for a toggled image button. -VIEWS_EXPORT void SetToggledImageFromVectorIcon( - ToggleImageButton* button, - const gfx::VectorIcon& icon, - int dip_size, - SkColor related_text_color = gfx::kGoogleGrey900); - -// As above, but with a given icon color instead of deriving from a text color. +// As above, but sets the toggled images for a toggled image button +// with a given icon color instead of deriving from a text color. VIEWS_EXPORT void SetToggledImageFromVectorIconWithColor( ToggleImageButton* button, const gfx::VectorIcon& icon, diff --git a/chromium/ui/views/controls/button/label_button_border.cc b/chromium/ui/views/controls/button/label_button_border.cc index beaac0fba8d..6536bbf8eed 100644 --- a/chromium/ui/views/controls/button/label_button_border.cc +++ b/chromium/ui/views/controls/button/label_button_border.cc @@ -6,7 +6,6 @@ #include <utility> -#include "base/logging.h" #include "cc/paint/paint_flags.h" #include "ui/gfx/animation/animation.h" #include "ui/gfx/canvas.h" diff --git a/chromium/ui/views/controls/button/md_text_button.cc b/chromium/ui/views/controls/button/md_text_button.cc index e7ab591a2d3..b9905fc6359 100644 --- a/chromium/ui/views/controls/button/md_text_button.cc +++ b/chromium/ui/views/controls/button/md_text_button.cc @@ -31,30 +31,12 @@ namespace views { // static -std::unique_ptr<LabelButton> MdTextButton::CreateSecondaryUiButton( - ButtonListener* listener, - const base::string16& text) { - return MdTextButton::Create(listener, text, style::CONTEXT_BUTTON_MD); -} - -// static -std::unique_ptr<LabelButton> MdTextButton::CreateSecondaryUiBlueButton( - ButtonListener* listener, - const base::string16& text) { - auto md_button = - MdTextButton::Create(listener, text, style::CONTEXT_BUTTON_MD); - md_button->SetProminent(true); - return md_button; -} - -// static std::unique_ptr<MdTextButton> MdTextButton::Create(ButtonListener* listener, const base::string16& text, int button_context) { auto button = base::WrapUnique<MdTextButton>( new MdTextButton(listener, button_context)); button->SetText(text); - button->SetFocusForPlatform(); return button; } @@ -172,8 +154,7 @@ PropertyEffects MdTextButton::UpdateStyleToIndicateDefaultStatus() { } MdTextButton::MdTextButton(ButtonListener* listener, int button_context) - : LabelButton(listener, base::string16(), button_context), - is_prominent_(false) { + : LabelButton(listener, base::string16(), button_context) { SetInkDropMode(InkDropMode::ON); set_has_ink_drop_action_on_click(true); set_show_ink_drop_when_hot_tracked(true); @@ -259,7 +240,7 @@ void MdTextButton::UpdateColors() { ui::NativeTheme* theme = GetNativeTheme(); SkColor bg_color = - theme->GetSystemColor(ui::NativeTheme::kColorId_DialogBackground); + theme->GetSystemColor(ui::NativeTheme::kColorId_ButtonColor); if (bg_color_override_) { bg_color = *bg_color_override_; @@ -274,9 +255,7 @@ void MdTextButton::UpdateColors() { } if (state() == STATE_PRESSED) { - SkColor shade = - theme->GetSystemColor(ui::NativeTheme::kColorId_ButtonPressedShade); - bg_color = color_utils::GetResultingPaintColor(shade, bg_color); + bg_color = theme->GetSystemButtonPressedColor(bg_color); } SkColor stroke_color; diff --git a/chromium/ui/views/controls/button/md_text_button.h b/chromium/ui/views/controls/button/md_text_button.h index fcd66fcd425..760ab2a80ae 100644 --- a/chromium/ui/views/controls/button/md_text_button.h +++ b/chromium/ui/views/controls/button/md_text_button.h @@ -19,14 +19,6 @@ class VIEWS_EXPORT MdTextButton : public LabelButton { public: METADATA_HEADER(MdTextButton); - // As above, but only creates an MdTextButton if MD is enabled in the - // secondary UI (as opposed to just "top chrome"/"primary" UI). - static std::unique_ptr<LabelButton> CreateSecondaryUiButton( - ButtonListener* listener, - const base::string16& text); - static std::unique_ptr<LabelButton> CreateSecondaryUiBlueButton( - ButtonListener* listener, - const base::string16& text); static std::unique_ptr<MdTextButton> Create( ButtonListener* listener, const base::string16& text, @@ -69,12 +61,12 @@ class VIEWS_EXPORT MdTextButton : public LabelButton { void UpdateColors(); // True if this button uses prominent styling (blue fill, etc.). - bool is_prominent_; + bool is_prominent_ = false; // When set, this provides the background color. base::Optional<SkColor> bg_color_override_; - float corner_radius_; + float corner_radius_ = 0.0f; DISALLOW_COPY_AND_ASSIGN(MdTextButton); }; diff --git a/chromium/ui/views/controls/button/radio_button.cc b/chromium/ui/views/controls/button/radio_button.cc index e9d8c1df6ac..7c894909eb1 100644 --- a/chromium/ui/views/controls/button/radio_button.cc +++ b/chromium/ui/views/controls/button/radio_button.cc @@ -4,7 +4,7 @@ #include "ui/views/controls/button/radio_button.h" -#include "base/logging.h" +#include "base/check.h" #include "ui/accessibility/ax_enums.mojom.h" #include "ui/accessibility/ax_node_data.h" #include "ui/base/resource/resource_bundle.h" diff --git a/chromium/ui/views/controls/button/toggle_button.cc b/chromium/ui/views/controls/button/toggle_button.cc index cc6aa145aec..bf88433d005 100644 --- a/chromium/ui/views/controls/button/toggle_button.cc +++ b/chromium/ui/views/controls/button/toggle_button.cc @@ -96,7 +96,7 @@ class ToggleButton::ThumbView : public InkDropHostView { const SkColor thumb_on_color = thumb_on_color_.value_or( theme->GetSystemColor(ui::NativeTheme::kColorId_ProminentButtonColor)); const SkColor thumb_off_color = thumb_off_color_.value_or( - theme->GetSystemColor(ui::NativeTheme::kColorId_DialogBackground)); + theme->GetSystemColor(ui::NativeTheme::kColorId_ButtonColor)); thumb_flags.setColor( color_utils::AlphaBlend(thumb_on_color, thumb_off_color, color_ratio_)); diff --git a/chromium/ui/views/controls/combobox/combobox.cc b/chromium/ui/views/controls/combobox/combobox.cc index 1137efc406d..b0364bcdb3e 100644 --- a/chromium/ui/views/controls/combobox/combobox.cc +++ b/chromium/ui/views/controls/combobox/combobox.cc @@ -9,12 +9,13 @@ #include <utility> #include "base/bind.h" -#include "base/logging.h" +#include "base/check_op.h" #include "build/build_config.h" #include "ui/accessibility/ax_action_data.h" #include "ui/accessibility/ax_enums.mojom.h" #include "ui/accessibility/ax_node_data.h" #include "ui/base/ime/input_method.h" +#include "ui/base/models/image_model.h" #include "ui/base/models/menu_model.h" #include "ui/events/event.h" #include "ui/gfx/canvas.h" @@ -176,7 +177,9 @@ class Combobox::ComboboxMenuModel : public ui::MenuModel { int GetGroupIdAt(int index) const override { return -1; } - bool GetIconAt(int index, gfx::Image* icon) const override { return false; } + ui::ImageModel GetIconAt(int index) const override { + return ui::ImageModel(); + } ui::ButtonMenuItemModel* GetButtonMenuItemAt(int index) const override { return nullptr; diff --git a/chromium/ui/views/controls/editable_combobox/editable_combobox.cc b/chromium/ui/views/controls/editable_combobox/editable_combobox.cc index 8a0fef03ad6..5f76ed0db20 100644 --- a/chromium/ui/views/controls/editable_combobox/editable_combobox.cc +++ b/chromium/ui/views/controls/editable_combobox/editable_combobox.cc @@ -9,8 +9,8 @@ #include <vector> #include "base/bind.h" +#include "base/check_op.h" #include "base/i18n/rtl.h" -#include "base/logging.h" #include "base/macros.h" #include "base/strings/string16.h" #include "build/build_config.h" @@ -226,7 +226,9 @@ class EditableCombobox::EditableComboboxMenuModel int GetGroupIdAt(int index) const override { return -1; } - bool GetIconAt(int index, gfx::Image* icon) const override { return false; } + ui::ImageModel GetIconAt(int index) const override { + return ui::ImageModel(); + } ui::ButtonMenuItemModel* GetButtonMenuItemAt(int index) const override { return nullptr; @@ -425,6 +427,14 @@ void EditableCombobox::RequestFocus() { textfield_->RequestFocus(); } +bool EditableCombobox::GetNeedsNotificationWhenVisibleBoundsChange() const { + return true; +} + +void EditableCombobox::OnVisibleBoundsChanged() { + CloseMenu(); +} + //////////////////////////////////////////////////////////////////////////////// // EditableCombobox, TextfieldController overrides: diff --git a/chromium/ui/views/controls/editable_combobox/editable_combobox.h b/chromium/ui/views/controls/editable_combobox/editable_combobox.h index 7228d52463a..c0f6b26fad8 100644 --- a/chromium/ui/views/controls/editable_combobox/editable_combobox.h +++ b/chromium/ui/views/controls/editable_combobox/editable_combobox.h @@ -124,6 +124,8 @@ class VIEWS_EXPORT EditableCombobox : public View, void OnThemeChanged() override; void GetAccessibleNodeData(ui::AXNodeData* node_data) override; void RequestFocus() override; + bool GetNeedsNotificationWhenVisibleBoundsChange() const override; + void OnVisibleBoundsChanged() override; // Overridden from TextfieldController: void ContentsChanged(Textfield* sender, diff --git a/chromium/ui/views/controls/highlight_path_generator.cc b/chromium/ui/views/controls/highlight_path_generator.cc index 206f850c916..01920dd775d 100644 --- a/chromium/ui/views/controls/highlight_path_generator.cc +++ b/chromium/ui/views/controls/highlight_path_generator.cc @@ -7,18 +7,13 @@ #include <algorithm> #include "third_party/skia/include/core/SkRect.h" +#include "ui/gfx/rrect_f.h" #include "ui/gfx/skia_util.h" #include "ui/views/view.h" #include "ui/views/view_class_properties.h" namespace views { -HighlightPathGenerator::RoundRect::RoundRect() = default; - -HighlightPathGenerator::RoundRect::RoundRect(const gfx::RectF& bounds, - float corner_radius) - : bounds(bounds), corner_radius(corner_radius) {} - HighlightPathGenerator::HighlightPathGenerator() : HighlightPathGenerator(gfx::Insets()) {} @@ -35,8 +30,8 @@ void HighlightPathGenerator::Install( } // static -base::Optional<HighlightPathGenerator::RoundRect> -HighlightPathGenerator::GetRoundRectForView(const View* view) { +base::Optional<gfx::RRectF> HighlightPathGenerator::GetRoundRectForView( + const View* view) { HighlightPathGenerator* path_generator = view->GetProperty(kHighlightPathGeneratorKey); return path_generator ? path_generator->GetRoundRect(view) : base::nullopt; @@ -44,28 +39,26 @@ HighlightPathGenerator::GetRoundRectForView(const View* view) { SkPath HighlightPathGenerator::GetHighlightPath(const View* view) { // A rounded rectangle must be supplied if using this default implementation. - base::Optional<HighlightPathGenerator::RoundRect> round_rect = - GetRoundRect(view); + base::Optional<gfx::RRectF> round_rect = GetRoundRect(view); DCHECK(round_rect); - return SkPath().addRRect( - SkRRect{gfx::RRectF(round_rect->bounds, round_rect->corner_radius)}); + return SkPath().addRRect(SkRRect{*round_rect}); } -base::Optional<HighlightPathGenerator::RoundRect> -HighlightPathGenerator::GetRoundRect(const gfx::RectF& rect) { +base::Optional<gfx::RRectF> HighlightPathGenerator::GetRoundRect( + const gfx::RectF& rect) { return base::nullopt; } -base::Optional<HighlightPathGenerator::RoundRect> -HighlightPathGenerator::GetRoundRect(const View* view) { +base::Optional<gfx::RRectF> HighlightPathGenerator::GetRoundRect( + const View* view) { gfx::Rect bounds(view->GetLocalBounds()); bounds.Inset(insets_); return GetRoundRect(gfx::RectF(bounds)); } -base::Optional<HighlightPathGenerator::RoundRect> -EmptyHighlightPathGenerator::GetRoundRect(const gfx::RectF& rect) { - return HighlightPathGenerator::RoundRect(); +base::Optional<gfx::RRectF> EmptyHighlightPathGenerator::GetRoundRect( + const gfx::RectF& rect) { + return gfx::RRectF(); } void InstallEmptyHighlightPathGenerator(View* view) { @@ -73,9 +66,9 @@ void InstallEmptyHighlightPathGenerator(View* view) { view, std::make_unique<EmptyHighlightPathGenerator>()); } -base::Optional<HighlightPathGenerator::RoundRect> -RectHighlightPathGenerator::GetRoundRect(const gfx::RectF& rect) { - return HighlightPathGenerator::RoundRect{rect, /*corner_radius=*/0}; +base::Optional<gfx::RRectF> RectHighlightPathGenerator::GetRoundRect( + const gfx::RectF& rect) { + return gfx::RRectF(rect); } void InstallRectHighlightPathGenerator(View* view) { @@ -87,13 +80,13 @@ CircleHighlightPathGenerator::CircleHighlightPathGenerator( const gfx::Insets& insets) : HighlightPathGenerator(insets) {} -base::Optional<HighlightPathGenerator::RoundRect> -CircleHighlightPathGenerator::GetRoundRect(const gfx::RectF& rect) { +base::Optional<gfx::RRectF> CircleHighlightPathGenerator::GetRoundRect( + const gfx::RectF& rect) { gfx::RectF bounds = rect; const float corner_radius = std::min(bounds.width(), bounds.height()) / 2.f; bounds.ClampToCenteredSize( gfx::SizeF(corner_radius * 2.f, corner_radius * 2.f)); - return HighlightPathGenerator::RoundRect{bounds, corner_radius}; + return gfx::RRectF(bounds, corner_radius); } void InstallCircleHighlightPathGenerator(View* view) { @@ -124,11 +117,11 @@ FixedSizeCircleHighlightPathGenerator::FixedSizeCircleHighlightPathGenerator( int radius) : radius_(radius) {} -base::Optional<HighlightPathGenerator::RoundRect> -FixedSizeCircleHighlightPathGenerator::GetRoundRect(const gfx::RectF& rect) { +base::Optional<gfx::RRectF> FixedSizeCircleHighlightPathGenerator::GetRoundRect( + const gfx::RectF& rect) { gfx::RectF bounds = rect; bounds.ClampToCenteredSize(gfx::SizeF(radius_ * 2, radius_ * 2)); - return HighlightPathGenerator::RoundRect{bounds, radius_}; + return gfx::RRectF(bounds, radius_); } void InstallFixedSizeCircleHighlightPathGenerator(View* view, int radius) { @@ -141,9 +134,9 @@ RoundRectHighlightPathGenerator::RoundRectHighlightPathGenerator( int corner_radius) : HighlightPathGenerator(insets), corner_radius_(corner_radius) {} -base::Optional<HighlightPathGenerator::RoundRect> -RoundRectHighlightPathGenerator::GetRoundRect(const gfx::RectF& rect) { - return HighlightPathGenerator::RoundRect{rect, corner_radius_}; +base::Optional<gfx::RRectF> RoundRectHighlightPathGenerator::GetRoundRect( + const gfx::RectF& rect) { + return gfx::RRectF(rect, corner_radius_); } void InstallRoundRectHighlightPathGenerator(View* view, diff --git a/chromium/ui/views/controls/highlight_path_generator.h b/chromium/ui/views/controls/highlight_path_generator.h index 427c5271402..e64d0d48b1b 100644 --- a/chromium/ui/views/controls/highlight_path_generator.h +++ b/chromium/ui/views/controls/highlight_path_generator.h @@ -11,9 +11,12 @@ #include "third_party/skia/include/core/SkPath.h" #include "ui/gfx/geometry/insets.h" #include "ui/gfx/geometry/rect_f.h" -#include "ui/gfx/geometry/rounded_corners_f.h" #include "ui/views/views_export.h" +namespace gfx { +class RRectF; +} + namespace views { class View; @@ -23,17 +26,6 @@ class View; // effects. class VIEWS_EXPORT HighlightPathGenerator { public: - struct VIEWS_EXPORT RoundRect { - // TODO(http://crbug.com/1056490): Remove these constructors and have - // callsites create a gfx::RoundedCornersF explicitly, or replace this - // struct with a gfx::RRectF. - RoundRect(); - RoundRect(const gfx::RectF& bounds, float corner_radius); - - gfx::RectF bounds; - gfx::RoundedCornersF corner_radius; - }; - // TODO(http://crbug.com/1056490): Remove this constructor in favor of the one // that takes |insets|. HighlightPathGenerator(); @@ -45,18 +37,18 @@ class VIEWS_EXPORT HighlightPathGenerator { static void Install(View* host, std::unique_ptr<HighlightPathGenerator> generator); - static base::Optional<RoundRect> GetRoundRectForView(const View* view); + static base::Optional<gfx::RRectF> GetRoundRectForView(const View* view); // TODO(http://crbug.com/1056490): Deprecate |GetHighlightPath()| in favor of // |GetRoundRect()|. virtual SkPath GetHighlightPath(const View* view); - // Optionally returns a RoundRect struct which contains data for drawing a + // Optionally returns a gfx::RRectF which contains data for drawing a // highlight. Note that |rect| is in the coordinate system of the view. // TODO(http://crbug.com/1056490): Once |GetHighlightPath()| is deprecated, // make this a pure virtual function and make the return not optional. - virtual base::Optional<RoundRect> GetRoundRect(const gfx::RectF& rect); - base::Optional<RoundRect> GetRoundRect(const View* view); + virtual base::Optional<gfx::RRectF> GetRoundRect(const gfx::RectF& rect); + base::Optional<gfx::RRectF> GetRoundRect(const View* view); private: const gfx::Insets insets_; @@ -74,7 +66,7 @@ class VIEWS_EXPORT EmptyHighlightPathGenerator : public HighlightPathGenerator { delete; // HighlightPathGenerator: - base::Optional<RoundRect> GetRoundRect(const gfx::RectF& rect) override; + base::Optional<gfx::RRectF> GetRoundRect(const gfx::RectF& rect) override; }; void VIEWS_EXPORT InstallEmptyHighlightPathGenerator(View* view); @@ -89,7 +81,7 @@ class VIEWS_EXPORT RectHighlightPathGenerator : public HighlightPathGenerator { delete; // HighlightPathGenerator: - base::Optional<RoundRect> GetRoundRect(const gfx::RectF& rect) override; + base::Optional<gfx::RRectF> GetRoundRect(const gfx::RectF& rect) override; }; void VIEWS_EXPORT InstallRectHighlightPathGenerator(View* view); @@ -105,7 +97,7 @@ class VIEWS_EXPORT CircleHighlightPathGenerator delete; // HighlightPathGenerator: - base::Optional<RoundRect> GetRoundRect(const gfx::RectF& rect) override; + base::Optional<gfx::RRectF> GetRoundRect(const gfx::RectF& rect) override; }; void VIEWS_EXPORT InstallCircleHighlightPathGenerator(View* view); @@ -143,7 +135,7 @@ class VIEWS_EXPORT FixedSizeCircleHighlightPathGenerator const FixedSizeCircleHighlightPathGenerator&) = delete; // HighlightPathGenerator: - base::Optional<RoundRect> GetRoundRect(const gfx::RectF& rect) override; + base::Optional<gfx::RRectF> GetRoundRect(const gfx::RectF& rect) override; private: const int radius_; @@ -164,7 +156,7 @@ class VIEWS_EXPORT RoundRectHighlightPathGenerator const RoundRectHighlightPathGenerator&) = delete; // HighlightPathGenerator: - base::Optional<RoundRect> GetRoundRect(const gfx::RectF& rect) override; + base::Optional<gfx::RRectF> GetRoundRect(const gfx::RectF& rect) override; private: const int corner_radius_; diff --git a/chromium/ui/views/controls/image_view.cc b/chromium/ui/views/controls/image_view.cc index e430b58317f..4ca005dbdc9 100644 --- a/chromium/ui/views/controls/image_view.cc +++ b/chromium/ui/views/controls/image_view.cc @@ -6,7 +6,7 @@ #include <utility> -#include "base/logging.h" +#include "base/check_op.h" #include "cc/paint/paint_flags.h" #include "skia/ext/image_operations.h" #include "ui/accessibility/ax_enums.mojom.h" diff --git a/chromium/ui/views/controls/label.cc b/chromium/ui/views/controls/label.cc index c15c27ae75b..ad6af149e28 100644 --- a/chromium/ui/views/controls/label.cc +++ b/chromium/ui/views/controls/label.cc @@ -598,10 +598,6 @@ std::unique_ptr<gfx::RenderText> Label::CreateRenderText() const { return render_text; } -void Label::PaintFocusRing(gfx::Canvas* canvas) const { - // No focus ring by default. -} - gfx::Rect Label::GetTextBounds() const { MaybeBuildDisplayText(); @@ -645,8 +641,6 @@ void Label::OnBoundsChanged(const gfx::Rect& previous_bounds) { void Label::OnPaint(gfx::Canvas* canvas) { View::OnPaint(canvas); PaintText(canvas); - if (HasFocus()) - PaintFocusRing(canvas); } void Label::OnThemeChanged() { @@ -895,9 +889,9 @@ bool Label::IsCommandIdChecked(int command_id) const { bool Label::IsCommandIdEnabled(int command_id) const { switch (command_id) { - case IDS_APP_COPY: + case MenuCommands::kCopy: return HasSelection() && !GetObscured(); - case IDS_APP_SELECT_ALL: + case MenuCommands::kSelectAll: return GetRenderTextForSelectionController() && !GetText().empty(); } return false; @@ -905,10 +899,10 @@ bool Label::IsCommandIdEnabled(int command_id) const { void Label::ExecuteCommand(int command_id, int event_flags) { switch (command_id) { - case IDS_APP_COPY: + case MenuCommands::kCopy: CopyToClipboard(); break; - case IDS_APP_SELECT_ALL: + case MenuCommands::kSelectAll: SelectAll(); DCHECK(HasSelection()); UpdateSelectionClipboard(); @@ -921,11 +915,11 @@ void Label::ExecuteCommand(int command_id, int event_flags) { bool Label::GetAcceleratorForCommandId(int command_id, ui::Accelerator* accelerator) const { switch (command_id) { - case IDS_APP_COPY: + case MenuCommands::kCopy: *accelerator = ui::Accelerator(ui::VKEY_C, ui::EF_CONTROL_DOWN); return true; - case IDS_APP_SELECT_ALL: + case MenuCommands::kSelectAll: *accelerator = ui::Accelerator(ui::VKEY_A, ui::EF_CONTROL_DOWN); return true; @@ -1101,8 +1095,8 @@ void Label::CopyToClipboard() { } void Label::BuildContextMenuContents() { - context_menu_contents_.AddItemWithStringId(IDS_APP_COPY, IDS_APP_COPY); - context_menu_contents_.AddItemWithStringId(IDS_APP_SELECT_ALL, + context_menu_contents_.AddItemWithStringId(MenuCommands::kCopy, IDS_APP_COPY); + context_menu_contents_.AddItemWithStringId(MenuCommands::kSelectAll, IDS_APP_SELECT_ALL); } diff --git a/chromium/ui/views/controls/label.h b/chromium/ui/views/controls/label.h index 4cb533caa9c..dc690b22dc2 100644 --- a/chromium/ui/views/controls/label.h +++ b/chromium/ui/views/controls/label.h @@ -35,6 +35,12 @@ class VIEWS_EXPORT Label : public View, public: METADATA_HEADER(Label); + enum MenuCommands { + kCopy = 1, + kSelectAll, + kLastCommandId = kSelectAll, + }; + // Helper to construct a Label that doesn't use the views typography spec. // Using this causes Label to obtain colors from ui::NativeTheme and line // spacing from gfx::FontList::GetHeight(). @@ -261,9 +267,6 @@ class VIEWS_EXPORT Label : public View, // Create a single RenderText instance to actually be painted. virtual std::unique_ptr<gfx::RenderText> CreateRenderText() const; - // Draw a focus ring. The default implementation does nothing. - virtual void PaintFocusRing(gfx::Canvas* canvas) const; - // Returns the preferred size and position of the text in local coordinates, // which may exceed the local bounds of the label. gfx::Rect GetTextBounds() const; diff --git a/chromium/ui/views/controls/label_unittest.cc b/chromium/ui/views/controls/label_unittest.cc index d3f01a185fe..763a169b27e 100644 --- a/chromium/ui/views/controls/label_unittest.cc +++ b/chromium/ui/views/controls/label_unittest.cc @@ -1384,38 +1384,38 @@ TEST_F(LabelSelectionTest, ContextMenuContents) { label()->SetText(ASCIIToUTF16("Label context menu")); label()->SizeToPreferredSize(); - // A non-selectable label would not show a context menu and both COPY and - // SELECT_ALL context menu items should be disabled for it. - EXPECT_FALSE(IsMenuCommandEnabled(IDS_APP_COPY)); - EXPECT_FALSE(IsMenuCommandEnabled(IDS_APP_SELECT_ALL)); + // A non-selectable label should not show a context menu and both copy and + // select-all context menu items should be disabled for it. + EXPECT_FALSE(IsMenuCommandEnabled(Label::MenuCommands::kCopy)); + EXPECT_FALSE(IsMenuCommandEnabled(Label::MenuCommands::kSelectAll)); - // For a selectable label with no selection, only SELECT_ALL should be + // For a selectable label with no selection, only kSelectAll should be // enabled. ASSERT_TRUE(label()->SetSelectable(true)); - EXPECT_FALSE(IsMenuCommandEnabled(IDS_APP_COPY)); - EXPECT_TRUE(IsMenuCommandEnabled(IDS_APP_SELECT_ALL)); + EXPECT_FALSE(IsMenuCommandEnabled(Label::MenuCommands::kCopy)); + EXPECT_TRUE(IsMenuCommandEnabled(Label::MenuCommands::kSelectAll)); - // For a selectable label with a selection, both COPY and SELECT_ALL should be - // enabled. + // For a selectable label with a selection, both copy and select-all should + // be enabled. label()->SelectRange(gfx::Range(0, 4)); - EXPECT_TRUE(IsMenuCommandEnabled(IDS_APP_COPY)); - EXPECT_TRUE(IsMenuCommandEnabled(IDS_APP_SELECT_ALL)); - // Ensure unsupported commands like PASTE are not enabled. - EXPECT_FALSE(IsMenuCommandEnabled(IDS_APP_PASTE)); + EXPECT_TRUE(IsMenuCommandEnabled(Label::MenuCommands::kCopy)); + EXPECT_TRUE(IsMenuCommandEnabled(Label::MenuCommands::kSelectAll)); + // Ensure unsupported commands are not enabled. + EXPECT_FALSE(IsMenuCommandEnabled(Label::MenuCommands::kLastCommandId + 1)); - // An obscured label would not show a context menu and both COPY and - // SELECT_ALL should be disabled for it. + // An obscured label would not show a context menu and both copy and + // select-all should be disabled for it. label()->SetObscured(true); EXPECT_FALSE(label()->GetSelectable()); - EXPECT_FALSE(IsMenuCommandEnabled(IDS_APP_COPY)); - EXPECT_FALSE(IsMenuCommandEnabled(IDS_APP_SELECT_ALL)); + EXPECT_FALSE(IsMenuCommandEnabled(Label::MenuCommands::kCopy)); + EXPECT_FALSE(IsMenuCommandEnabled(Label::MenuCommands::kSelectAll)); label()->SetObscured(false); - // For an empty label, both COPY and SELECT_ALL should be disabled. + // For an empty label, both copy and select-all should be disabled. label()->SetText(base::string16()); ASSERT_TRUE(label()->SetSelectable(true)); - EXPECT_FALSE(IsMenuCommandEnabled(IDS_APP_COPY)); - EXPECT_FALSE(IsMenuCommandEnabled(IDS_APP_SELECT_ALL)); + EXPECT_FALSE(IsMenuCommandEnabled(Label::MenuCommands::kCopy)); + EXPECT_FALSE(IsMenuCommandEnabled(Label::MenuCommands::kSelectAll)); } } // namespace views diff --git a/chromium/ui/views/controls/link.cc b/chromium/ui/views/controls/link.cc index 5c292fc21de..29348fd8a9d 100644 --- a/chromium/ui/views/controls/link.cc +++ b/chromium/ui/views/controls/link.cc @@ -6,7 +6,7 @@ #include "build/build_config.h" -#include "base/logging.h" +#include "base/check.h" #include "base/strings/utf_string_conversions.h" #include "ui/accessibility/ax_enums.mojom.h" #include "ui/accessibility/ax_node_data.h" @@ -22,9 +22,6 @@ namespace views { -// static -constexpr gfx::Insets Link::kFocusBorderPadding; - Link::Link(const base::string16& title, int text_context, int text_style) : Label(title, text_context, text_style) { RecalculateFont(); @@ -39,12 +36,6 @@ Link::Link(const base::string16& title, int text_context, int text_style) Link::~Link() = default; -Link::FocusStyle Link::GetFocusStyle() const { - // Use an underline to indicate focus unless the link is always drawn with an - // underline. - return underline_ ? FocusStyle::kRing : FocusStyle::kUnderline; -} - SkColor Link::GetColor() const { // TODO(tapted): Use style::GetColor(). const ui::NativeTheme* theme = GetNativeTheme(); @@ -60,25 +51,6 @@ SkColor Link::GetColor() const { : ui::NativeTheme::kColorId_LinkEnabled); } -void Link::PaintFocusRing(gfx::Canvas* canvas) const { - if (GetFocusStyle() == FocusStyle::kRing) { - gfx::Rect focus_ring_bounds = GetTextBounds(); - focus_ring_bounds.Inset(-kFocusBorderPadding); - focus_ring_bounds.Intersect(GetLocalBounds()); - canvas->DrawFocusRect(focus_ring_bounds); - } -} - -gfx::Insets Link::GetInsets() const { - gfx::Insets insets = Label::GetInsets(); - if (GetFocusStyle() == FocusStyle::kRing && - GetFocusBehavior() != FocusBehavior::NEVER) { - DCHECK(!GetText().empty()); - insets += kFocusBorderPadding; - } - return insets; -} - gfx::NativeCursor Link::GetCursor(const ui::MouseEvent& event) { if (!GetEnabled()) return gfx::kNullCursor; @@ -214,18 +186,6 @@ bool Link::IsSelectionSupported() const { return false; } -bool Link::GetUnderline() const { - return underline_; -} - -void Link::SetUnderline(bool underline) { - if (underline_ == underline) - return; - underline_ = underline; - RecalculateFont(); - OnPropertyChanged(&underline_, kPropertyEffectsPreferredSizeChanged); -} - void Link::SetPressed(bool pressed) { if (pressed_ != pressed) { pressed_ = pressed; @@ -236,12 +196,8 @@ void Link::SetPressed(bool pressed) { } void Link::RecalculateFont() { - // Underline the link if it is enabled and |underline_| is true. Also - // underline to indicate focus when that's the style. const int style = font_list().GetFontStyle(); - const bool underline = - underline_ || (HasFocus() && GetFocusStyle() == FocusStyle::kUnderline); - const int intended_style = (GetEnabled() && underline) + const int intended_style = (GetEnabled() && HasFocus()) ? (style | gfx::Font::UNDERLINE) : (style & ~gfx::Font::UNDERLINE); @@ -262,15 +218,9 @@ void Link::ConfigureFocus() { } } -DEFINE_ENUM_CONVERTERS(Link::FocusStyle, - {Link::FocusStyle::kUnderline, - base::ASCIIToUTF16("UNDERLINE")}, - {Link::FocusStyle::kRing, base::ASCIIToUTF16("RING")}) BEGIN_METADATA(Link) METADATA_PARENT_CLASS(Label) ADD_READONLY_PROPERTY_METADATA(Link, SkColor, Color) -ADD_READONLY_PROPERTY_METADATA(Link, Link::FocusStyle, FocusStyle) -ADD_PROPERTY_METADATA(Link, bool, Underline) END_METADATA() } // namespace views diff --git a/chromium/ui/views/controls/link.h b/chromium/ui/views/controls/link.h index 684e05d7747..51b5a2341a5 100644 --- a/chromium/ui/views/controls/link.h +++ b/chromium/ui/views/controls/link.h @@ -35,24 +35,11 @@ class VIEWS_EXPORT Link : public Label { using ClickedCallback = base::RepeatingCallback<void(Link* source, int event_flags)>; - // The padding for the focus ring border when rendering a focused Link with - // FocusStyle::kRing. - static constexpr gfx::Insets kFocusBorderPadding = gfx::Insets(1); - - // How the Link is styled when focused. - enum class FocusStyle { - kUnderline, // An underline style is added to the text only when focused. - kRing, // A focus ring is drawn around the View. - }; - explicit Link(const base::string16& title, int text_context = style::CONTEXT_LABEL, int text_style = style::STYLE_LINK); ~Link() override; - // Returns the current FocusStyle of this Link. - FocusStyle GetFocusStyle() const; - // Allow providing callbacks that expect either zero or two args, since many // callers don't care about the arguments and can avoid adapter functions this // way. @@ -69,8 +56,6 @@ class VIEWS_EXPORT Link : public Label { SkColor GetColor() const; // Label: - void PaintFocusRing(gfx::Canvas* canvas) const override; - gfx::Insets GetInsets() const override; gfx::NativeCursor GetCursor(const ui::MouseEvent& event) override; bool CanProcessEventsWithinSubtree() const override; bool OnMousePressed(const ui::MouseEvent& event) override; @@ -89,13 +74,6 @@ class VIEWS_EXPORT Link : public Label { void SetEnabledColor(SkColor color) override; bool IsSelectionSupported() const override; - bool GetUnderline() const; - // TODO(estade): almost all the places that call this pass false. With - // Harmony, false is already the default so those callsites can be removed. - // TODO(tapted): Then remove all callsites when client code sets a correct - // typography style and derives this from style::GetFont(STYLE_LINK). - void SetUnderline(bool underline); - private: void SetPressed(bool pressed); @@ -105,9 +83,6 @@ class VIEWS_EXPORT Link : public Label { ClickedCallback callback_; - // Whether the link should be underlined when enabled. - bool underline_ = false; - // Whether the link is currently pressed. bool pressed_ = false; diff --git a/chromium/ui/views/controls/menu/menu_closure_animation_mac.mm b/chromium/ui/views/controls/menu/menu_closure_animation_mac.mm index eaee6349d8e..f74acc5d65d 100644 --- a/chromium/ui/views/controls/menu/menu_closure_animation_mac.mm +++ b/chromium/ui/views/controls/menu/menu_closure_animation_mac.mm @@ -7,7 +7,8 @@ #import <Cocoa/Cocoa.h> #include "base/bind.h" -#include "base/logging.h" +#include "base/check_op.h" +#include "base/notreached.h" #include "base/threading/thread_task_runner_handle.h" #include "ui/gfx/animation/linear_animation.h" #include "ui/views/controls/menu/menu_item_view.h" diff --git a/chromium/ui/views/controls/menu/menu_config.h b/chromium/ui/views/controls/menu/menu_config.h index 9d0e8aafc84..c84d1377c94 100644 --- a/chromium/ui/views/controls/menu/menu_config.h +++ b/chromium/ui/views/controls/menu/menu_config.h @@ -43,9 +43,6 @@ struct VIEWS_EXPORT MenuConfig { // Font list used by menus. gfx::FontList font_list; - // Color for the arrow to scroll bookmarks. - SkColor arrow_color = SK_ColorBLACK; - // Menu border sizes. The vertical border size does not apply to menus with // rounded corners - those menus always use the corner radius as the vertical // border size. diff --git a/chromium/ui/views/controls/menu/menu_config_win.cc b/chromium/ui/views/controls/menu/menu_config_win.cc index 02ee472e0c4..154e8cf424f 100644 --- a/chromium/ui/views/controls/menu/menu_config_win.cc +++ b/chromium/ui/views/controls/menu/menu_config_win.cc @@ -9,7 +9,7 @@ #include <Vssym32.h> #include <uxtheme.h> -#include "base/logging.h" +#include "base/check.h" #include "base/win/scoped_gdi_object.h" #include "ui/gfx/color_utils.h" #include "ui/gfx/system_fonts_win.h" @@ -20,7 +20,6 @@ using ui::NativeTheme; namespace views { void MenuConfig::Init() { - arrow_color = color_utils::GetSysSkColor(COLOR_MENUTEXT); font_list = gfx::FontList(gfx::win::GetSystemFont(gfx::win::SystemFont::kMenu)); diff --git a/chromium/ui/views/controls/menu/menu_controller.cc b/chromium/ui/views/controls/menu/menu_controller.cc index d8c09129ad9..d9938916a53 100644 --- a/chromium/ui/views/controls/menu/menu_controller.cc +++ b/chromium/ui/views/controls/menu/menu_controller.cc @@ -61,6 +61,10 @@ #include "ui/aura/window.h" #endif +#if defined(USE_OZONE) +#include "ui/ozone/public/ozone_platform.h" +#endif + using base::TimeDelta; using ui::OSExchangeData; @@ -100,6 +104,18 @@ bool AcceleratorShouldCancelMenu(const ui::Accelerator& accelerator) { } #endif +bool ShouldIgnoreScreenBoundsForMenus() { +#if defined(USE_OZONE) + // Wayland requires placing menus is screen coordinates. See comment in + // ozone_platform_wayland.cc. + return ui::OzonePlatform::GetInstance() + ->GetPlatformProperties() + .ignore_screen_bounds_for_menus; +#else + return false; +#endif +} + // The amount of time the mouse should be down before a mouse release is // considered intentional. This is to prevent spurious mouse releases from // activating controls, especially when some UI element is revealed under the @@ -776,19 +792,15 @@ void MenuController::OnMouseReleased(SubmenuView* source, return; } } + const int command = part.menu->GetCommand(); if (part.menu->GetDelegate()->ShouldExecuteCommandWithoutClosingMenu( - part.menu->GetCommand(), event)) { - part.menu->GetDelegate()->ExecuteCommand(part.menu->GetCommand(), - event.flags()); + command, event)) { + part.menu->GetDelegate()->ExecuteCommand(command, event.flags()); return; } if (!part.menu->NonIconChildViewsCount() && part.menu->GetDelegate()->IsTriggerableEvent(part.menu, event)) { - base::TimeDelta shown_time = base::TimeTicks::Now() - menu_start_time_; - if (!state_.context_menu || !View::ShouldShowContextMenuOnMousePress() || - shown_time > menu_selection_hold_time) { - Accept(part.menu, event.flags()); - } + Accept(part.menu, event.flags()); return; } } else if (part.type == MenuPart::MENU_ITEM) { @@ -2226,7 +2238,7 @@ gfx::Rect MenuController::CalculateMenuBounds(MenuItemView* item, menu_bounds.set_x(create_on_right ? right_of_parent : left_of_parent); // Everything after this check requires monitor bounds to be non-empty. - if (monitor_bounds.IsEmpty()) + if (ShouldIgnoreScreenBoundsForMenus() || monitor_bounds.IsEmpty()) return menu_bounds; // Menu does not actually fit where it was placed, move it to the other side @@ -2258,7 +2270,8 @@ gfx::Rect MenuController::CalculateMenuBounds(MenuItemView* item, anchor_bounds.x() + (anchor_bounds.width() - menu_bounds.width()) / 2; menu_bounds.set_x(horizontally_centered); menu_bounds.set_y(above_anchor - kTouchYPadding); - if (menu_bounds.y() < monitor_bounds.y()) + if (!ShouldIgnoreScreenBoundsForMenus() && + menu_bounds.y() < monitor_bounds.y()) menu_bounds.set_y(anchor_bounds.y() + kTouchYPadding); } @@ -2268,7 +2281,7 @@ gfx::Rect MenuController::CalculateMenuBounds(MenuItemView* item, } // Everything beyond this point requires monitor bounds to be non-empty. - if (monitor_bounds.IsEmpty()) + if (ShouldIgnoreScreenBoundsForMenus() || monitor_bounds.IsEmpty()) return menu_bounds; // If the menu position is below or above the anchor bounds, force it to fit diff --git a/chromium/ui/views/controls/menu/menu_controller_unittest.cc b/chromium/ui/views/controls/menu/menu_controller_unittest.cc index 14d45fb9dcf..3176d01e445 100644 --- a/chromium/ui/views/controls/menu/menu_controller_unittest.cc +++ b/chromium/ui/views/controls/menu/menu_controller_unittest.cc @@ -6,7 +6,6 @@ #include "base/bind.h" #include "base/callback.h" -#include "base/logging.h" #include "base/macros.h" #include "base/message_loop/message_loop_current.h" #include "base/single_thread_task_runner.h" @@ -51,18 +50,34 @@ #if defined(USE_X11) #include "ui/events/test/events_test_utils_x11.h" -#include "ui/gfx/x/x11.h" +#include "ui/gfx/x/x11.h" // nogncheck #endif #if defined(OS_CHROMEOS) #include "ui/base/ui_base_features.h" #endif +#if defined(USE_OZONE) +#include "ui/ozone/public/ozone_platform.h" +#endif + namespace views { namespace test { namespace { +bool ShouldIgnoreScreenBoundsForMenus() { +#if defined(USE_OZONE) + // Wayland requires placing menus is screen coordinates. See comment in + // ozone_platform_wayland.cc. + return ui::OzonePlatform::GetInstance() + ->GetPlatformProperties() + .ignore_screen_bounds_for_menus; +#else + return false; +#endif +} + // Test implementation of MenuControllerDelegate that only reports the values // called of OnMenuClosed. class TestMenuControllerDelegate : public internal::MenuControllerDelegate { @@ -1518,7 +1533,7 @@ TEST_F(MenuControllerTest, NoTouchCloseWhenSendingGesturesToOwner) { location.Offset(1, 1); ui::TouchEvent touch_event( ui::ET_TOUCH_PRESSED, location, ui::EventTimeForNow(), - ui::PointerDetails(ui::EventPointerType::POINTER_TYPE_TOUCH, 0)); + ui::PointerDetails(ui::EventPointerType::kTouch, 0)); controller->OnTouchEvent(sub_menu, &touch_event); // Menu should still be visible. @@ -1592,9 +1607,8 @@ TEST_F(MenuControllerTest, AsynchronousTouchEventRepostEvent) { sub_menu->ShowAt(owner(), item->bounds(), false); gfx::Point location(sub_menu->bounds().bottom_right()); location.Offset(1, 1); - ui::TouchEvent event( - ui::ET_TOUCH_PRESSED, location, ui::EventTimeForNow(), - ui::PointerDetails(ui::EventPointerType::POINTER_TYPE_TOUCH, 0)); + ui::TouchEvent event(ui::ET_TOUCH_PRESSED, location, ui::EventTimeForNow(), + ui::PointerDetails(ui::EventPointerType::kTouch, 0)); controller->OnTouchEvent(sub_menu, &event); views::test::WaitForMenuClosureAnimation(); @@ -1712,6 +1726,8 @@ TEST_F(MenuControllerTest, ArrowKeysAtEnds) { TEST_F(MenuControllerTest, CalculateMenuBoundsBestFitTest) { MenuBoundsOptions options; gfx::Rect expected; + const bool ignore_screen_bounds_for_menus = + ShouldIgnoreScreenBoundsForMenus(); // Fits in all locations -> placed below. options.anchor_bounds = @@ -1730,9 +1746,13 @@ TEST_F(MenuControllerTest, CalculateMenuBoundsBestFitTest) { options.monitor_bounds = gfx::Rect(0, 0, options.anchor_bounds.right() + options.menu_size.width(), options.anchor_bounds.bottom()); - expected = gfx::Rect(options.anchor_bounds.x(), - options.anchor_bounds.y() - options.menu_size.height(), - options.menu_size.width(), options.menu_size.height()); + if (ignore_screen_bounds_for_menus) { + expected = gfx::Rect(options.anchor_bounds.origin(), options.menu_size); + } else { + expected = gfx::Rect(options.anchor_bounds.x(), + options.anchor_bounds.y() - options.menu_size.height(), + options.menu_size.width(), options.menu_size.height()); + } EXPECT_EQ(expected, CalculateMenuBounds(options)); // Fits on both sides, prefer right -> placed right. @@ -1741,10 +1761,15 @@ TEST_F(MenuControllerTest, CalculateMenuBoundsBestFitTest) { options.monitor_bounds = gfx::Rect(0, 0, options.anchor_bounds.right() + options.menu_size.width(), options.menu_size.height()); - expected = - gfx::Rect(options.anchor_bounds.right(), - options.monitor_bounds.bottom() - options.menu_size.height(), - options.menu_size.width(), options.menu_size.height()); + if (ignore_screen_bounds_for_menus) { + expected = gfx::Rect(options.anchor_bounds.origin(), options.menu_size); + } else { + expected = + gfx::Rect(options.anchor_bounds.right(), + options.monitor_bounds.bottom() - options.menu_size.height(), + options.menu_size.width(), options.menu_size.height()); + } + EXPECT_EQ(expected, CalculateMenuBounds(options)); // Fits only on left -> placed left. @@ -1752,10 +1777,14 @@ TEST_F(MenuControllerTest, CalculateMenuBoundsBestFitTest) { options.menu_size.height() / 2, 0, 0); options.monitor_bounds = gfx::Rect(0, 0, options.anchor_bounds.right(), options.menu_size.height()); - expected = - gfx::Rect(options.anchor_bounds.x() - options.menu_size.width(), - options.monitor_bounds.bottom() - options.menu_size.height(), - options.menu_size.width(), options.menu_size.height()); + if (ignore_screen_bounds_for_menus) { + expected = gfx::Rect(options.anchor_bounds.origin(), options.menu_size); + } else { + expected = + gfx::Rect(options.anchor_bounds.x() - options.menu_size.width(), + options.monitor_bounds.bottom() - options.menu_size.height(), + options.menu_size.width(), options.menu_size.height()); + } EXPECT_EQ(expected, CalculateMenuBounds(options)); // Fits on both sides, prefer left -> placed left. @@ -1765,10 +1794,17 @@ TEST_F(MenuControllerTest, CalculateMenuBoundsBestFitTest) { options.monitor_bounds = gfx::Rect(0, 0, options.anchor_bounds.right() + options.menu_size.width(), options.menu_size.height()); - expected = - gfx::Rect(options.anchor_bounds.x() - options.menu_size.width(), - options.monitor_bounds.bottom() - options.menu_size.height(), - options.menu_size.width(), options.menu_size.height()); + if (ignore_screen_bounds_for_menus) { + expected = + gfx::Rect({options.anchor_bounds.right() - options.menu_size.width(), + options.anchor_bounds.origin().y()}, + options.menu_size); + } else { + expected = + gfx::Rect(options.anchor_bounds.x() - options.menu_size.width(), + options.monitor_bounds.bottom() - options.menu_size.height(), + options.menu_size.width(), options.menu_size.height()); + } EXPECT_EQ(expected, CalculateMenuBounds(options)); // Fits only on right -> placed right. @@ -1776,10 +1812,17 @@ TEST_F(MenuControllerTest, CalculateMenuBoundsBestFitTest) { options.monitor_bounds = gfx::Rect(0, 0, options.anchor_bounds.right() + options.menu_size.width(), options.menu_size.height()); - expected = - gfx::Rect(options.anchor_bounds.right(), - options.monitor_bounds.bottom() - options.menu_size.height(), - options.menu_size.width(), options.menu_size.height()); + if (ignore_screen_bounds_for_menus) { + expected = + gfx::Rect({options.anchor_bounds.right() - options.menu_size.width(), + options.anchor_bounds.origin().y()}, + options.menu_size); + } else { + expected = + gfx::Rect(options.anchor_bounds.right(), + options.monitor_bounds.bottom() - options.menu_size.height(), + options.menu_size.width(), options.menu_size.height()); + } EXPECT_EQ(expected, CalculateMenuBounds(options)); } @@ -1816,11 +1859,20 @@ TEST_F(MenuControllerTest, CalculateMenuBoundsAnchorTest) { // Menu does not fit above -> placed below. options.anchor_bounds = gfx::Rect(options.menu_size.height() / 2, options.menu_size.width(), 0, 0); - expected = gfx::Rect( - options.anchor_bounds.x() + - (options.anchor_bounds.width() - options.menu_size.width()) / 2, - options.anchor_bounds.y() + kTouchYPadding, options.menu_size.width(), - options.menu_size.height()); + if (ShouldIgnoreScreenBoundsForMenus()) { + expected = gfx::Rect( + options.anchor_bounds.x() + + (options.anchor_bounds.width() - options.menu_size.width()) / 2, + options.anchor_bounds.y() - options.anchor_bounds.bottom() - + kTouchYPadding, + options.menu_size.width(), options.menu_size.height()); + } else { + expected = gfx::Rect( + options.anchor_bounds.x() + + (options.anchor_bounds.width() - options.menu_size.width()) / 2, + options.anchor_bounds.y() + kTouchYPadding, options.menu_size.width(), + options.menu_size.height()); + } EXPECT_EQ(expected, CalculateMenuBounds(options)); } @@ -1951,6 +2003,11 @@ TEST_P(MenuControllerTest, TestSubmenuFitsOnScreen) { // squished or move above the anchor when it grows vertically and horizontally // beyond the monitor bounds. TEST_F(MenuControllerTest, GrowingMenuMovesLaterallyNotVertically) { + // We can't know the position of windows in Wayland. Thus, this case is not + // valid for Wayland. + if (ShouldIgnoreScreenBoundsForMenus()) + return; + MenuBoundsOptions options; options.monitor_bounds = gfx::Rect(0, 0, 100, 100); // The anchor should be near the bottom right side of the screen. diff --git a/chromium/ui/views/controls/menu/menu_host.cc b/chromium/ui/views/controls/menu/menu_host.cc index 232a7de1b54..0b9230bda55 100644 --- a/chromium/ui/views/controls/menu/menu_host.cc +++ b/chromium/ui/views/controls/menu/menu_host.cc @@ -7,7 +7,8 @@ #include <utility> #include "base/auto_reset.h" -#include "base/logging.h" +#include "base/check_op.h" +#include "base/notreached.h" #include "base/trace_event/trace_event.h" #include "build/build_config.h" #include "ui/aura/window_observer.h" diff --git a/chromium/ui/views/controls/menu/menu_host_root_view.cc b/chromium/ui/views/controls/menu/menu_host_root_view.cc index bbb1290858d..890b56554ef 100644 --- a/chromium/ui/views/controls/menu/menu_host_root_view.cc +++ b/chromium/ui/views/controls/menu/menu_host_root_view.cc @@ -94,4 +94,7 @@ MenuController* MenuHostRootView::GetMenuControllerForInputEvents() { : nullptr; } +BEGIN_METADATA(MenuHostRootView) +METADATA_PARENT_CLASS(RootView) +END_METADATA() } // namespace views diff --git a/chromium/ui/views/controls/menu/menu_host_root_view.h b/chromium/ui/views/controls/menu/menu_host_root_view.h index e37dd792677..85548317aa2 100644 --- a/chromium/ui/views/controls/menu/menu_host_root_view.h +++ b/chromium/ui/views/controls/menu/menu_host_root_view.h @@ -21,6 +21,8 @@ class SubmenuView; // such that when MenuHostRootView is deleted it doesn't delete the menu items. class MenuHostRootView : public internal::RootView { public: + METADATA_HEADER(MenuHostRootView); + MenuHostRootView(Widget* widget, SubmenuView* submenu); void ClearSubmenu() { submenu_ = nullptr; } diff --git a/chromium/ui/views/controls/menu/menu_item_view.cc b/chromium/ui/views/controls/menu/menu_item_view.cc index 6f45e5ac5d1..d00618f37c3 100644 --- a/chromium/ui/views/controls/menu/menu_item_view.cc +++ b/chromium/ui/views/controls/menu/menu_item_view.cc @@ -923,7 +923,7 @@ void MenuItemView::PaintButton(gfx::Canvas* canvas, PaintButtonMode mode) { const gfx::VectorIcon& radio_icon = toggled ? kMenuRadioSelectedIcon : kMenuRadioEmptyIcon; const SkColor radio_icon_color = GetNativeTheme()->GetSystemColor( - toggled ? ui::NativeTheme::kColorId_ButtonEnabledColor + toggled ? ui::NativeTheme::kColorId_ButtonCheckedColor : ui::NativeTheme::kColorId_ButtonUncheckedColor); radio_check_image_view_->SetImage( gfx::CreateVectorIcon(radio_icon, kMenuCheckSize, radio_icon_color)); diff --git a/chromium/ui/views/controls/menu/menu_model_adapter.cc b/chromium/ui/views/controls/menu/menu_model_adapter.cc index 88a868cf6f9..79af9195b11 100644 --- a/chromium/ui/views/controls/menu/menu_model_adapter.cc +++ b/chromium/ui/views/controls/menu/menu_model_adapter.cc @@ -6,7 +6,8 @@ #include <utility> -#include "base/logging.h" +#include "base/check.h" +#include "base/notreached.h" #include "ui/base/models/menu_model.h" #include "ui/gfx/image/image.h" #include "ui/gfx/paint_vector_icon.h" @@ -104,15 +105,17 @@ MenuItemView* MenuModelAdapter::AddMenuItemFromModelAt(ui::MenuModel* model, model->GetSeparatorTypeAt(model_index)); } - gfx::Image icon; - model->GetIconAt(model_index, &icon); + ui::ImageModel icon = model->GetIconAt(model_index); + ui::ImageModel minor_icon = model->GetMinorIconAt(model_index); return menu->AddMenuItemAt( menu_index, item_id, model->GetLabelAt(model_index), model->GetMinorTextAt(model_index), - ui::ThemedVectorIcon(model->GetMinorIconAt(model_index)), - icon.IsEmpty() ? gfx::ImageSkia() : *icon.ToImageSkia(), - icon.IsEmpty() ? ui::ThemedVectorIcon(model->GetVectorIconAt(model_index)) - : ui::ThemedVectorIcon(), + minor_icon.IsVectorIcon() + ? ui::ThemedVectorIcon(minor_icon.GetVectorIcon()) + : ui::ThemedVectorIcon(), + icon.IsImage() ? *icon.GetImage().ToImageSkia() : gfx::ImageSkia(), + icon.IsVectorIcon() ? ui::ThemedVectorIcon(icon.GetVectorIcon()) + : ui::ThemedVectorIcon(), *type, ui::NORMAL_SEPARATOR); } 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 7f9bb3bd850..6241a9d7916 100644 --- a/chromium/ui/views/controls/menu/menu_model_adapter_unittest.cc +++ b/chromium/ui/views/controls/menu/menu_model_adapter_unittest.cc @@ -66,7 +66,9 @@ class MenuModelBase : public ui::MenuModel { int GetGroupIdAt(int index) const override { return 0; } - bool GetIconAt(int index, gfx::Image* icon) const override { return false; } + ui::ImageModel GetIconAt(int index) const override { + return ui::ImageModel(); + } ui::ButtonMenuItemModel* GetButtonMenuItemAt(int index) const override { return nullptr; 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 321308675f6..4f70db3ec4b 100644 --- a/chromium/ui/views/controls/menu/menu_runner_cocoa_unittest.mm +++ b/chromium/ui/views/controls/menu/menu_runner_cocoa_unittest.mm @@ -299,7 +299,8 @@ class MenuRunnerCocoaTest : public ViewsTestBase, DISALLOW_COPY_AND_ASSIGN(MenuRunnerCocoaTest); }; -TEST_P(MenuRunnerCocoaTest, RunMenuAndCancel) { +// Crashes frequently, https://crbug.com/1073069 +TEST_P(MenuRunnerCocoaTest, DISABLED_RunMenuAndCancel) { base::TimeTicks min_time = ui::EventTimeForNow(); RunMenu(base::BindOnce(&MenuRunnerCocoaTest::MenuCancelCallback, 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 7a9d15cb206..3ba6950690e 100644 --- a/chromium/ui/views/controls/menu/menu_scroll_view_container.cc +++ b/chromium/ui/views/controls/menu/menu_scroll_view_container.cc @@ -22,6 +22,7 @@ #include "ui/views/controls/menu/submenu_view.h" #include "ui/views/layout/flex_layout.h" #include "ui/views/round_rect_painter.h" +#include "ui/views/view.h" #include "ui/views/view_class_properties.h" using ui::NativeTheme; @@ -51,6 +52,12 @@ class MenuScrollButton : public View { pref_height_); } + void OnThemeChanged() override { + View::OnThemeChanged(); + arrow_color_ = GetNativeTheme()->GetSystemColor( + ui::NativeTheme::kColorId_EnabledMenuItemForegroundColor); + } + bool CanDrop(const OSExchangeData& data) override { DCHECK(host_->GetMenuItem()->GetMenuController()); return true; // Always return true so that drop events are targeted to us. @@ -108,7 +115,7 @@ class MenuScrollButton : public View { cc::PaintFlags flags; flags.setStyle(cc::PaintFlags::kFill_Style); flags.setAntiAlias(true); - flags.setColor(config.arrow_color); + flags.setColor(arrow_color_); canvas->DrawPath(path, flags); } @@ -122,6 +129,9 @@ class MenuScrollButton : public View { // Preferred height. int pref_height_; + // Color for the arrow to scroll. + SkColor arrow_color_; + DISALLOW_COPY_AND_ASSIGN(MenuScrollButton); }; diff --git a/chromium/ui/views/controls/menu/menu_scroll_view_container.h b/chromium/ui/views/controls/menu/menu_scroll_view_container.h index 5f87cfa5a9b..e0dc860b3a2 100644 --- a/chromium/ui/views/controls/menu/menu_scroll_view_container.h +++ b/chromium/ui/views/controls/menu/menu_scroll_view_container.h @@ -31,8 +31,6 @@ class MenuScrollViewContainer : public View { // External function to check if the bubble border is used. bool HasBubbleBorder() const; - void SetFootnoteView(View* view); - // View overrides. gfx::Size CalculatePreferredSize() const override; void OnPaintBackground(gfx::Canvas* canvas) override; diff --git a/chromium/ui/views/controls/menu/native_menu_win.cc b/chromium/ui/views/controls/menu/native_menu_win.cc index 54de8fb75e9..042ecddda20 100644 --- a/chromium/ui/views/controls/menu/native_menu_win.cc +++ b/chromium/ui/views/controls/menu/native_menu_win.cc @@ -6,7 +6,7 @@ #include <utility> -#include "base/logging.h" +#include "base/check.h" #include "base/strings/string_util.h" #include "ui/base/accelerators/accelerator.h" #include "ui/base/l10n/l10n_util.h" diff --git a/chromium/ui/views/controls/message_box_view.cc b/chromium/ui/views/controls/message_box_view.cc index 0d522a2ed58..bbdfc07370e 100644 --- a/chromium/ui/views/controls/message_box_view.cc +++ b/chromium/ui/views/controls/message_box_view.cc @@ -231,7 +231,7 @@ void MessageBoxView::ResetLayoutManager() { constexpr int kMessageViewColumnSetId = 0; ColumnSet* column_set = layout->AddColumnSet(kMessageViewColumnSetId); column_set->AddColumn(GridLayout::FILL, GridLayout::FILL, 1, - GridLayout::FIXED, message_width_, 0); + GridLayout::ColumnSize::kFixed, message_width_, 0); const LayoutProvider* provider = LayoutProvider::Get(); @@ -242,7 +242,7 @@ void MessageBoxView::ResetLayoutManager() { column_set = layout->AddColumnSet(kExtraViewColumnSetId); column_set->AddPaddingColumn(0, horizontal_insets.left()); column_set->AddColumn(GridLayout::FILL, GridLayout::FILL, 1, - GridLayout::USE_PREF, 0, 0); + GridLayout::ColumnSize::kUsePreferred, 0, 0); column_set->AddPaddingColumn(0, horizontal_insets.right()); } diff --git a/chromium/ui/views/controls/native/native_view_host.cc b/chromium/ui/views/controls/native/native_view_host.cc index b29553ae8f7..4779e4f07d9 100644 --- a/chromium/ui/views/controls/native/native_view_host.cc +++ b/chromium/ui/views/controls/native/native_view_host.cc @@ -7,7 +7,7 @@ #include <memory> #include <utility> -#include "base/logging.h" +#include "base/check.h" #include "ui/accessibility/ax_enums.mojom.h" #include "ui/base/cursor/cursor.h" #include "ui/gfx/canvas.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 26a4b5a55fa..2223f6e3b28 100644 --- a/chromium/ui/views/controls/native/native_view_host_aura.cc +++ b/chromium/ui/views/controls/native/native_view_host_aura.cc @@ -7,7 +7,7 @@ #include <memory> #include <utility> -#include "base/logging.h" +#include "base/check.h" #include "base/optional.h" #include "build/build_config.h" #include "ui/aura/client/aura_constants.h" diff --git a/chromium/ui/views/controls/native/native_view_host_aura_unittest.cc b/chromium/ui/views/controls/native/native_view_host_aura_unittest.cc index 973caf4a0aa..d1d03765bf3 100644 --- a/chromium/ui/views/controls/native/native_view_host_aura_unittest.cc +++ b/chromium/ui/views/controls/native/native_view_host_aura_unittest.cc @@ -15,7 +15,7 @@ #include "ui/aura/window_targeter.h" #include "ui/aura/window_tree_host.h" #include "ui/base/cursor/cursor.h" -#include "ui/base/mojom/cursor_type.mojom-shared.h" +#include "ui/base/cursor/mojom/cursor_type.mojom-shared.h" #include "ui/events/event_utils.h" #include "ui/views/controls/native/native_view_host.h" #include "ui/views/controls/native/native_view_host_test_base.h" diff --git a/chromium/ui/views/controls/progress_bar.cc b/chromium/ui/views/controls/progress_bar.cc index 3801b433a4e..45e2f590995 100644 --- a/chromium/ui/views/controls/progress_bar.cc +++ b/chromium/ui/views/controls/progress_bar.cc @@ -9,7 +9,8 @@ #include <memory> #include <string> -#include "base/logging.h" +#include "base/check_op.h" +#include "base/i18n/number_formatting.h" #include "base/macros.h" #include "cc/paint/paint_flags.h" #include "third_party/skia/include/core/SkPath.h" @@ -21,6 +22,7 @@ #include "ui/gfx/color_utils.h" #include "ui/native_theme/native_theme.h" #include "ui/views/metadata/metadata_impl_macros.h" +#include "ui/views/widget/widget.h" namespace views { @@ -42,6 +44,10 @@ void AddPossiblyRoundRectToPath(const gfx::Rect& rectangle, } } +int RoundToPercent(double fractional_value) { + return static_cast<int>(fractional_value * 100); +} + } // namespace ProgressBar::ProgressBar(int preferred_height, bool allow_round_corner) @@ -54,6 +60,10 @@ ProgressBar::~ProgressBar() = default; void ProgressBar::GetAccessibleNodeData(ui::AXNodeData* node_data) { node_data->role = ax::mojom::Role::kProgressIndicator; + if (IsIndeterminate()) + node_data->RemoveStringAttribute(ax::mojom::StringAttribute::kValue); + else + node_data->SetValue(base::FormatPercent(RoundToPercent(current_value_))); } gfx::Size ProgressBar::CalculatePreferredSize() const { @@ -64,6 +74,14 @@ gfx::Size ProgressBar::CalculatePreferredSize() const { return pref_size; } +void ProgressBar::VisibilityChanged(View* starting_from, bool is_visible) { + MaybeNotifyAccessibilityValueChanged(); +} + +void ProgressBar::AddedToWidget() { + MaybeNotifyAccessibilityValueChanged(); +} + void ProgressBar::OnPaint(gfx::Canvas* canvas) { if (IsIndeterminate()) return OnPaintIndeterminate(canvas); @@ -117,6 +135,8 @@ void ProgressBar::SetValue(double value) { indeterminate_bar_animation_.reset(); OnPropertyChanged(¤t_value_, kPropertyEffectsPaint); } + + MaybeNotifyAccessibilityValueChanged(); } SkColor ProgressBar::GetForegroundColor() const { @@ -227,6 +247,15 @@ void ProgressBar::OnPaintIndeterminate(gfx::Canvas* canvas) { canvas->DrawPath(slice_path, slice_flags); } +void ProgressBar::MaybeNotifyAccessibilityValueChanged() { + if (!GetWidget() || !GetWidget()->IsVisible() || + RoundToPercent(current_value_) == last_announced_percentage_) { + return; + } + last_announced_percentage_ = RoundToPercent(current_value_); + NotifyAccessibilityEvent(ax::mojom::Event::kValueChanged, true); +} + BEGIN_METADATA(ProgressBar) METADATA_PARENT_CLASS(View) ADD_PROPERTY_METADATA(ProgressBar, SkColor, ForegroundColor) diff --git a/chromium/ui/views/controls/progress_bar.h b/chromium/ui/views/controls/progress_bar.h index 563c15cd7cd..b57af1f1355 100644 --- a/chromium/ui/views/controls/progress_bar.h +++ b/chromium/ui/views/controls/progress_bar.h @@ -30,9 +30,11 @@ class VIEWS_EXPORT ProgressBar : public View, public gfx::AnimationDelegate { bool allow_round_corner = true); ~ProgressBar() override; - // Overridden from View: + // View: void GetAccessibleNodeData(ui::AXNodeData* node_data) override; gfx::Size CalculatePreferredSize() const override; + void VisibilityChanged(View* starting_from, bool is_visible) override; + void AddedToWidget() override; void OnPaint(gfx::Canvas* canvas) override; double GetValue() const; @@ -59,6 +61,9 @@ class VIEWS_EXPORT ProgressBar : public View, public gfx::AnimationDelegate { bool IsIndeterminate(); void OnPaintIndeterminate(gfx::Canvas* canvas); + // Fire an accessibility event if visible and the progress has changed. + void MaybeNotifyAccessibilityValueChanged(); + // Current progress to display, should be in the range 0.0 to 1.0. double current_value_ = 0.0; @@ -72,6 +77,8 @@ class VIEWS_EXPORT ProgressBar : public View, public gfx::AnimationDelegate { std::unique_ptr<gfx::LinearAnimation> indeterminate_bar_animation_; + int last_announced_percentage_ = -1; + DISALLOW_COPY_AND_ASSIGN(ProgressBar); }; diff --git a/chromium/ui/views/controls/progress_bar_unittest.cc b/chromium/ui/views/controls/progress_bar_unittest.cc index 280637e455d..e633e419022 100644 --- a/chromium/ui/views/controls/progress_bar_unittest.cc +++ b/chromium/ui/views/controls/progress_bar_unittest.cc @@ -8,38 +8,122 @@ #include "testing/gtest/include/gtest/gtest.h" #include "ui/accessibility/ax_enums.mojom.h" #include "ui/accessibility/ax_node_data.h" +#include "ui/events/test/event_generator.h" #include "ui/gfx/color_utils.h" #include "ui/native_theme/native_theme.h" +#include "ui/views/accessibility/ax_event_manager.h" +#include "ui/views/accessibility/ax_event_observer.h" #include "ui/views/test/views_test_base.h" +#include "ui/views/widget/widget_utils.h" namespace views { -using ProgressBarTest = ViewsTestBase; +namespace { -TEST_F(ProgressBarTest, Accessibility) { - ProgressBar bar; - bar.SetValue(0.62); +class TestAXEventObserver : public AXEventObserver { + public: + TestAXEventObserver() { AXEventManager::Get()->AddObserver(this); } + + ~TestAXEventObserver() override { + AXEventManager::Get()->RemoveObserver(this); + } + + TestAXEventObserver(const TestAXEventObserver&) = delete; + TestAXEventObserver& operator=(const TestAXEventObserver&) = delete; + + int value_changed_count() const { return value_changed_count_; } + + // AXEventObserver: + void OnViewEvent(View* view, ax::mojom::Event event_type) override { + if (event_type == ax::mojom::Event::kValueChanged) + value_changed_count_++; + } + + private: + int value_changed_count_ = 0; +}; + +} // namespace + +class ProgressBarTest : public ViewsTestBase { + protected: + // ViewsTestBase: + void SetUp() override { + ViewsTestBase::SetUp(); + bar_ = new ProgressBar; + + widget_ = CreateTestWidget(); + widget_->SetContentsView(bar_); + widget_->Show(); + + event_generator_ = std::make_unique<ui::test::EventGenerator>( + GetRootWindow(widget_.get())); + } + + void TearDown() override { + widget_.reset(); + ViewsTestBase::TearDown(); + } + + ProgressBar* bar_; + std::unique_ptr<Widget> widget_; + + std::unique_ptr<ui::test::EventGenerator> event_generator_; +}; + +TEST_F(ProgressBarTest, AccessibleNodeData) { + bar_->SetValue(0.626); ui::AXNodeData node_data; - bar.GetAccessibleNodeData(&node_data); + bar_->GetAccessibleNodeData(&node_data); EXPECT_EQ(ax::mojom::Role::kProgressIndicator, node_data.role); EXPECT_EQ(base::string16(), node_data.GetString16Attribute(ax::mojom::StringAttribute::kName)); + EXPECT_EQ(std::string("62%"), + node_data.GetStringAttribute(ax::mojom::StringAttribute::kValue)); EXPECT_FALSE( node_data.HasIntAttribute(ax::mojom::IntAttribute::kRestriction)); } +// Verifies the correct a11y events are raised for an accessible progress bar. +TEST_F(ProgressBarTest, AccessibilityEvents) { + TestAXEventObserver observer; + EXPECT_EQ(0, observer.value_changed_count()); + + bar_->SetValue(0.50); + EXPECT_EQ(1, observer.value_changed_count()); + + bar_->SetValue(0.63); + EXPECT_EQ(2, observer.value_changed_count()); + + bar_->SetValue(0.636); + EXPECT_EQ(2, observer.value_changed_count()); + + bar_->SetValue(0.642); + EXPECT_EQ(3, observer.value_changed_count()); + + widget_->Hide(); + widget_->Show(); + EXPECT_EQ(3, observer.value_changed_count()); + + widget_->Hide(); + bar_->SetValue(0.8); + EXPECT_EQ(3, observer.value_changed_count()); + + widget_->Show(); + EXPECT_EQ(4, observer.value_changed_count()); +} + // Test that default colors can be overridden. Used by Chromecast. TEST_F(ProgressBarTest, OverrideDefaultColors) { - ProgressBar bar; - EXPECT_NE(SK_ColorRED, bar.GetForegroundColor()); - EXPECT_NE(SK_ColorGREEN, bar.GetBackgroundColor()); - EXPECT_NE(bar.GetForegroundColor(), bar.GetBackgroundColor()); - - bar.SetForegroundColor(SK_ColorRED); - bar.SetBackgroundColor(SK_ColorGREEN); - EXPECT_EQ(SK_ColorRED, bar.GetForegroundColor()); - EXPECT_EQ(SK_ColorGREEN, bar.GetBackgroundColor()); + EXPECT_NE(SK_ColorRED, bar_->GetForegroundColor()); + EXPECT_NE(SK_ColorGREEN, bar_->GetBackgroundColor()); + EXPECT_NE(bar_->GetForegroundColor(), bar_->GetBackgroundColor()); + + bar_->SetForegroundColor(SK_ColorRED); + bar_->SetBackgroundColor(SK_ColorGREEN); + EXPECT_EQ(SK_ColorRED, bar_->GetForegroundColor()); + EXPECT_EQ(SK_ColorGREEN, bar_->GetBackgroundColor()); } } // namespace views diff --git a/chromium/ui/views/controls/resize_area.cc b/chromium/ui/views/controls/resize_area.cc index a565893fdce..a83a939e050 100644 --- a/chromium/ui/views/controls/resize_area.cc +++ b/chromium/ui/views/controls/resize_area.cc @@ -4,7 +4,6 @@ #include "ui/views/controls/resize_area.h" -#include "base/logging.h" #include "ui/accessibility/ax_enums.mojom.h" #include "ui/accessibility/ax_node_data.h" #include "ui/base/cursor/cursor.h" diff --git a/chromium/ui/views/controls/scroll_view.cc b/chromium/ui/views/controls/scroll_view.cc index 89e642951ea..8f1244d79c2 100644 --- a/chromium/ui/views/controls/scroll_view.cc +++ b/chromium/ui/views/controls/scroll_view.cc @@ -7,8 +7,8 @@ #include <algorithm> #include "base/bind.h" +#include "base/check_op.h" #include "base/feature_list.h" -#include "base/logging.h" #include "base/macros.h" #include "base/numerics/ranges.h" #include "build/build_config.h" diff --git a/chromium/ui/views/controls/scroll_view_unittest.cc b/chromium/ui/views/controls/scroll_view_unittest.cc index 3d08c952fae..0359bc89ed3 100644 --- a/chromium/ui/views/controls/scroll_view_unittest.cc +++ b/chromium/ui/views/controls/scroll_view_unittest.cc @@ -799,6 +799,56 @@ TEST_F(ScrollViewTest, ScrollChildToVisibleOnFocus) { EXPECT_EQ(415 - viewport_height, offset.y()); } +// Verifies that ScrollView scrolls into view when its contents root is focused. +TEST_F(ScrollViewTest, ScrollViewToVisibleOnContentsRootFocus) { + ScrollViewTestApi outer_test_api(scroll_view_.get()); + auto outer_contents = std::make_unique<CustomView>(); + outer_contents->SetPreferredSize(gfx::Size(500, 1000)); + auto* outer_contents_ptr = + scroll_view_->SetContents(std::move(outer_contents)); + + auto inner_scroll_view = std::make_unique<ScrollView>(); + auto* inner_scroll_view_ptr = + outer_contents_ptr->AddChildView(std::move(inner_scroll_view)); + + ScrollViewTestApi inner_test_api(inner_scroll_view_ptr); + auto inner_contents = std::make_unique<FixedView>(); + inner_contents->SetPreferredSize(gfx::Size(500, 1000)); + auto* inner_contents_ptr = + inner_scroll_view_ptr->SetContents(std::move(inner_contents)); + + inner_scroll_view_ptr->SetBoundsRect(gfx::Rect(0, 510, 100, 100)); + inner_scroll_view_ptr->Layout(); + EXPECT_EQ(gfx::Point(), inner_test_api.IntegralViewOffset()); + + scroll_view_->SetBoundsRect(gfx::Rect(0, 0, 200, 200)); + scroll_view_->Layout(); + EXPECT_EQ(gfx::Point(), outer_test_api.IntegralViewOffset()); + + // Scroll the inner scroll view to y=405 height=10. This should make the y + // position of the inner content at (405 + 10) - inner_viewport_height + // (scroll region bottom aligned). The outer scroll view should not scroll. + inner_contents_ptr->ScrollRectToVisible(gfx::Rect(0, 405, 10, 10)); + const int inner_viewport_height = + inner_test_api.contents_viewport()->height(); + gfx::ScrollOffset inner_offset = inner_test_api.CurrentOffset(); + EXPECT_EQ(415 - inner_viewport_height, inner_offset.y()); + gfx::ScrollOffset outer_offset = outer_test_api.CurrentOffset(); + EXPECT_EQ(0, outer_offset.y()); + + // Set focus to the inner scroll view's contents root. This should cause the + // outer scroll view to scroll to y=510 height=100 so that the y position of + // the outer content is at (510 + 100) - outer_viewport_height (scroll region + // bottom aligned). The inner scroll view should not scroll. + inner_contents_ptr->SetFocus(); + const int outer_viewport_height = + outer_test_api.contents_viewport()->height(); + inner_offset = inner_test_api.CurrentOffset(); + EXPECT_EQ(415 - inner_viewport_height, inner_offset.y()); + outer_offset = outer_test_api.CurrentOffset(); + EXPECT_EQ(610 - outer_viewport_height, outer_offset.y()); +} + // Verifies ClipHeightTo() uses the height of the content when it is between the // minimum and maximum height values. TEST_F(ScrollViewTest, ClipHeightToNormalContentHeight) { diff --git a/chromium/ui/views/controls/scrollbar/scroll_bar_views.cc b/chromium/ui/views/controls/scrollbar/scroll_bar_views.cc index 405168573ef..5da0afbd601 100644 --- a/chromium/ui/views/controls/scrollbar/scroll_bar_views.cc +++ b/chromium/ui/views/controls/scrollbar/scroll_bar_views.cc @@ -8,7 +8,8 @@ #include <memory> #include <utility> -#include "base/logging.h" +#include "base/check.h" +#include "base/notreached.h" #include "ui/events/keycodes/keyboard_codes.h" #include "ui/gfx/canvas.h" #include "ui/views/controls/button/button.h" diff --git a/chromium/ui/views/controls/slider.cc b/chromium/ui/views/controls/slider.cc index 3e03f5379e3..0f71ce43d4a 100644 --- a/chromium/ui/views/controls/slider.cc +++ b/chromium/ui/views/controls/slider.cc @@ -7,7 +7,7 @@ #include <algorithm> #include <memory> -#include "base/logging.h" +#include "base/check_op.h" #include "base/message_loop/message_loop_current.h" #include "base/strings/stringprintf.h" #include "base/strings/utf_string_conversions.h" diff --git a/chromium/ui/views/controls/styled_label.cc b/chromium/ui/views/controls/styled_label.cc index 9004b93f773..5c915f9d452 100644 --- a/chromium/ui/views/controls/styled_label.cc +++ b/chromium/ui/views/controls/styled_label.cc @@ -556,9 +556,6 @@ std::unique_ptr<Label> StyledLabel::CreateLabel( // Note this ignores |default_text_style_|, in favor of style::STYLE_LINK. auto link = std::make_unique<Link>(text, text_context_); - // Links in a StyledLabel do not get underlines. - link->SetUnderline(false); - layout_views_->link_targets[link.get()] = range; result = std::move(link); diff --git a/chromium/ui/views/controls/tabbed_pane/tabbed_pane.cc b/chromium/ui/views/controls/tabbed_pane/tabbed_pane.cc index 389092da35b..4fdef37e512 100644 --- a/chromium/ui/views/controls/tabbed_pane/tabbed_pane.cc +++ b/chromium/ui/views/controls/tabbed_pane/tabbed_pane.cc @@ -7,8 +7,8 @@ #include <algorithm> #include <utility> +#include "base/check_op.h" #include "base/i18n/rtl.h" -#include "base/logging.h" #include "base/macros.h" #include "build/build_config.h" #include "cc/paint/paint_flags.h" diff --git a/chromium/ui/views/controls/tabbed_pane/tabbed_pane.h b/chromium/ui/views/controls/tabbed_pane/tabbed_pane.h index e06e0db5f2b..4c42c238ecb 100644 --- a/chromium/ui/views/controls/tabbed_pane/tabbed_pane.h +++ b/chromium/ui/views/controls/tabbed_pane/tabbed_pane.h @@ -24,7 +24,7 @@ class TabStrip; namespace test { class TabbedPaneAccessibilityMacTest; -class TabbedPaneTest; +class TabbedPaneWithWidgetTest; } // namespace test // TabbedPane is a view that shows tabs. When the user clicks on a tab, the @@ -100,7 +100,7 @@ class VIEWS_EXPORT TabbedPane : public View { friend class FocusTraversalTest; friend class Tab; friend class TabStrip; - friend class test::TabbedPaneTest; + friend class test::TabbedPaneWithWidgetTest; friend class test::TabbedPaneAccessibilityMacTest; // Adds a new tab at |index| with |title|. |contents| is the view displayed 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 51cfc08d9ee..808afa4d777 100644 --- a/chromium/ui/views/controls/tabbed_pane/tabbed_pane_unittest.cc +++ b/chromium/ui/views/controls/tabbed_pane/tabbed_pane_unittest.cc @@ -43,14 +43,75 @@ ax::mojom::Role GetAccessibleRole(View* view) { } // namespace -class TabbedPaneTest : public ViewsTestBase { +using TabbedPaneTest = ViewsTestBase; + +// Tests tab orientation. +TEST_F(TabbedPaneTest, HorizontalOrientationDefault) { + auto tabbed_pane = std::make_unique<TabbedPane>(); + EXPECT_EQ(tabbed_pane->GetOrientation(), + TabbedPane::Orientation::kHorizontal); +} + +// Tests tab orientation. +TEST_F(TabbedPaneTest, VerticalOrientation) { + auto tabbed_pane = std::make_unique<TabbedPane>( + TabbedPane::Orientation::kVertical, TabbedPane::TabStripStyle::kBorder); + EXPECT_EQ(tabbed_pane->GetOrientation(), TabbedPane::Orientation::kVertical); +} + +// Tests tab strip style. +TEST_F(TabbedPaneTest, TabStripBorderStyle) { + auto tabbed_pane = std::make_unique<TabbedPane>(); + EXPECT_EQ(tabbed_pane->GetStyle(), TabbedPane::TabStripStyle::kBorder); +} + +// Tests tab strip style. +TEST_F(TabbedPaneTest, TabStripHighlightStyle) { + auto tabbed_pane = + std::make_unique<TabbedPane>(TabbedPane::Orientation::kVertical, + TabbedPane::TabStripStyle::kHighlight); + EXPECT_EQ(tabbed_pane->GetStyle(), TabbedPane::TabStripStyle::kHighlight); +} + +// Tests the preferred size and layout when tabs are aligned vertically.. +TEST_F(TabbedPaneTest, SizeAndLayoutInVerticalOrientation) { + auto tabbed_pane = std::make_unique<TabbedPane>( + TabbedPane::Orientation::kVertical, TabbedPane::TabStripStyle::kBorder); + View* child1 = + tabbed_pane->AddTab(ASCIIToUTF16("tab1"), + std::make_unique<StaticSizedView>(gfx::Size(20, 10))); + View* child2 = tabbed_pane->AddTab( + ASCIIToUTF16("tab2"), std::make_unique<StaticSizedView>(gfx::Size(5, 5))); + tabbed_pane->SelectTabAt(0); + + // |tabbed_pane_| reserves extra width for the tab strip in vertical mode. + EXPECT_GT(tabbed_pane->GetPreferredSize().width(), 20); + // |tabbed_pane_| height should match the largest child in vertical mode. + EXPECT_EQ(tabbed_pane->GetPreferredSize().height(), 10); + + // The child views should resize to fit in larger tabbed panes. + tabbed_pane->SetBounds(0, 0, 100, 200); + + EXPECT_GT(child1->bounds().width(), 0); + // |tabbed_pane_| reserves extra width for the tab strip. Therefore the + // children's width should be smaller than the |tabbed_pane_|'s width. + EXPECT_LT(child1->bounds().width(), 100); + // |tabbed_pane_| has no border. Therefore the children should be as high as + // the |tabbed_pane_|. + EXPECT_EQ(child1->bounds().height(), 200); + + // If we switch to the other tab, it should get assigned the same bounds. + tabbed_pane->SelectTabAt(1); + EXPECT_EQ(child1->bounds(), child2->bounds()); +} + +class TabbedPaneWithWidgetTest : public ViewsTestBase { public: - TabbedPaneTest() = default; + TabbedPaneWithWidgetTest() = default; void SetUp() override { ViewsTestBase::SetUp(); - tabbed_pane_ = std::make_unique<TabbedPane>(); - tabbed_pane_->set_owned_by_client(); + auto tabbed_pane = std::make_unique<TabbedPane>(); // Create a widget so that accessibility data will be returned correctly. widget_ = std::make_unique<Widget>(); @@ -59,22 +120,17 @@ class TabbedPaneTest : public ViewsTestBase { params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; params.bounds = gfx::Rect(0, 0, 650, 650); widget_->Init(std::move(params)); - widget_->SetContentsView(tabbed_pane_.get()); + tabbed_pane_ = tabbed_pane.get(); + widget_->SetContentsView(tabbed_pane.release()); } void TearDown() override { - tabbed_pane_.reset(); + tabbed_pane_ = nullptr; widget_.reset(); ViewsTestBase::TearDown(); } protected: - void MakeTabbedPane(TabbedPane::Orientation orientation, - TabbedPane::TabStripStyle style) { - tabbed_pane_ = std::make_unique<TabbedPane>(orientation, style); - tabbed_pane_->set_owned_by_client(); - } - Tab* GetTabAt(size_t index) { return static_cast<Tab*>(tabbed_pane_->tab_strip_->children()[index]); } @@ -90,41 +146,16 @@ class TabbedPaneTest : public ViewsTestBase { } std::unique_ptr<Widget> widget_; - std::unique_ptr<TabbedPane> tabbed_pane_; + TabbedPane* tabbed_pane_; private: - DISALLOW_COPY_AND_ASSIGN(TabbedPaneTest); + DISALLOW_COPY_AND_ASSIGN(TabbedPaneWithWidgetTest); }; -// Tests tab orientation. -TEST_F(TabbedPaneTest, HorizontalOrientation) { - EXPECT_EQ(tabbed_pane_->GetOrientation(), - TabbedPane::Orientation::kHorizontal); -} - -// Tests tab orientation. -TEST_F(TabbedPaneTest, VerticalOrientation) { - MakeTabbedPane(TabbedPane::Orientation::kVertical, - TabbedPane::TabStripStyle::kBorder); - EXPECT_EQ(tabbed_pane_->GetOrientation(), TabbedPane::Orientation::kVertical); -} - -// Tests tab strip style. -TEST_F(TabbedPaneTest, TabStripBorderStyle) { - EXPECT_EQ(tabbed_pane_->GetStyle(), TabbedPane::TabStripStyle::kBorder); -} - -// Tests tab strip style. -TEST_F(TabbedPaneTest, TabStripHighlightStyle) { - MakeTabbedPane(TabbedPane::Orientation::kVertical, - TabbedPane::TabStripStyle::kHighlight); - EXPECT_EQ(tabbed_pane_->GetStyle(), TabbedPane::TabStripStyle::kHighlight); -} - // Tests the preferred size and layout when tabs are aligned horizontally. // TabbedPane requests a size that fits the largest child or the minimum size // necessary to display the tab titles, whichever is larger. -TEST_F(TabbedPaneTest, SizeAndLayout) { +TEST_F(TabbedPaneWithWidgetTest, SizeAndLayout) { View* child1 = tabbed_pane_->AddTab( ASCIIToUTF16("tab1"), std::make_unique<StaticSizedView>(gfx::Size(20, 10))); @@ -165,39 +196,7 @@ TEST_F(TabbedPaneTest, SizeAndLayout) { EXPECT_EQ(child2->bounds(), child3->bounds()); } -// Tests the preferred size and layout when tabs are aligned vertically.. -TEST_F(TabbedPaneTest, SizeAndLayoutInVerticalOrientation) { - MakeTabbedPane(TabbedPane::Orientation::kVertical, - TabbedPane::TabStripStyle::kBorder); - View* child1 = tabbed_pane_->AddTab( - ASCIIToUTF16("tab1"), - std::make_unique<StaticSizedView>(gfx::Size(20, 10))); - View* child2 = tabbed_pane_->AddTab( - ASCIIToUTF16("tab2"), std::make_unique<StaticSizedView>(gfx::Size(5, 5))); - tabbed_pane_->SelectTabAt(0); - - // |tabbed_pane_| reserves extra width for the tab strip in vertical mode. - EXPECT_GT(tabbed_pane_->GetPreferredSize().width(), 20); - // |tabbed_pane_| height should match the largest child in vertical mode. - EXPECT_EQ(tabbed_pane_->GetPreferredSize().height(), 10); - - // The child views should resize to fit in larger tabbed panes. - tabbed_pane_->SetBounds(0, 0, 100, 200); - RunPendingMessages(); - EXPECT_GT(child1->bounds().width(), 0); - // |tabbed_pane_| reserves extra width for the tab strip. Therefore the - // children's width should be smaller than the |tabbed_pane_|'s width. - EXPECT_LT(child1->bounds().width(), 100); - // |tabbed_pane_| has no border. Therefore the children should be as high as - // the |tabbed_pane_|. - EXPECT_EQ(child1->bounds().height(), 200); - - // If we switch to the other tab, it should get assigned the same bounds. - tabbed_pane_->SelectTabAt(1); - EXPECT_EQ(child1->bounds(), child2->bounds()); -} - -TEST_F(TabbedPaneTest, AddAndSelect) { +TEST_F(TabbedPaneWithWidgetTest, AddAndSelect) { // Add several tabs; only the first should be selected automatically. for (size_t i = 0; i < 3; ++i) { tabbed_pane_->AddTab(DefaultTabTitle(), std::make_unique<View>()); @@ -218,7 +217,7 @@ TEST_F(TabbedPaneTest, AddAndSelect) { EXPECT_NE(0u, tabbed_pane_->GetSelectedTabIndex()); } -TEST_F(TabbedPaneTest, ArrowKeyBindings) { +TEST_F(TabbedPaneWithWidgetTest, ArrowKeyBindings) { // Add several tabs; only the first should be selected automatically. for (size_t i = 0; i < 3; ++i) { tabbed_pane_->AddTab(DefaultTabTitle(), std::make_unique<View>()); @@ -246,7 +245,7 @@ TEST_F(TabbedPaneTest, ArrowKeyBindings) { // Use TabbedPane::HandleAccessibleAction() to select tabs and make sure their // a11y information is correct. -TEST_F(TabbedPaneTest, SelectTabWithAccessibleAction) { +TEST_F(TabbedPaneWithWidgetTest, SelectTabWithAccessibleAction) { constexpr size_t kNumTabs = 3; for (size_t i = 0; i < kNumTabs; ++i) { tabbed_pane_->AddTab(DefaultTabTitle(), std::make_unique<View>()); @@ -281,17 +280,17 @@ TEST_F(TabbedPaneTest, SelectTabWithAccessibleAction) { EXPECT_EQ(1u, tabbed_pane_->GetSelectedTabIndex()); } -TEST_F(TabbedPaneTest, AccessiblePaneTitleTracksActiveTabTitle) { +TEST_F(TabbedPaneWithWidgetTest, AccessiblePaneTitleTracksActiveTabTitle) { const base::string16 kFirstTitle = ASCIIToUTF16("Tab1"); const base::string16 kSecondTitle = ASCIIToUTF16("Tab2"); tabbed_pane_->AddTab(kFirstTitle, std::make_unique<View>()); tabbed_pane_->AddTab(kSecondTitle, std::make_unique<View>()); - EXPECT_EQ(kFirstTitle, GetAccessibleName(tabbed_pane_.get())); + EXPECT_EQ(kFirstTitle, GetAccessibleName(tabbed_pane_)); tabbed_pane_->SelectTabAt(1); - EXPECT_EQ(kSecondTitle, GetAccessibleName(tabbed_pane_.get())); + EXPECT_EQ(kSecondTitle, GetAccessibleName(tabbed_pane_)); } -TEST_F(TabbedPaneTest, AccessiblePaneContentsTitleTracksTabTitle) { +TEST_F(TabbedPaneWithWidgetTest, AccessiblePaneContentsTitleTracksTabTitle) { const base::string16 kFirstTitle = ASCIIToUTF16("Tab1"); const base::string16 kSecondTitle = ASCIIToUTF16("Tab2"); View* const tab1_contents = @@ -302,7 +301,7 @@ TEST_F(TabbedPaneTest, AccessiblePaneContentsTitleTracksTabTitle) { EXPECT_EQ(kSecondTitle, GetAccessibleName(tab2_contents)); } -TEST_F(TabbedPaneTest, AccessiblePaneContentsRoleIsTab) { +TEST_F(TabbedPaneWithWidgetTest, AccessiblePaneContentsRoleIsTab) { const base::string16 kFirstTitle = ASCIIToUTF16("Tab1"); const base::string16 kSecondTitle = ASCIIToUTF16("Tab2"); View* const tab1_contents = diff --git a/chromium/ui/views/controls/table/table_utils.cc b/chromium/ui/views/controls/table/table_utils.cc index 36ed734a622..d6ac523212e 100644 --- a/chromium/ui/views/controls/table/table_utils.cc +++ b/chromium/ui/views/controls/table/table_utils.cc @@ -8,7 +8,7 @@ #include <algorithm> -#include "base/logging.h" +#include "base/notreached.h" #include "ui/gfx/canvas.h" #include "ui/gfx/font_list.h" #include "ui/gfx/text_utils.h" diff --git a/chromium/ui/views/controls/table/table_view.cc b/chromium/ui/views/controls/table/table_view.cc index 458e2470409..94eb7a0527c 100644 --- a/chromium/ui/views/controls/table/table_view.cc +++ b/chromium/ui/views/controls/table/table_view.cc @@ -1422,28 +1422,33 @@ void TableView::UpdateVirtualAccessibilityChildrenBounds() { } gfx::Rect TableView::CalculateHeaderRowAccessibilityBounds() const { - return AdjustRectForAXRelativeBounds(header_->GetVisibleBounds()); + gfx::Rect header_bounds = header_->GetVisibleBounds(); + gfx::Point header_origin = header_bounds.origin(); + ConvertPointToTarget(header_, this, &header_origin); + header_bounds.set_origin(header_origin); + return header_bounds; } gfx::Rect TableView::CalculateHeaderCellAccessibilityBounds( const int visible_column_index) const { + const gfx::Rect& header_bounds = CalculateHeaderRowAccessibilityBounds(); const VisibleColumn& visible_column = visible_columns_[visible_column_index]; - gfx::Rect header_cell_bounds(visible_column.x, header_->y(), - visible_column.width, header_->height()); - return AdjustRectForAXRelativeBounds(header_cell_bounds); + gfx::Rect header_cell_bounds(visible_column.x, header_bounds.y(), + visible_column.width, header_bounds.height()); + return header_cell_bounds; } gfx::Rect TableView::CalculateTableRowAccessibilityBounds( const int row_index) const { gfx::Rect row_bounds = GetRowBounds(row_index); - return AdjustRectForAXRelativeBounds(row_bounds); + return row_bounds; } gfx::Rect TableView::CalculateTableCellAccessibilityBounds( const int row_index, const int visible_column_index) const { gfx::Rect cell_bounds = GetCellBounds(row_index, visible_column_index); - return AdjustRectForAXRelativeBounds(cell_bounds); + return cell_bounds; } void TableView::UpdateAccessibilityFocus() { @@ -1510,13 +1515,6 @@ AXVirtualView* TableView::GetVirtualAccessibilityCell( return i->get(); } -gfx::Rect TableView::AdjustRectForAXRelativeBounds( - const gfx::Rect& rect) const { - gfx::Rect converted_rect = rect; - View::ConvertRectToScreen(this, &converted_rect); - return converted_rect; -} - DEFINE_ENUM_CONVERTERS(TableTypes, {TableTypes::TEXT_ONLY, base::ASCIIToUTF16("TEXT_ONLY")}, {TableTypes::ICON_AND_TEXT, diff --git a/chromium/ui/views/controls/table/table_view.h b/chromium/ui/views/controls/table/table_view.h index 6a5546c7b74..1a640fdf53e 100644 --- a/chromium/ui/views/controls/table/table_view.h +++ b/chromium/ui/views/controls/table/table_view.h @@ -383,11 +383,6 @@ class VIEWS_EXPORT TableView : public views::View, // |visible_column_index| indexes into |visible_columns_|. AXVirtualView* GetVirtualAccessibilityCell(int row, int visible_column_index); - // Returns |rect|, adjusted for use in AXRelativeBounds by translating it into - // screen coordinates. The result must be converted to gfx::RectF when setting - // into AXRelativeBounds. - gfx::Rect AdjustRectForAXRelativeBounds(const gfx::Rect& rect) const; - ui::TableModel* model_ = nullptr; std::vector<ui::TableColumn> columns_; diff --git a/chromium/ui/views/controls/table/table_view_unittest.cc b/chromium/ui/views/controls/table/table_view_unittest.cc index fb2a3ecc409..df538cc5d3c 100644 --- a/chromium/ui/views/controls/table/table_view_unittest.cc +++ b/chromium/ui/views/controls/table/table_view_unittest.cc @@ -80,23 +80,33 @@ class TableViewTestHelper { // Generate the bounds for the header row and cells. auto header_row = std::vector<gfx::Rect>(); - header_row.push_back(table_->CalculateHeaderRowAccessibilityBounds()); + gfx::Rect header_row_bounds = + table_->CalculateHeaderRowAccessibilityBounds(); + View::ConvertRectToScreen(table_, &header_row_bounds); + header_row.push_back(header_row_bounds); for (size_t column_index = 0; column_index < visible_col_count(); column_index++) { - header_row.push_back( - table_->CalculateHeaderCellAccessibilityBounds(column_index)); + gfx::Rect header_cell_bounds = + table_->CalculateHeaderCellAccessibilityBounds(column_index); + View::ConvertRectToScreen(table_, &header_cell_bounds); + header_row.push_back(header_cell_bounds); } expected_bounds.push_back(header_row); // Generate the bounds for the table rows and cells. for (int row_index = 0; row_index < table_->GetRowCount(); row_index++) { auto table_row = std::vector<gfx::Rect>(); - table_row.push_back( - table_->CalculateTableRowAccessibilityBounds(row_index)); + gfx::Rect table_row_bounds = + table_->CalculateTableRowAccessibilityBounds(row_index); + View::ConvertRectToScreen(table_, &table_row_bounds); + table_row.push_back(table_row_bounds); for (size_t column_index = 0; column_index < visible_col_count(); column_index++) { - table_row.push_back(table_->CalculateTableCellAccessibilityBounds( - row_index, column_index)); + gfx::Rect table_cell_bounds = + table_->CalculateTableCellAccessibilityBounds(row_index, + column_index); + View::ConvertRectToScreen(table_, &table_cell_bounds); + table_row.push_back(table_cell_bounds); } expected_bounds.push_back(table_row); } diff --git a/chromium/ui/views/controls/textfield/textfield.cc b/chromium/ui/views/controls/textfield/textfield.cc index 61b11e53fc3..d8e8fd0ace0 100644 --- a/chromium/ui/views/controls/textfield/textfield.cc +++ b/chromium/ui/views/controls/textfield/textfield.cc @@ -8,6 +8,7 @@ #include <set> #include <string> #include <utility> +#include "ui/events/gesture_event_details.h" #if defined(OS_WIN) #include <vector> @@ -55,6 +56,7 @@ #include "ui/views/painter.h" #include "ui/views/style/platform_style.h" #include "ui/views/views_delegate.h" +#include "ui/views/views_features.h" #include "ui/views/widget/widget.h" #include "ui/wm/core/coordinate_conversion.h" @@ -215,20 +217,20 @@ ui::TextEditCommand GetCommandForKeyEvent(const ui::KeyEvent& event) { ui::TextEditCommand GetTextEditCommandFromMenuCommand(int command_id, bool has_selection) { switch (command_id) { - case IDS_APP_UNDO: + case Textfield::kUndo: return ui::TextEditCommand::UNDO; - case IDS_APP_CUT: + case Textfield::kCut: return ui::TextEditCommand::CUT; - case IDS_APP_COPY: + case Textfield::kCopy: return ui::TextEditCommand::COPY; - case IDS_APP_PASTE: + case Textfield::kPaste: return ui::TextEditCommand::PASTE; - case IDS_APP_DELETE: + case Textfield::kDelete: // The DELETE menu action only works in case of an active selection. if (has_selection) return ui::TextEditCommand::DELETE_FORWARD; break; - case IDS_APP_SELECT_ALL: + case Textfield::kSelectAll: return ui::TextEditCommand::SELECT_ALL; } return ui::TextEditCommand::INVALID_COMMAND; @@ -265,6 +267,11 @@ bool IsControlKeyModifier(int flags) { #endif } +bool IsValidCharToInsert(const base::char16& ch) { + // Filter out all control characters, including tab and new line characters. + return (ch >= 0x20 && ch < 0x7F) || ch > 0x9F; +} + } // namespace // static @@ -296,12 +303,11 @@ Textfield::Textfield() selection_controller_(this) { set_context_menu_controller(this); set_drag_controller(this); - cursor_view_.SetPaintToLayer(ui::LAYER_SOLID_COLOR); - cursor_view_.layer()->SetColor(GetTextColor()); - // |cursor_view_| is owned by Textfield view. - cursor_view_.set_owned_by_client(); - cursor_view_.GetViewAccessibility().OverrideIsIgnored(true); - AddChildView(&cursor_view_); + auto cursor_view = std::make_unique<View>(); + cursor_view->SetPaintToLayer(ui::LAYER_SOLID_COLOR); + cursor_view->layer()->SetColor(GetTextColor()); + cursor_view->GetViewAccessibility().OverrideIsIgnored(true); + cursor_view_ = AddChildView(std::move(cursor_view)); GetRenderText()->SetFontList(GetDefaultFontList()); UpdateBorder(); SetFocusBehavior(FocusBehavior::ALWAYS); @@ -388,7 +394,12 @@ const base::string16& Textfield::GetText() const { } void Textfield::SetText(const base::string16& new_text) { - model_->SetText(new_text); + SetText(new_text, new_text.length()); +} + +void Textfield::SetText(const base::string16& new_text, + size_t cursor_position) { + model_->SetText(new_text, cursor_position); OnCaretBoundsChanged(); UpdateCursorViewPosition(); UpdateCursorVisibility(); @@ -434,8 +445,8 @@ void Textfield::ClearSelection() { UpdateAfterChange(false, true); } -bool Textfield::HasSelection() const { - return !GetSelectedRange().is_empty(); +bool Textfield::HasSelection(bool primary_only) const { + return model_->HasSelection(primary_only); } SkColor Textfield::GetTextColor() const { @@ -551,8 +562,15 @@ const gfx::Range& Textfield::GetSelectedRange() const { } void Textfield::SetSelectedRange(const gfx::Range& range) { - model_->SelectRange(range); - UpdateAfterChange(false, true); + SetSelectedRange(range, true); +} + +void Textfield::SetSelectedRange(const gfx::Range& range, bool primary) { + model_->SelectRange(range, primary); + if (primary) + UpdateAfterChange(false, true); + else + SchedulePaint(); OnPropertyChanged(&model_ + kTextfieldSelectedRange, kPropertyEffectsPaint); } @@ -571,7 +589,7 @@ size_t Textfield::GetCursorPosition() const { void Textfield::SetColor(SkColor value) { GetRenderText()->SetColor(value); - cursor_view_.layer()->SetColor(value); + cursor_view_->layer()->SetColor(value); OnPropertyChanged(&model_ + kTextfieldTextColor, kPropertyEffectsPaint); } @@ -685,7 +703,7 @@ bool Textfield::OnMousePressed(const ui::MouseEvent& event) { if (!handled && (event.IsOnlyLeftMouseButton() || event.IsOnlyRightMouseButton())) { if (!had_focus) - RequestFocusWithPointer(ui::EventPointerType::POINTER_TYPE_MOUSE); + RequestFocusWithPointer(ui::EventPointerType::kMouse); #if !defined(OS_WIN) ShowVirtualKeyboardIfEnabled(); #endif @@ -693,7 +711,7 @@ bool Textfield::OnMousePressed(const ui::MouseEvent& event) { #if defined(OS_LINUX) && !defined(OS_CHROMEOS) if (!handled && !had_focus && event.IsOnlyMiddleMouseButton()) - RequestFocusWithPointer(ui::EventPointerType::POINTER_TYPE_MOUSE); + RequestFocusWithPointer(ui::EventPointerType::kMouse); #endif return selection_controller_.OnMousePressed( @@ -767,29 +785,27 @@ bool Textfield::OnKeyReleased(const ui::KeyEvent& event) { } void Textfield::OnGestureEvent(ui::GestureEvent* event) { - bool show_virtual_keyboard = true; -#if defined(OS_WIN) - show_virtual_keyboard = event->details().primary_pointer_type() == - ui::EventPointerType::POINTER_TYPE_TOUCH || - event->details().primary_pointer_type() == - ui::EventPointerType::POINTER_TYPE_PEN; -#endif + static const bool kTakeFocusOnTapUp = + base::FeatureList::IsEnabled(features::kTextfieldFocusOnTapUp); + switch (event->type()) { case ui::ET_GESTURE_TAP_DOWN: - RequestFocusWithPointer(event->details().primary_pointer_type()); - if (show_virtual_keyboard) - ShowVirtualKeyboardIfEnabled(); - event->SetHandled(); + if (!kTakeFocusOnTapUp) { + RequestFocusForGesture(event->details()); + event->SetHandled(); + } break; case ui::ET_GESTURE_TAP: + if (kTakeFocusOnTapUp) + RequestFocusForGesture(event->details()); if (controller_ && controller_->HandleGestureEvent(this, *event)) { event->SetHandled(); return; } if (event->details().tap_count() == 1) { - // If tap is on the selection and touch handles are not present, handles - // should be shown without changing selection. Otherwise, cursor should - // be moved to the tap location. + // If tap is on the selection and touch handles are not present, + // handles should be shown without changing selection. Otherwise, + // cursor should be moved to the tap location. if (touch_selection_controller_ || !GetRenderText()->IsPointInSelection(event->location())) { OnBeforeUserAction(); @@ -816,15 +832,15 @@ void Textfield::OnGestureEvent(ui::GestureEvent* event) { SelectWordAt(event->location()); OnAfterUserAction(); CreateTouchSelectionControllerAndNotifyIt(); - // If touch selection activated successfully, mark event as handled so - // that the regular context menu is not shown. + // If touch selection activated successfully, mark event as handled + // so that the regular context menu is not shown. if (touch_selection_controller_) event->SetHandled(); } else { - // If long-press happens on the selection, deactivate touch selection - // and try to initiate drag-drop. If drag-drop is not enabled, context - // menu will be shown. Event is not marked as handled to let Views - // handle drag-drop or context menu. + // If long-press happens on the selection, deactivate touch + // selection and try to initiate drag-drop. If drag-drop is not + // enabled, context menu will be shown. Event is not marked as + // handled to let Views handle drag-drop or context menu. DestroyTouchSelection(); initiating_drag_ = switches::IsTouchDragDropEnabled(); } @@ -837,29 +853,34 @@ void Textfield::OnGestureEvent(ui::GestureEvent* event) { event->SetHandled(); break; case ui::ET_GESTURE_SCROLL_BEGIN: - touch_handles_hidden_due_to_scroll_ = - touch_selection_controller_ != nullptr; - DestroyTouchSelection(); - drag_start_location_ = event->location(); - drag_start_display_offset_ = - GetRenderText()->GetUpdatedDisplayOffset().x(); - event->SetHandled(); + if (HasFocus()) { + touch_handles_hidden_due_to_scroll_ = + touch_selection_controller_ != nullptr; + DestroyTouchSelection(); + drag_start_location_ = event->location(); + drag_start_display_offset_ = + GetRenderText()->GetUpdatedDisplayOffset().x(); + event->SetHandled(); + } break; - case ui::ET_GESTURE_SCROLL_UPDATE: { - int new_offset = drag_start_display_offset_ + event->location().x() - - drag_start_location_.x(); - GetRenderText()->SetDisplayOffset(new_offset); - SchedulePaint(); - event->SetHandled(); + case ui::ET_GESTURE_SCROLL_UPDATE: + if (HasFocus()) { + int new_offset = drag_start_display_offset_ + event->location().x() - + drag_start_location_.x(); + GetRenderText()->SetDisplayOffset(new_offset); + SchedulePaint(); + event->SetHandled(); + } break; - } case ui::ET_GESTURE_SCROLL_END: case ui::ET_SCROLL_FLING_START: - if (touch_handles_hidden_due_to_scroll_) { - CreateTouchSelectionControllerAndNotifyIt(); - touch_handles_hidden_due_to_scroll_ = false; + if (HasFocus()) { + if (touch_handles_hidden_due_to_scroll_) { + CreateTouchSelectionControllerAndNotifyIt(); + touch_handles_hidden_due_to_scroll_ = false; + } + event->SetHandled(); } - event->SetHandled(); break; default: return; @@ -985,7 +1006,7 @@ int Textfield::OnPerformDrop(const ui::DropTargetEvent& event) { // Adjust the drop destination if it is on or after the current selection. size_t pos = drop_destination_model.caret_pos(); pos -= render_text->selection().Intersect(gfx::Range(0, pos)).length(); - model_->DeleteSelectionAndInsertTextAt(new_text, pos); + model_->DeletePrimarySelectionAndInsertTextAt(new_text, pos); } else { model_->MoveCursorTo(drop_destination_model); // Drop always inserts text even if the textfield is not in insert mode. @@ -1106,7 +1127,7 @@ void Textfield::OnFocus() { GetRenderText()->set_focused(true); if (ShouldShowCursor()) { UpdateCursorViewPosition(); - cursor_view_.SetVisible(true); + cursor_view_->SetVisible(true); } if (GetInputMethod()) GetInputMethod()->SetFocusedTextInputClient(this); @@ -1135,7 +1156,7 @@ void Textfield::OnBlur() { #endif // defined(OS_CHROMEOS) } StopBlinkingCursor(); - cursor_view_.SetVisible(false); + cursor_view_->SetVisible(false); DestroyTouchSelection(); @@ -1159,7 +1180,7 @@ void Textfield::OnThemeChanged() { render_text->set_selection_color(GetSelectionTextColor()); render_text->set_selection_background_focused_color( GetSelectionBackgroundColor()); - cursor_view_.layer()->SetColor(GetTextColor()); + cursor_view_->layer()->SetColor(GetTextColor()); } //////////////////////////////////////////////////////////////////////////////// @@ -1377,23 +1398,23 @@ bool Textfield::IsCommandIdEnabled(int command_id) const { bool Textfield::GetAcceleratorForCommandId(int command_id, ui::Accelerator* accelerator) const { switch (command_id) { - case IDS_APP_UNDO: + case kUndo: *accelerator = ui::Accelerator(ui::VKEY_Z, ui::EF_PLATFORM_ACCELERATOR); return true; - case IDS_APP_CUT: + case kCut: *accelerator = ui::Accelerator(ui::VKEY_X, ui::EF_PLATFORM_ACCELERATOR); return true; - case IDS_APP_COPY: + case kCopy: *accelerator = ui::Accelerator(ui::VKEY_C, ui::EF_PLATFORM_ACCELERATOR); return true; - case IDS_APP_PASTE: + case kPaste: *accelerator = ui::Accelerator(ui::VKEY_V, ui::EF_PLATFORM_ACCELERATOR); return true; - case IDS_APP_SELECT_ALL: + case kSelectAll: *accelerator = ui::Accelerator(ui::VKEY_A, ui::EF_PLATFORM_ACCELERATOR); return true; @@ -1459,13 +1480,17 @@ void Textfield::ClearCompositionText() { } void Textfield::InsertText(const base::string16& new_text) { - // TODO(suzhe): Filter invalid characters. - if (GetTextInputType() == ui::TEXT_INPUT_TYPE_NONE || new_text.empty()) + base::string16 filtered_new_text; + std::copy_if(new_text.begin(), new_text.end(), + std::back_inserter(filtered_new_text), IsValidCharToInsert); + + if (GetTextInputType() == ui::TEXT_INPUT_TYPE_NONE || + filtered_new_text.empty()) return; OnBeforeUserAction(); skip_input_method_cancel_composition_ = true; - model_->InsertText(new_text); + model_->InsertText(filtered_new_text); skip_input_method_cancel_composition_ = false; UpdateAfterChange(true, true); OnAfterUserAction(); @@ -1477,13 +1502,12 @@ void Textfield::InsertChar(const ui::KeyEvent& event) { return; } - // Filter out all control characters, including tab and new line characters, - // and all characters with Alt modifier (and Search on ChromeOS, Ctrl on - // Linux). But allow characters with the AltGr modifier. On Windows AltGr is - // represented by Alt+Ctrl or Right Alt, and on Linux it's a different flag - // that we don't care about. + // Filter all invalid chars and all characters with Alt modifier (and Search + // on ChromeOS, Ctrl on Linux). But allow characters with the AltGr modifier. + // On Windows AltGr is represented by Alt+Ctrl or Right Alt, and on Linux it's + // a different flag that we don't care about. const base::char16 ch = event.GetCharacter(); - const bool should_insert_char = ((ch >= 0x20 && ch < 0x7F) || ch > 0x9F) && + const bool should_insert_char = IsValidCharToInsert(ch) && !ui::IsSystemKeyModifier(event.flags()) && !IsControlKeyModifier(event.flags()); if (GetTextInputType() == ui::TEXT_INPUT_TYPE_NONE || !should_insert_char) @@ -1711,9 +1735,9 @@ bool Textfield::IsTextEditCommandEnabled(ui::TextEditCommand command) const { case ui::TextEditCommand::REDO: return editable && model_->CanRedo(); case ui::TextEditCommand::CUT: - return editable && readable && model_->HasSelection(); + return editable && readable && HasSelection(); case ui::TextEditCommand::COPY: - return readable && model_->HasSelection(); + return readable && HasSelection(); case ui::TextEditCommand::PASTE: ui::Clipboard::GetForCurrentThread()->ReadText( ui::ClipboardBuffer::kCopyPaste, &result); @@ -1722,8 +1746,7 @@ bool Textfield::IsTextEditCommandEnabled(ui::TextEditCommand command) const { return !GetText().empty() && GetSelectedRange().length() != GetText().length(); case ui::TextEditCommand::TRANSPOSE: - return editable && !model_->HasSelection() && - !model_->HasCompositionText(); + return editable && !HasSelection() && !model_->HasCompositionText(); case ui::TextEditCommand::YANK: return editable; case ui::TextEditCommand::MOVE_DOWN: @@ -2053,13 +2076,13 @@ void Textfield::RequestFocusWithPointer(ui::EventPointerType pointer_type) { return; switch (pointer_type) { - case ui::EventPointerType::POINTER_TYPE_MOUSE: + case ui::EventPointerType::kMouse: focus_reason_ = ui::TextInputClient::FOCUS_REASON_MOUSE; break; - case ui::EventPointerType::POINTER_TYPE_PEN: + case ui::EventPointerType::kPen: focus_reason_ = ui::TextInputClient::FOCUS_REASON_PEN; break; - case ui::EventPointerType::POINTER_TYPE_TOUCH: + case ui::EventPointerType::kTouch: focus_reason_ = ui::TextInputClient::FOCUS_REASON_TOUCH; break; default: @@ -2070,6 +2093,19 @@ void Textfield::RequestFocusWithPointer(ui::EventPointerType pointer_type) { View::RequestFocus(); } +void Textfield::RequestFocusForGesture(const ui::GestureEventDetails& details) { + bool show_virtual_keyboard = true; +#if defined(OS_WIN) + show_virtual_keyboard = + details.primary_pointer_type() == ui::EventPointerType::kTouch || + details.primary_pointer_type() == ui::EventPointerType::kPen; +#endif + + RequestFocusWithPointer(details.primary_pointer_type()); + if (show_virtual_keyboard) + ShowVirtualKeyboardIfEnabled(); +} + views::PropertyChangedSubscription Textfield::AddTextChangedCallback( views::PropertyChangedCallback callback) { return AddPropertyChangedCallback(&model_ + kTextfieldText, @@ -2216,7 +2252,7 @@ void Textfield::UpdateAfterChange(bool text_changed, bool cursor_changed) { } void Textfield::UpdateCursorVisibility() { - cursor_view_.SetVisible(ShouldShowCursor()); + cursor_view_->SetVisible(ShouldShowCursor()); if (ShouldBlinkCursor()) StartBlinkingCursor(); else @@ -2229,7 +2265,7 @@ void Textfield::UpdateCursorViewPosition() { location.set_height( std::min(location.height(), GetLocalBounds().height() - location.y() - location.y())); - cursor_view_.SetBoundsRect(location); + cursor_view_->SetBoundsRect(location); } int Textfield::GetTextStyle() const { @@ -2339,15 +2375,14 @@ void Textfield::UpdateContextMenu() { context_menu_contents_.reset(); context_menu_contents_ = std::make_unique<ui::SimpleMenuModel>(this); - context_menu_contents_->AddItemWithStringId(IDS_APP_UNDO, IDS_APP_UNDO); + context_menu_contents_->AddItemWithStringId(kUndo, IDS_APP_UNDO); context_menu_contents_->AddSeparator(ui::NORMAL_SEPARATOR); - context_menu_contents_->AddItemWithStringId(IDS_APP_CUT, IDS_APP_CUT); - context_menu_contents_->AddItemWithStringId(IDS_APP_COPY, IDS_APP_COPY); - context_menu_contents_->AddItemWithStringId(IDS_APP_PASTE, IDS_APP_PASTE); - context_menu_contents_->AddItemWithStringId(IDS_APP_DELETE, IDS_APP_DELETE); + context_menu_contents_->AddItemWithStringId(kCut, IDS_APP_CUT); + context_menu_contents_->AddItemWithStringId(kCopy, IDS_APP_COPY); + context_menu_contents_->AddItemWithStringId(kPaste, IDS_APP_PASTE); + context_menu_contents_->AddItemWithStringId(kDelete, IDS_APP_DELETE); context_menu_contents_->AddSeparator(ui::NORMAL_SEPARATOR); - context_menu_contents_->AddItemWithStringId(IDS_APP_SELECT_ALL, - IDS_APP_SELECT_ALL); + context_menu_contents_->AddItemWithStringId(kSelectAll, IDS_APP_SELECT_ALL); // If the controller adds menu commands, also override ExecuteCommand() and // IsCommandIdEnabled() as appropriate, for the commands added. @@ -2398,7 +2433,9 @@ void Textfield::OnEditFailed() { } bool Textfield::ShouldShowCursor() const { - return HasFocus() && !HasSelection() && GetEnabled() && !GetReadOnly() && + // Show the cursor when the primary selected range is empty; secondary + // selections do not affect cursor visibility. + return HasFocus() && !HasSelection(true) && GetEnabled() && !GetReadOnly() && !drop_cursor_visible_ && GetRenderText()->cursor_enabled(); } @@ -2419,7 +2456,7 @@ void Textfield::StopBlinkingCursor() { void Textfield::OnCursorBlinkTimerFired() { DCHECK(ShouldBlinkCursor()); UpdateCursorViewPosition(); - cursor_view_.SetVisible(!cursor_view_.GetVisible()); + cursor_view_->SetVisible(!cursor_view_->GetVisible()); } void Textfield::OnEnabledChanged() { diff --git a/chromium/ui/views/controls/textfield/textfield.h b/chromium/ui/views/controls/textfield/textfield.h index 21bafd3fe89..825c45513cf 100644 --- a/chromium/ui/views/controls/textfield/textfield.h +++ b/chromium/ui/views/controls/textfield/textfield.h @@ -31,6 +31,7 @@ #include "ui/base/ime/text_input_type.h" #include "ui/base/models/simple_menu_model.h" #include "ui/base/pointer/touch_editing_controller.h" +#include "ui/events/gesture_event_details.h" #include "ui/events/keycodes/keyboard_codes.h" #include "ui/gfx/font_list.h" #include "ui/gfx/geometry/insets.h" @@ -75,6 +76,13 @@ class VIEWS_EXPORT Textfield : public View, public: METADATA_HEADER(Textfield); + enum MenuCommands { + kUndo = kLastTouchEditableCommandId + 1, + kDelete, + kSelectAll, + kLastCommandId = kSelectAll, + }; + // Returns the text cursor blink time, or 0 for no blinking. static base::TimeDelta GetCaretBlinkInterval(); @@ -105,11 +113,14 @@ class VIEWS_EXPORT Textfield : public View, // textfield. const base::string16& GetText() const; - // Sets the text currently displayed in the Textfield. This doesn't - // change the cursor position if the current cursor is within the - // new text's range, or moves the cursor to the end if the cursor is - // out of the new text's range. + // Sets the text currently displayed in the Textfield and the cursor position. + // Calls to |SetText| are often followed by updating the selection or cursor, + // which does not update the edit history. I.e. the cursor position after + // redoing this change will be determined by |cursor_position| here and not by + // any subsequent calls to e.g. |SetSelectedRange|. Selections are not + // explicitly set here since redo's clear the selection anyways. void SetText(const base::string16& new_text); + void SetText(const base::string16& new_text, size_t cursor_position); // Appends the given string to the previously-existing text in the field. void AppendText(const base::string16& new_text); @@ -136,8 +147,9 @@ class VIEWS_EXPORT Textfield : public View, // Clears the selection within the edit field and sets the caret to the end. void ClearSelection(); - // Checks if there is any selected text. - bool HasSelection() const; + // Checks if there is any selected text. |primary_only| indicates whether + // secondary selections should also be considered. + bool HasSelection(bool primary_only = false) const; // Gets/sets the text color to be used when painting the Textfield. SkColor GetTextColor() const; @@ -206,6 +218,7 @@ class VIEWS_EXPORT Textfield : public View, // Selects the specified logical text range. void SetSelectedRange(const gfx::Range& range); + void SetSelectedRange(const gfx::Range& range, bool primary); // Gets the text selection model. const gfx::SelectionModel& GetSelectionModel() const; @@ -413,11 +426,14 @@ class VIEWS_EXPORT Textfield : public View, // override this to customize when the placeholder text is shown. virtual bool ShouldShowPlaceholderText() const; - protected: // Like RequestFocus, but explicitly states that the focus is triggered by // a pointer event. void RequestFocusWithPointer(ui::EventPointerType pointer_type); + // Like RequestFocus, but explicitly states that the focus is triggered by a + // gesture event. + void RequestFocusForGesture(const ui::GestureEventDetails& details); + private: friend class TextfieldTestApi; @@ -641,7 +657,7 @@ class VIEWS_EXPORT Textfield : public View, std::unique_ptr<views::MenuRunner> context_menu_runner_; // View containing the text cursor. - View cursor_view_; + View* cursor_view_ = nullptr; #if defined(OS_MACOSX) // Used to track active password input sessions. diff --git a/chromium/ui/views/controls/textfield/textfield_model.cc b/chromium/ui/views/controls/textfield/textfield_model.cc index 9e4896bf28d..d3df8334a98 100644 --- a/chromium/ui/views/controls/textfield/textfield_model.cc +++ b/chromium/ui/views/controls/textfield/textfield_model.cc @@ -7,7 +7,7 @@ #include <algorithm> #include <utility> -#include "base/logging.h" +#include "base/check_op.h" #include "base/macros.h" #include "base/message_loop/message_loop_current.h" #include "base/no_destructor.h" @@ -19,6 +19,30 @@ #include "ui/gfx/utf16_indexing.h" #include "ui/views/style/platform_style.h" +namespace { + +// Orders ranges decreasing with respect to their min index. This is useful for +// applying text edits such that an edit doesn't offset the positions of later +// edits. It should be reversed when undoing edits. +void order_ranges(std::vector<gfx::Range>* ranges) { + std::sort(ranges->begin(), ranges->end(), [](const auto& r1, const auto& r2) { + return r1.GetMin() > r2.GetMin(); + }); +} + +// Adjusts |position| for the deletion of |ranges|. E.g., if |position| is 10, +// and |ranges| is {{1, 3}, {15, 18}, and {6, 13}}, this will return 4, +// subtracting 2 (3-1), 0 (15>10), and 4 (10-6) for each range respectively. +size_t adjust_position_for_removals(size_t position, + std::vector<gfx::Range> ranges) { + size_t adjustment = 0; + for (auto range : ranges) + adjustment += range.Intersect(gfx::Range(0, position)).length(); + return position - adjustment; +} + +} // namespace + namespace views { namespace internal { @@ -38,15 +62,26 @@ 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_selection_); + // Insertions must be applied in order of increasing indices since |Redo| + // applies them in decreasing order. + auto insertion_texts = old_texts_; + std::reverse(insertion_texts.begin(), insertion_texts.end()); + auto insertion_text_starts = old_text_starts_; + std::reverse(insertion_text_starts.begin(), insertion_text_starts.end()); + model->ModifyText({{new_text_start_, new_text_end()}}, insertion_texts, + insertion_text_starts, old_primary_selection_, + old_secondary_selections_); } // 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_, - gfx::Range(new_cursor_pos_, new_cursor_pos_)); + std::vector<gfx::Range> deletions; + for (size_t i = 0; i < old_texts_.size(); ++i) { + deletions.emplace_back(old_text_starts_[i], + old_text_starts_[i] + old_texts_[i].length()); + } + model->ModifyText(deletions, {new_text_}, {new_text_start_}, + {new_cursor_pos_, new_cursor_pos_}, {}); } // Try to merge the |edit| into this edit and returns true on success. The @@ -73,24 +108,27 @@ class Edit { Edit(Type type, MergeType merge_type, - const base::string16& old_text, - size_t old_text_start, - gfx::Range old_selection, + std::vector<base::string16> old_texts, + std::vector<size_t> old_text_starts, + gfx::Range old_primary_selection, + std::vector<gfx::Range> old_secondary_selections, 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_text_(old_text), - old_text_start_(old_text_start), - old_selection_(old_selection), + old_texts_(old_texts), + old_text_starts_(old_text_starts), + old_primary_selection_(old_primary_selection), + old_secondary_selections_(old_secondary_selections), delete_backward_(delete_backward), new_cursor_pos_(new_cursor_pos), new_text_(new_text), new_text_start_(new_text_start) {} - // Each type of edit provides its own specific merge implementation. + // Each type of edit provides its own specific merge implementation. Assumes + // |edit| occurs after |this|. virtual bool DoMerge(const Edit* edit) = 0; Type type() const { return type_; } @@ -101,9 +139,6 @@ class Edit { // Should this edit be forcibly merged with the previous edit? bool force_merge() const { return merge_type_ == MergeType::kForceMerge; } - // Returns the end index of the |old_text_|. - size_t old_text_end() const { return old_text_start_ + old_text_.length(); } - // Returns the end index of the |new_text_|. size_t new_text_end() const { return new_text_start_ + new_text_.length(); } @@ -111,32 +146,43 @@ class Edit { // where an omnibox autocomplete string is set after a new character is typed. void MergeReplace(const Edit* edit) { CHECK_EQ(Type::kReplace, edit->type_); - CHECK_EQ(0U, edit->old_text_start_); + CHECK_EQ(1U, edit->old_text_starts_.size()); + CHECK_EQ(0U, edit->old_text_starts_[0]); CHECK_EQ(0U, edit->new_text_start_); - base::string16 old_text = edit->old_text_; - old_text.erase(new_text_start_, new_text_.length()); - old_text.insert(old_text_start_, old_text_); - // SetText() replaces entire text. Set |old_text_| to the entire - // replaced text with |this| edit undone. - old_text_ = old_text; - old_text_start_ = edit->old_text_start_; - delete_backward_ = false; - new_text_ = edit->new_text_; - new_text_start_ = edit->new_text_start_; + // We need to compute the merged edit's |old_texts_| by undoing this edit. + // Otherwise, |old_texts_| would be the autocompleted text following the + // user input. E.g., given goo|[gle.com], when the user types 'g', the text + // updates to goog|[le.com]. If we leave old_texts_ unchanged as 'gle.com', + // then undoing will result in 'gle.com' instead of 'goo|[gle.com]' + base::string16 old_texts = edit->old_texts_[0]; + // Remove |new_text_|. + old_texts.erase(new_text_start_, new_text_.length()); + // Add |old_texts_| in reverse order since we're undoing an edit. + for (size_t i = old_texts_.size(); i != 0; i--) + old_texts.insert(old_text_starts_[i - 1], old_texts_[i - 1]); + merge_type_ = MergeType::kDoNotMerge; + old_texts_ = {old_texts}; + old_text_starts_ = {0}; + delete_backward_ = false; + new_cursor_pos_ = edit->new_cursor_pos_; + new_text_ = edit->new_text_; + new_text_start_ = 0; } Type type_; // The type of merging allowed. MergeType merge_type_; - // 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_; + // Deleted texts ordered with decreasing indices. + std::vector<base::string16> old_texts_; + // The indices of |old_texts_|. + std::vector<size_t> old_text_starts_; + // The text selection ranges prior to the edit. |old_primary_selection_| + // represents the selection associated with the cursor. + gfx::Range old_primary_selection_; + std::vector<gfx::Range> old_secondary_selections_; // True if the deletion is made backward. bool delete_backward_; // New cursor position. @@ -149,100 +195,128 @@ class Edit { DISALLOW_COPY_AND_ASSIGN(Edit); }; +// Insert text at a given position. Assumes 1) no previous selection and 2) the +// insertion is at the cursor, which will advance by the insertion length. class InsertEdit : public Edit { public: InsertEdit(bool mergeable, const base::string16& new_text, size_t at) : Edit(Type::kInsert, mergeable ? MergeType::kMergeable : MergeType::kDoNotMerge, - base::string16(), - at, - gfx::Range(at, at), - false /* N/A */, - at + new_text.length() /* new cursor */, - new_text, - at) {} - - // Edit implementation. + {} /* old_texts */, + {} /* old_text_starts */, + {gfx::Range(at, at)} /* old_primary_selection */, + {} /* old_secondary_selections */, + false /* delete_backward */, + at + new_text.length() /* new_cursor_pos */, + new_text /* new_text */, + at /* new_text_start */) {} + + // Merge if |edit| is an insertion continuing forward where |this| ended. E.g. + // If |this| changed "ab|c" to "abX|c", an edit to "abXY|c" can be merged. bool DoMerge(const Edit* edit) override { + // Reject other edit types, and inserts starting somewhere other than where + // this insert ended. if (edit->type() != Type::kInsert || new_text_end() != edit->new_text_start_) return false; - // If continuous edit, merge it. - // TODO(oshima): gtk splits edits between whitespace. Find out what - // we want to here and implement if necessary. new_text_ += edit->new_text_; new_cursor_pos_ = edit->new_cursor_pos_; return true; } }; +// Delete one or more ranges and do a single insertion. The insertion need not +// be adjacent to the deletions (e.g. drag & drop). class ReplaceEdit : public Edit { public: ReplaceEdit(MergeType merge_type, - const base::string16& old_text, - size_t old_text_start, - gfx::Range old_selection, + std::vector<base::string16> old_texts, + std::vector<size_t> old_text_starts, + gfx::Range old_primary_selection, + std::vector<gfx::Range> old_secondary_selections, bool backward, size_t new_cursor_pos, const base::string16& new_text, size_t new_text_start) : Edit(Type::kReplace, merge_type, - old_text, - old_text_start, - old_selection, + old_texts, + old_text_starts, + old_primary_selection, + old_secondary_selections, backward, new_cursor_pos, new_text, new_text_start) {} - // Edit implementation. + // Merge if |edit| is an insertion or replacement continuing forward where + // |this| ended. E.g. If |this| changed "a|bc" to "aX|c", edits to "aXY|" or + // "aXYc" can be merged. Drag and drops are marked kDoNotMerge and should not + // get here. bool DoMerge(const Edit* edit) override { - if (edit->type() == Type::kDelete || - new_text_end() != edit->old_text_start_ || - edit->old_text_start_ != edit->new_text_start_) + // Reject deletions, replacements deleting multiple ranges, and edits + // inserting or deleting text somewhere other than where this edit ended. + if (edit->type() == Type::kDelete || edit->old_texts_.size() > 1 || + new_text_end() != edit->new_text_start_ || + (!edit->old_text_starts_.empty() && + new_text_end() != edit->old_text_starts_[0])) return false; - old_text_ += edit->old_text_; + if (edit->old_texts_.size() == 1) + old_texts_[0] += edit->old_texts_[0]; new_text_ += edit->new_text_; new_cursor_pos_ = edit->new_cursor_pos_; return true; } }; +// Delete possibly multiple texts. class DeleteEdit : public Edit { public: DeleteEdit(bool mergeable, - const base::string16& text, - size_t text_start, + std::vector<base::string16> texts, + std::vector<size_t> text_starts, + gfx::Range old_primary_selection, + std::vector<gfx::Range> old_secondary_selections, bool backward, - gfx::Range old_selection) + size_t new_cursor_pos) : Edit(Type::kDelete, mergeable ? MergeType::kMergeable : MergeType::kDoNotMerge, - text, - text_start, - old_selection, + texts, + text_starts, + old_primary_selection, + old_secondary_selections, backward, - text_start, - base::string16(), - text_start) {} + new_cursor_pos, + base::string16() /* new_text */, + 0 /* new_text_start */) {} - // Edit implementation. + // Merge if |edit| is a deletion continuing in the same direction and position + // where |this| ended. E.g. If |this| changed "ab|c" to "a|c" an edit to "|c" + // can be merged. bool DoMerge(const Edit* edit) override { if (edit->type() != Type::kDelete) return false; + // Deletions with selections are marked kDoNotMerge and should not get here. + DCHECK(old_secondary_selections_.empty()); + DCHECK(old_primary_selection_.is_empty()); + DCHECK(edit->old_secondary_selections_.empty()); + DCHECK(edit->old_primary_selection_.is_empty()); if (delete_backward_) { - // backspace can be merged only with backspace at the same position. - if (!edit->delete_backward_ || old_text_start_ != edit->old_text_end()) + // Backspace can be merged only with backspace at the same position. + if (!edit->delete_backward_ || + old_text_starts_[0] != + edit->old_text_starts_[0] + edit->old_texts_[0].length()) return false; - old_text_start_ = edit->old_text_start_; - old_text_ = edit->old_text_ + old_text_; + old_text_starts_[0] = edit->old_text_starts_[0]; + old_texts_[0] = edit->old_texts_[0] + old_texts_[0]; new_cursor_pos_ = edit->new_cursor_pos_; } else { - // delete can be merged only with delete at the same position. - if (edit->delete_backward_ || old_text_start_ != edit->old_text_start_) + // Delete can be merged only with delete at the same position. + if (edit->delete_backward_ || + old_text_starts_[0] != edit->old_text_starts_[0]) return false; - old_text_ += edit->old_text_; + old_texts_[0] += edit->old_texts_[0]; } return true; } @@ -315,7 +389,8 @@ TextfieldModel::~TextfieldModel() { ClearComposition(); } -bool TextfieldModel::SetText(const base::string16& new_text) { +bool TextfieldModel::SetText(const base::string16& new_text, + size_t cursor_position) { using MergeType = internal::MergeType; bool changed = false; if (HasCompositionText()) { @@ -325,14 +400,11 @@ bool TextfieldModel::SetText(const base::string16& new_text) { if (text() != new_text) { if (changed) // No need to remember composition. Undo(); - // SetText moves the cursor to the end. - size_t new_cursor = new_text.length(); // If there is a composition text, don't merge with previous edit. // Otherwise, force merge the edits. ExecuteAndRecordReplace( changed ? MergeType::kDoNotMerge : MergeType::kForceMerge, - gfx::Range(0, text().length()), new_cursor, new_text, 0U); - render_text_->SetCursorPosition(new_cursor); + {gfx::Range(0, text().length())}, cursor_position, new_text, 0U); } ClearSelection(); return changed; @@ -372,7 +444,7 @@ bool TextfieldModel::Delete(bool add_to_kill_buffer) { gfx::Range range_to_delete(cursor_position, next_grapheme_index); if (add_to_kill_buffer) SetKillBuffer(GetTextFromRange(range_to_delete)); - ExecuteAndRecordDelete(range_to_delete, true); + ExecuteAndRecordDelete({range_to_delete}, true); return true; } return false; @@ -400,7 +472,7 @@ bool TextfieldModel::Backspace(bool add_to_kill_buffer) { PlatformStyle::RangeToDeleteBackwards(text(), cursor_position)); if (add_to_kill_buffer) SetKillBuffer(GetTextFromRange(range_to_delete)); - ExecuteAndRecordDelete(range_to_delete, true); + ExecuteAndRecordDelete({range_to_delete}, true); return true; } return false; @@ -446,10 +518,10 @@ base::string16 TextfieldModel::GetSelectedText() const { return GetTextFromRange(render_text_->selection()); } -void TextfieldModel::SelectRange(const gfx::Range& range) { +void TextfieldModel::SelectRange(const gfx::Range& range, bool primary) { if (HasCompositionText()) ConfirmCompositionText(); - render_text_->SelectRange(range); + render_text_->SelectRange(range, primary); } void TextfieldModel::SelectSelectionModel(const gfx::SelectionModel& sel) { @@ -526,7 +598,8 @@ bool TextfieldModel::Redo() { } bool TextfieldModel::Cut() { - if (!HasCompositionText() && HasSelection() && !render_text_->obscured()) { + if (!HasCompositionText() && HasSelection(true) && + !render_text_->obscured()) { ui::ScopedClipboardWriter(ui::ClipboardBuffer::kCopyPaste) .WriteText(GetSelectedText()); DeleteSelection(); @@ -536,7 +609,8 @@ bool TextfieldModel::Cut() { } bool TextfieldModel::Copy() { - if (!HasCompositionText() && HasSelection() && !render_text_->obscured()) { + if (!HasCompositionText() && HasSelection(true) && + !render_text_->obscured()) { ui::ScopedClipboardWriter(ui::ClipboardBuffer::kCopyPaste) .WriteText(GetSelectedText()); return true; @@ -605,23 +679,30 @@ bool TextfieldModel::Yank() { return false; } -bool TextfieldModel::HasSelection() const { - return !render_text_->selection().is_empty(); +bool TextfieldModel::HasSelection(bool primary_only) const { + if (primary_only) + return !render_text_->selection().is_empty(); + auto selections = render_text_->GetAllSelections(); + return std::any_of( + selections.begin(), selections.end(), + [](const auto& selection) { return !selection.is_empty(); }); } void TextfieldModel::DeleteSelection() { DCHECK(!HasCompositionText()); DCHECK(HasSelection()); - ExecuteAndRecordDelete(render_text_->selection(), false); + ExecuteAndRecordDelete(render_text_->GetAllSelections(), false); } -void TextfieldModel::DeleteSelectionAndInsertTextAt( +void TextfieldModel::DeletePrimarySelectionAndInsertTextAt( const base::string16& new_text, size_t position) { using MergeType = internal::MergeType; if (HasCompositionText()) CancelCompositionText(); - ExecuteAndRecordReplace(MergeType::kDoNotMerge, render_text_->selection(), + // We don't use |ExecuteAndRecordReplaceSelection| because that assumes the + // insertion occurs at the cursor. + ExecuteAndRecordReplace(MergeType::kDoNotMerge, {render_text_->selection()}, position + new_text.length(), new_text, position); } @@ -774,13 +855,25 @@ void TextfieldModel::ClearRedoHistory() { edit_history_.erase(delete_start, edit_history_.end()); } -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(); +void TextfieldModel::ExecuteAndRecordDelete(std::vector<gfx::Range> ranges, + bool mergeable) { + // We need only check replacement_ranges[0] as |delete_backwards_| is + // irrelevant for multi-range deletions which can't be merged anyways. + const bool backward = ranges[0].is_reversed(); + order_ranges(&ranges); + + std::vector<base::string16> old_texts; + std::vector<size_t> old_text_starts; + for (const auto& range : ranges) { + old_texts.push_back(GetTextFromRange(range)); + old_text_starts.push_back(range.GetMin()); + } + + size_t cursor_pos = adjust_position_for_removals(GetCursorPosition(), ranges); + auto edit = std::make_unique<internal::DeleteEdit>( - mergeable, old_text, old_text_start, backward, curr_selection); + mergeable, old_texts, old_text_starts, render_text_->selection(), + render_text_->secondary_selections(), backward, cursor_pos); edit->Redo(this); AddOrMergeEditHistory(std::move(edit)); } @@ -788,22 +881,36 @@ void TextfieldModel::ExecuteAndRecordDelete(gfx::Range range, bool mergeable) { void TextfieldModel::ExecuteAndRecordReplaceSelection( internal::MergeType merge_type, const base::string16& new_text) { - size_t new_text_start = render_text_->selection().GetMin(); + auto replacement_ranges = render_text_->GetAllSelections(); + size_t new_text_start = + adjust_position_for_removals(GetCursorPosition(), replacement_ranges); size_t new_cursor_pos = new_text_start + new_text.length(); - ExecuteAndRecordReplace(merge_type, render_text_->selection(), new_cursor_pos, + + ExecuteAndRecordReplace(merge_type, replacement_ranges, new_cursor_pos, new_text, new_text_start); } -void TextfieldModel::ExecuteAndRecordReplace(internal::MergeType merge_type, - gfx::Range replacement_range, - size_t new_cursor_pos, - const base::string16& new_text, - size_t new_text_start) { - size_t old_text_start = replacement_range.GetMin(); - bool backward = replacement_range.is_reversed(); +void TextfieldModel::ExecuteAndRecordReplace( + internal::MergeType merge_type, + std::vector<gfx::Range> replacement_ranges, + size_t new_cursor_pos, + const base::string16& new_text, + size_t new_text_start) { + // We need only check replacement_ranges[0] as |delete_backwards_| is + // irrelevant for multi-range deletions which can't be merged anyways. + const bool backward = replacement_ranges[0].is_reversed(); + order_ranges(&replacement_ranges); + + std::vector<base::string16> old_texts; + std::vector<size_t> old_text_starts; + for (const auto& range : replacement_ranges) { + old_texts.push_back(GetTextFromRange(range)); + old_text_starts.push_back(range.GetMin()); + } + auto edit = std::make_unique<internal::ReplaceEdit>( - merge_type, GetTextFromRange(replacement_range), old_text_start, - render_text_->selection(), backward, new_cursor_pos, new_text, + merge_type, old_texts, old_text_starts, render_text_->selection(), + render_text_->secondary_selections(), backward, new_cursor_pos, new_text, new_text_start); edit->Redo(this); AddOrMergeEditHistory(std::move(edit)); @@ -838,23 +945,28 @@ void TextfieldModel::AddOrMergeEditHistory( } } -void TextfieldModel::ModifyText(size_t delete_from, - size_t delete_to, - const base::string16& new_text, - size_t new_text_insert_at, - gfx::Range selection) { - DCHECK_LE(delete_from, delete_to); +void TextfieldModel::ModifyText( + const std::vector<gfx::Range>& deletions, + const std::vector<base::string16>& insertion_texts, + const std::vector<size_t>& insertion_positions, + const gfx::Range& primary_selection, + const std::vector<gfx::Range>& secondary_selections) { + DCHECK_EQ(insertion_texts.size(), insertion_positions.size()); base::string16 old_text = text(); ClearComposition(); - if (delete_from != delete_to) - SetRenderTextText(old_text.erase(delete_from, delete_to - delete_from)); - if (!new_text.empty()) - SetRenderTextText(old_text.insert(new_text_insert_at, new_text)); - if (selection.start() == selection.end()) { - render_text_->SetCursorPosition(selection.start()); - } else { - render_text_->SelectRange(selection); - } + + for (auto deletion : deletions) + old_text.erase(deletion.start(), deletion.length()); + for (size_t i = 0; i < insertion_texts.size(); ++i) + old_text.insert(insertion_positions[i], insertion_texts[i]); + SetRenderTextText(old_text); + + if (primary_selection.start() == primary_selection.end()) + render_text_->SetCursorPosition(primary_selection.start()); + else + render_text_->SelectRange(primary_selection); + for (auto secondary_selection : secondary_selections) + render_text_->SelectRange(secondary_selection, false); } void TextfieldModel::SetRenderTextText(const base::string16& text) { diff --git a/chromium/ui/views/controls/textfield/textfield_model.h b/chromium/ui/views/controls/textfield/textfield_model.h index 0cc866ce613..9873bc160e9 100644 --- a/chromium/ui/views/controls/textfield/textfield_model.h +++ b/chromium/ui/views/controls/textfield/textfield_model.h @@ -65,12 +65,17 @@ class VIEWS_EXPORT TextfieldModel { // Edit related methods. const base::string16& text() const { return render_text_->text(); } - // Sets the text. Returns true if the text has been modified. The current - // composition text will be confirmed first. Setting the same text will not - // add edit history because it's not user visible change nor user-initiated - // change. This allow a client code to set the same text multiple times - // without worrying about messing edit history. - bool SetText(const base::string16& new_text); + // Sets the text. Returns true if the text was modified. The current + // composition text will be confirmed first. Setting the same text, even with + // an updated |cursor_position|, will neither add edit history nor change the + // cursor because it's neither a user visible change nor user-initiated + // change. This allows clients to set the same text multiple times without + // messing up edit history. The resulting history edit will have + // |new_cursor_pos| set to |cursor_position|. This is important even if + // subsequent calls will override the cursor position because updating the + // cursor alone won't update the edit history. I.e. the cursor position after + // applying or redoing the edit will be determined by |cursor_position|. + bool SetText(const base::string16& new_text, size_t cursor_position); gfx::RenderText* render_text() { return render_text_.get(); } @@ -138,13 +143,15 @@ class VIEWS_EXPORT TextfieldModel { // Selection related methods. - // Returns the selected text. + // Returns the primary selected text associated with the cursor. Does not + // return secondary selections. base::string16 GetSelectedText() const; - // The current composition text will be confirmed. The selection starts with - // the range's start position, and ends with the range's end position, - // therefore the cursor position becomes the end position. - void SelectRange(const gfx::Range& range); + // The current composition text will be confirmed. If |primary| is true, the + // selection starts with the range's start position and ends with the range's + // end position; therefore the cursor position becomes the end position. If + // |primary| is false, then the selection is not associated with the cursor. + void SelectRange(const gfx::Range& range, bool primary = true); // The current composition text will be confirmed. // render_text_'s selection model is set to |sel|. @@ -200,16 +207,17 @@ class VIEWS_EXPORT TextfieldModel { bool Yank(); // Tells if any text is selected, even if the selection is in composition - // text. - bool HasSelection() const; + // text. |primary_only| indicates whether secondary selections should also be + // considered. + bool HasSelection(bool primary_only = false) const; // Deletes the selected text. This method shouldn't be called with // composition text. void DeleteSelection(); // Deletes the selected text (if any) and insert text at given position. - void DeleteSelectionAndInsertTextAt(const base::string16& new_text, - size_t position); + void DeletePrimarySelectionAndInsertTextAt(const base::string16& new_text, + size_t position); // Retrieves the text content in a given range. base::string16 GetTextFromRange(const gfx::Range& range) const; @@ -256,23 +264,25 @@ class VIEWS_EXPORT TextfieldModel { FRIEND_TEST_ALL_PREFIXES(TextfieldModelTest, UndoRedo_CutCopyPasteTest); FRIEND_TEST_ALL_PREFIXES(TextfieldModelTest, UndoRedo_ReplaceTest); - // Insert the given |new_text|. |mergeable| indicates if this insert operation - // can be merged with previous edits in the history. + // Insert the given |new_text| at the cursor. |mergeable| indicates if this + // operation can be merged with previous edits in the history. Will delete any + // selected text. void InsertTextInternal(const base::string16& new_text, bool mergeable); - // Replace the current text with the given |new_text|. |mergeable| indicates - // if this replace operation can be merged with previous edits in the history. + // Replace the current selected text with the given |new_text|. |mergeable| + // indicates if this operation can be merged with previous edits in the + // history. void ReplaceTextInternal(const base::string16& new_text, bool mergeable); // Clears redo history. void ClearRedoHistory(); // Executes and records edit operations. - void ExecuteAndRecordDelete(gfx::Range range, bool mergeable); + void ExecuteAndRecordDelete(std::vector<gfx::Range> ranges, bool mergeable); void ExecuteAndRecordReplaceSelection(internal::MergeType merge_type, const base::string16& new_text); void ExecuteAndRecordReplace(internal::MergeType merge_type, - gfx::Range replacement_range, + std::vector<gfx::Range> replacement_range, size_t new_cursor_pos, const base::string16& new_text, size_t new_text_start); @@ -282,15 +292,20 @@ 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 |delete_to|. - // 2) Insert the |new_text| at the index |new_text_insert_at|. - // Note that the index is after deletion. - // 3) Select |selection|. - void ModifyText(size_t delete_from, - size_t delete_to, - const base::string16& new_text, - size_t new_text_insert_at, - gfx::Range selection); + // 1) Delete the |deletions|. + // 2) Insert the |insertion_texts| at the |insertion_positions|. + // 3) Select |primary_selection| and |secondary_selections|. + // Deletions and insertions are applied in order and affect later edit + // indices. E.g., given 'xyz', inserting 'A' at 1 and 'B' at 2 will result in + // 'xAByz', not 'xAyBz'. On the other hand, inserting 'B' at 2 then 'A' at 1 + // will result in 'xAyBz'. Thus, for applying or redoing edits, they should be + // ordered with increasing indices; while for undoing edits, they should be + // ordered decreasing. + void ModifyText(const std::vector<gfx::Range>& deletions, + const std::vector<base::string16>& insertion_texts, + const std::vector<size_t>& insertion_positions, + const gfx::Range& primary_selection, + const std::vector<gfx::Range>& secondary_selections); // Calls render_text->SetText() and delegate's callback. void SetRenderTextText(const base::string16& text); diff --git a/chromium/ui/views/controls/textfield/textfield_model_unittest.cc b/chromium/ui/views/controls/textfield/textfield_model_unittest.cc index 91d5f62a0c7..d9b29c3f0fb 100644 --- a/chromium/ui/views/controls/textfield/textfield_model_unittest.cc +++ b/chromium/ui/views/controls/textfield/textfield_model_unittest.cc @@ -57,10 +57,27 @@ class TextfieldModelTest : public ViewsTestBase, protected: void ResetModel(TextfieldModel* model) const { - model->SetText(base::string16()); + model->SetText(base::string16(), 0); model->ClearEditHistory(); } + const std::vector<base::string16> GetAllSelectionTexts( + TextfieldModel* model) const { + std::vector<base::string16> selected_texts; + for (auto range : model->render_text()->GetAllSelections()) + selected_texts.push_back(model->GetTextFromRange(range)); + return selected_texts; + } + + void VerifyAllSelectionTexts( + TextfieldModel* model, + std::vector<std::string> expected_selected_texts) const { + std::vector<base::string16> selected_texts = GetAllSelectionTexts(model); + EXPECT_EQ(expected_selected_texts.size(), selected_texts.size()); + for (size_t i = 0; i < selected_texts.size(); ++i) + EXPECT_STR_EQ(expected_selected_texts[i], selected_texts[i]); + } + bool composition_text_confirmed_or_cleared_ = false; private: @@ -191,7 +208,7 @@ TEST_F(TextfieldModelTest, EditString_ComplexScript) { #endif // Test cursor position and deletion for Hindi Virama. - model.SetText(base::WideToUTF16(L"\x0D38\x0D4D\x0D15\x0D16\x0D2E")); + model.SetText(base::WideToUTF16(L"\x0D38\x0D4D\x0D15\x0D16\x0D2E"), 0); model.MoveCursorTo(0); EXPECT_EQ(0U, model.GetCursorPosition()); @@ -210,7 +227,7 @@ TEST_F(TextfieldModelTest, EditString_ComplexScript) { #endif model.SetText( - base::WideToUTF16(L"\x05d5\x05b7\x05D9\x05B0\x05D4\x05B4\x05D9")); + base::WideToUTF16(L"\x05d5\x05b7\x05D9\x05B0\x05D4\x05B4\x05D9"), 0); model.MoveCursorTo(0); EXPECT_TRUE(model.Delete()); EXPECT_TRUE(model.Delete()); @@ -220,7 +237,7 @@ TEST_F(TextfieldModelTest, EditString_ComplexScript) { // The first 2 characters are not strong directionality characters. model.SetText( - base::WideToUTF16(L"\x002C\x0020\x05D1\x05BC\x05B7\x05E9\x05BC")); + base::WideToUTF16(L"\x002C\x0020\x05D1\x05BC\x05B7\x05E9\x05BC"), 0); model.MoveCursor(gfx::LINE_BREAK, gfx::CURSOR_LEFT, gfx::SELECTION_NONE); EXPECT_TRUE(model.Backspace()); EXPECT_EQ(base::WideToUTF16(L"\x002C\x0020\x05D1\x05BC\x05B7\x05E9"), @@ -229,7 +246,7 @@ TEST_F(TextfieldModelTest, EditString_ComplexScript) { // Halfwidth katakana ダ: // "HALFWIDTH KATAKANA LETTER TA" + "HALFWIDTH KATAKANA VOICED SOUND MARK" // ("ABC" prefix as sanity check that the entire string isn't deleted). - model.SetText(base::WideToUTF16(L"ABC\xFF80\xFF9E")); + model.SetText(base::WideToUTF16(L"ABC\xFF80\xFF9E"), 0); model.MoveCursorTo(model.text().length()); model.Backspace(); #if defined(OS_MACOSX) @@ -244,7 +261,7 @@ TEST_F(TextfieldModelTest, EditString_ComplexScript) { // Emoji with Fitzpatrick modifier: // 'BOY' + 'EMOJI MODIFIER FITZPATRICK TYPE-5' - model.SetText(base::WideToUTF16(L"\U0001F466\U0001F3FE")); + model.SetText(base::WideToUTF16(L"\U0001F466\U0001F3FE"), 0); model.MoveCursorTo(model.text().length()); model.Backspace(); #if defined(OS_MACOSX) @@ -316,6 +333,23 @@ TEST_F(TextfieldModelTest, Selection) { gfx::SELECTION_NONE); EXPECT_EQ(3U, model.GetCursorPosition()); + // Select multiple ranges and move cursor. + model.SelectRange(gfx::Range(1U, 3U)); + model.SelectRange(gfx::Range(5U, 4U), false); + EXPECT_STR_EQ("EL", model.GetSelectedText()); + EXPECT_EQ(3U, model.GetCursorPosition()); + model.MoveCursor(gfx::CHARACTER_BREAK, gfx::CURSOR_LEFT, gfx::SELECTION_NONE); + EXPECT_TRUE(model.GetSelectedText().empty()); + EXPECT_EQ(1U, model.GetCursorPosition()); + EXPECT_TRUE(model.render_text()->secondary_selections().empty()); + model.SelectRange(gfx::Range(1U, 3U)); + model.SelectRange(gfx::Range(4U, 5U), false); + model.MoveCursor(gfx::CHARACTER_BREAK, gfx::CURSOR_RIGHT, + gfx::SELECTION_NONE); + EXPECT_TRUE(model.GetSelectedText().empty()); + EXPECT_EQ(3U, model.GetCursorPosition()); + EXPECT_TRUE(model.render_text()->secondary_selections().empty()); + // Select all and move cursor. model.SelectAll(false); model.MoveCursor(gfx::CHARACTER_BREAK, gfx::CURSOR_LEFT, gfx::SELECTION_NONE); @@ -378,9 +412,9 @@ TEST_F(TextfieldModelTest, Selection_BidiWithNonSpacingMarks) { // In case of "aBc", this test shows how to select "aB" or "Bc", assume 'B' is // an RTL character. - model.SetText( - base::WideToUTF16(L"a\x05E9" - L"b")); + model.SetText(base::WideToUTF16(L"a\x05E9" + L"b"), + 0); model.MoveCursorTo(0); model.MoveCursor(gfx::CHARACTER_BREAK, gfx::CURSOR_RIGHT, gfx::SELECTION_RETAIN); @@ -479,6 +513,56 @@ TEST_F(TextfieldModelTest, SelectionAndEdit) { EXPECT_STR_EQ("BEE", model.text()); } +TEST_F(TextfieldModelTest, SelectionAndEdit_WithSecondarySelection) { + // Backspace + TextfieldModel model(nullptr); + model.Append(base::ASCIIToUTF16("asynchronous promises make the moon spin?")); + model.SelectRange(gfx::Range(0U, 4U)); + model.SelectRange(gfx::Range(17U, 19U), false); + model.SelectRange(gfx::Range(15U, 7U), false); + model.SelectRange(gfx::Range(41U, 20U), false); + EXPECT_TRUE(model.Backspace()); + EXPECT_STR_EQ("chrome", model.text()); + EXPECT_TRUE(model.GetSelectedText().empty()); + EXPECT_EQ(0U, model.GetCursorPosition()); + EXPECT_TRUE(model.render_text()->secondary_selections().empty()); + + // Delete with an empty primary selection + model.Append(base::ASCIIToUTF16(" is constructor overloading bad?")); + model.SelectRange(gfx::Range(1U, 1U)); + model.SelectRange(gfx::Range(22U, 12U), false); + model.SelectRange(gfx::Range(26U, 23U), false); + model.SelectRange(gfx::Range(27U, 38U), false); + EXPECT_TRUE(model.Delete()); + EXPECT_STR_EQ("chrome is cool", model.text()); + EXPECT_TRUE(model.GetSelectedText().empty()); + EXPECT_EQ(1U, model.GetCursorPosition()); + EXPECT_TRUE(model.render_text()->secondary_selections().empty()); + + // Insert + model.Append(base::ASCIIToUTF16(" are inherited classes heavy?")); + model.SelectRange(gfx::Range(27U, 16U)); + model.SelectRange(gfx::Range(41U, 34U), false); + model.SelectRange(gfx::Range(42U, 43U), false); + model.InsertChar('n'); + EXPECT_STR_EQ("chrome is cool and classy", model.text()); + EXPECT_TRUE(model.GetSelectedText().empty()); + EXPECT_EQ(17U, model.GetCursorPosition()); + EXPECT_TRUE(model.render_text()->secondary_selections().empty()); + + // Replace + model.Append( + base::ASCIIToUTF16("help! why can't i instantiate an abstract sun!?")); + model.SelectRange(gfx::Range(71U, 72U)); + model.SelectRange(gfx::Range(30U, 70U), false); + model.SelectRange(gfx::Range(29U, 25U), false); + model.ReplaceChar('!'); + EXPECT_STR_EQ("chrome is cool and classy!!!", model.text()); + EXPECT_TRUE(model.GetSelectedText().empty()); + EXPECT_EQ(28U, model.GetCursorPosition()); + EXPECT_TRUE(model.render_text()->secondary_selections().empty()); +} + TEST_F(TextfieldModelTest, Word) { TextfieldModel model(nullptr); model.Append( @@ -577,21 +661,33 @@ TEST_F(TextfieldModelTest, Word) { TEST_F(TextfieldModelTest, SetText) { TextfieldModel model(nullptr); model.Append(base::ASCIIToUTF16("HELLO")); + + // SetText moves cursor to the indicated position. model.MoveCursor(gfx::LINE_BREAK, gfx::CURSOR_RIGHT, gfx::SELECTION_NONE); - model.SetText(base::ASCIIToUTF16("GOODBYE")); + model.SetText(base::ASCIIToUTF16("GOODBYE"), 6); EXPECT_STR_EQ("GOODBYE", model.text()); - // SetText move the cursor to the end of the new text. - EXPECT_EQ(7U, model.GetCursorPosition()); + EXPECT_EQ(6U, model.GetCursorPosition()); + model.SetText(base::ASCIIToUTF16("SUNSET"), 6); + EXPECT_STR_EQ("SUNSET", model.text()); + EXPECT_EQ(6U, model.GetCursorPosition()); model.SelectAll(false); - EXPECT_STR_EQ("GOODBYE", model.GetSelectedText()); + EXPECT_STR_EQ("SUNSET", model.GetSelectedText()); model.MoveCursor(gfx::LINE_BREAK, gfx::CURSOR_RIGHT, gfx::SELECTION_NONE); - EXPECT_EQ(7U, model.GetCursorPosition()); + EXPECT_EQ(6U, model.GetCursorPosition()); - model.SetText(base::ASCIIToUTF16("BYE")); - // Setting shorter string moves the cursor to the end of the new string. + // Setting text to the current text should not modify the cursor position. + model.SetText(base::ASCIIToUTF16("SUNSET"), 3); + EXPECT_STR_EQ("SUNSET", model.text()); + EXPECT_EQ(6U, model.GetCursorPosition()); + + // Setting text that's shorter than the indicated cursor moves the cursor to + // the text end. + model.SetText(base::ASCIIToUTF16("BYE"), 5); EXPECT_EQ(3U, model.GetCursorPosition()); EXPECT_EQ(base::string16(), model.GetSelectedText()); - model.SetText(base::string16()); + + // SetText with empty string. + model.SetText(base::string16(), 0); EXPECT_EQ(0U, model.GetCursorPosition()); } @@ -615,7 +711,7 @@ TEST_F(TextfieldModelTest, Clipboard) { EXPECT_EQ(11U, model.GetCursorPosition()); // Copy with an empty selection should do nothing. - model.Copy(); + EXPECT_FALSE(model.Copy()); clipboard->ReadText(ui::ClipboardBuffer::kCopyPaste, &clipboard_text); EXPECT_EQ(initial_clipboard_text, clipboard_text); EXPECT_STR_EQ("HELLO WORLD", model.text()); @@ -666,6 +762,132 @@ TEST_F(TextfieldModelTest, Clipboard) { EXPECT_TRUE(model.Paste()); EXPECT_STR_EQ("HELLO HELLOHELLO", model.text()); EXPECT_EQ(16U, model.GetCursorPosition()); + + // Paste should replace the selection. + model.render_text()->SetObscured(false); + model.SetText(base::ASCIIToUTF16("It's time to say goodbye."), 0); + model.SelectRange({17, 24}); + EXPECT_TRUE(model.Paste()); + clipboard->ReadText(ui::ClipboardBuffer::kCopyPaste, &clipboard_text); + EXPECT_STR_EQ("HELLO ", clipboard_text); + EXPECT_STR_EQ("It's time to say HELLO.", model.text()); + EXPECT_EQ(22U, model.GetCursorPosition()); + EXPECT_FALSE(model.HasSelection()); + EXPECT_TRUE(model.render_text()->secondary_selections().empty()); + + // Paste with an empty clipboard should not replace the selection. + ui::Clipboard::GetForCurrentThread()->Clear(ui::ClipboardBuffer::kCopyPaste); + model.SelectRange({5, 8}); + EXPECT_FALSE(model.Paste()); + clipboard->ReadText(ui::ClipboardBuffer::kCopyPaste, &clipboard_text); + EXPECT_TRUE(clipboard_text.empty()); + EXPECT_STR_EQ("It's time to say HELLO.", model.text()); + EXPECT_EQ(8U, model.GetCursorPosition()); + EXPECT_STR_EQ("tim", model.GetSelectedText()); +} + +TEST_F(TextfieldModelTest, Clipboard_WithSecondarySelections) { + ui::Clipboard* clipboard = ui::Clipboard::GetForCurrentThread(); + const base::string16 initial_clipboard_text = + base::ASCIIToUTF16("initial text"); + ui::ScopedClipboardWriter(ui::ClipboardBuffer::kCopyPaste) + .WriteText(initial_clipboard_text); + + base::string16 clipboard_text; + TextfieldModel model(nullptr); + model.Append(base::ASCIIToUTF16("It's time to say HELLO.")); + + // Cut with multiple selections should copy only the primary selection but + // delete all selections. + model.SelectRange({0, 5}); + model.SelectRange({13, 17}, false); + EXPECT_TRUE(model.Cut()); + clipboard->ReadText(ui::ClipboardBuffer::kCopyPaste, &clipboard_text); + EXPECT_STR_EQ("It's ", clipboard_text); + EXPECT_STR_EQ("time to HELLO.", model.text()); + EXPECT_EQ(0U, model.GetCursorPosition()); + EXPECT_FALSE(model.HasSelection()); + EXPECT_TRUE(model.render_text()->secondary_selections().empty()); + + // Copy with multiple selections should copy only the primary selection and + // retain multiple selections. + model.SelectRange({13, 8}); + model.SelectRange({0, 4}, false); + EXPECT_TRUE(model.Copy()); + clipboard->ReadText(ui::ClipboardBuffer::kCopyPaste, &clipboard_text); + EXPECT_STR_EQ("HELLO", clipboard_text); + EXPECT_STR_EQ("time to HELLO.", model.text()); + EXPECT_EQ(8U, model.GetCursorPosition()); + EXPECT_TRUE(model.HasSelection()); + VerifyAllSelectionTexts(&model, {"HELLO", "time"}); + + // Paste with multiple selections should paste at the primary selection and + // delete all selections. + model.SelectRange({0, 1}); + model.SelectRange({5, 8}, false); + model.SelectRange({14, 14}, false); + EXPECT_TRUE(model.Paste()); + clipboard->ReadText(ui::ClipboardBuffer::kCopyPaste, &clipboard_text); + EXPECT_STR_EQ("HELLO", clipboard_text); + EXPECT_STR_EQ("HELLOime HELLO.", model.text()); + EXPECT_EQ(5U, model.GetCursorPosition()); + EXPECT_FALSE(model.HasSelection()); + EXPECT_TRUE(model.render_text()->secondary_selections().empty()); + + // Paste with multiple selections and an empty clipboard should not change the + // text or selections. + ui::Clipboard::GetForCurrentThread()->Clear(ui::ClipboardBuffer::kCopyPaste); + model.SelectRange({1, 2}); + model.SelectRange({4, 5}, false); + EXPECT_FALSE(model.Paste()); + clipboard->ReadText(ui::ClipboardBuffer::kCopyPaste, &clipboard_text); + EXPECT_TRUE(clipboard_text.empty()); + EXPECT_STR_EQ("HELLOime HELLO.", model.text()); + EXPECT_EQ(2U, model.GetCursorPosition()); + VerifyAllSelectionTexts(&model, {"E", "O"}); + + // Cut with an empty primary selection and nonempty secondary selections + // should neither delete the secondary selection nor replace the clipboard. + ui::ScopedClipboardWriter(ui::ClipboardBuffer::kCopyPaste) + .WriteText(initial_clipboard_text); + model.SelectRange({2, 2}); + model.SelectRange({4, 5}, false); + EXPECT_FALSE(model.Cut()); + clipboard->ReadText(ui::ClipboardBuffer::kCopyPaste, &clipboard_text); + EXPECT_STR_EQ("initial text", clipboard_text); + EXPECT_STR_EQ("HELLOime HELLO.", model.text()); + EXPECT_EQ(2U, model.GetCursorPosition()); + VerifyAllSelectionTexts(&model, {"", "O"}); + + // Copy with an empty primary selection and nonempty secondary selections + // should not replace the clipboard. + EXPECT_FALSE(model.Copy()); + clipboard->ReadText(ui::ClipboardBuffer::kCopyPaste, &clipboard_text); + EXPECT_STR_EQ("initial text", clipboard_text); + EXPECT_STR_EQ("HELLOime HELLO.", model.text()); + EXPECT_EQ(2U, model.GetCursorPosition()); + VerifyAllSelectionTexts(&model, {"", "O"}); + + // Paste with an empty primary selection, nonempty secondary selection, and + // empty clipboard should change neither the text nor the selections. + ui::Clipboard::GetForCurrentThread()->Clear(ui::ClipboardBuffer::kCopyPaste); + EXPECT_FALSE(model.Paste()); + clipboard->ReadText(ui::ClipboardBuffer::kCopyPaste, &clipboard_text); + EXPECT_TRUE(clipboard_text.empty()); + EXPECT_STR_EQ("HELLOime HELLO.", model.text()); + EXPECT_EQ(2U, model.GetCursorPosition()); + VerifyAllSelectionTexts(&model, {"", "O"}); + + // Paste with an empty primary selection and nonempty secondary selections + // should paste at the primary selection and delete the secondary selections. + ui::ScopedClipboardWriter(ui::ClipboardBuffer::kCopyPaste) + .WriteText(initial_clipboard_text); + EXPECT_TRUE(model.Paste()); + clipboard->ReadText(ui::ClipboardBuffer::kCopyPaste, &clipboard_text); + EXPECT_STR_EQ("initial text", clipboard_text); + EXPECT_STR_EQ("HEinitial textLLime HELLO.", model.text()); + EXPECT_EQ(14U, model.GetCursorPosition()); + EXPECT_FALSE(model.HasSelection()); } static void SelectWordTestVerifier( @@ -734,9 +956,9 @@ TEST_F(TextfieldModelTest, SelectWordTest_MixScripts) { word_and_cursor.emplace_back(L"\x5929", 14); // The text consists of Ascii, Hebrew, Hindi with Virama sign, and Chinese. - model.SetText( - base::WideToUTF16(L"a\x05d0 \x05d1\x05d2 \x0915\x094d\x0915 " - L"\x4E2D\x56FD\x82B1\x5929")); + model.SetText(base::WideToUTF16(L"a\x05d0 \x05d1\x05d2 \x0915\x094d\x0915 " + L"\x4E2D\x56FD\x82B1\x5929"), + 0); for (size_t i = 0; i < word_and_cursor.size(); ++i) { model.MoveCursor(gfx::LINE_BREAK, gfx::CURSOR_LEFT, gfx::SELECTION_NONE); for (size_t j = 0; j < i; ++j) @@ -871,42 +1093,57 @@ TEST_F(TextfieldModelTest, SelectRangeTest) { gfx::Range range(0, 6); EXPECT_FALSE(range.is_reversed()); model.SelectRange(range); + EXPECT_TRUE(model.HasSelection()); EXPECT_STR_EQ("HELLO ", model.GetSelectedText()); range = gfx::Range(6, 1); EXPECT_TRUE(range.is_reversed()); model.SelectRange(range); + EXPECT_TRUE(model.HasSelection()); EXPECT_STR_EQ("ELLO ", model.GetSelectedText()); range = gfx::Range(2, 1000); EXPECT_FALSE(range.is_reversed()); model.SelectRange(range); + EXPECT_TRUE(model.HasSelection()); EXPECT_STR_EQ("LLO WORLD", model.GetSelectedText()); range = gfx::Range(1000, 3); EXPECT_TRUE(range.is_reversed()); model.SelectRange(range); + EXPECT_TRUE(model.HasSelection()); EXPECT_STR_EQ("LO WORLD", model.GetSelectedText()); range = gfx::Range(0, 0); EXPECT_TRUE(range.is_empty()); model.SelectRange(range); + EXPECT_FALSE(model.HasSelection()); EXPECT_TRUE(model.GetSelectedText().empty()); range = gfx::Range(3, 3); EXPECT_TRUE(range.is_empty()); model.SelectRange(range); + EXPECT_FALSE(model.HasSelection()); EXPECT_TRUE(model.GetSelectedText().empty()); range = gfx::Range(1000, 100); EXPECT_FALSE(range.is_empty()); model.SelectRange(range); + EXPECT_FALSE(model.HasSelection()); EXPECT_TRUE(model.GetSelectedText().empty()); range = gfx::Range(1000, 1000); EXPECT_TRUE(range.is_empty()); model.SelectRange(range); + EXPECT_FALSE(model.HasSelection()); EXPECT_TRUE(model.GetSelectedText().empty()); + + EXPECT_TRUE(range.is_empty()); + model.SelectRange({1, 5}); + model.SelectRange({100, 7}, false); + EXPECT_TRUE(model.HasSelection()); + EXPECT_STR_EQ("ELLO", model.GetSelectedText()); + VerifyAllSelectionTexts(&model, {"ELLO", "ORLD"}); } TEST_F(TextfieldModelTest, SelectionTest) { @@ -994,6 +1231,16 @@ TEST_F(TextfieldModelTest, SelectSelectionModelTest) { model.SelectSelectionModel(gfx::SelectionModel(1000, gfx::CURSOR_BACKWARD)); EXPECT_TRUE(model.GetSelectedText().empty()); + + gfx::SelectionModel mutliselection_selection_model{{2, 3}, + gfx::CURSOR_BACKWARD}; + mutliselection_selection_model.AddSecondarySelection({5, 4}); + mutliselection_selection_model.AddSecondarySelection({1, 0}); + mutliselection_selection_model.AddSecondarySelection({20, 9}); + mutliselection_selection_model.AddSecondarySelection({6, 6}); + model.SelectSelectionModel(mutliselection_selection_model); + EXPECT_STR_EQ("L", model.GetSelectedText()); + VerifyAllSelectionTexts(&model, {"L", "O", "H", "LD", ""}); } TEST_F(TextfieldModelTest, CompositionTextTest) { @@ -1065,7 +1312,7 @@ TEST_F(TextfieldModelTest, CompositionTextTest) { model.SetCompositionText(composition); EXPECT_STR_EQ("1234567890", model.text()); - EXPECT_TRUE(model.SetText(base::ASCIIToUTF16("1234567890"))); + EXPECT_TRUE(model.SetText(base::ASCIIToUTF16("1234567890"), 0)); EXPECT_TRUE(composition_text_confirmed_or_cleared_); composition_text_confirmed_or_cleared_ = false; model.MoveCursor(gfx::LINE_BREAK, gfx::CURSOR_RIGHT, gfx::SELECTION_NONE); @@ -1123,7 +1370,7 @@ TEST_F(TextfieldModelTest, CompositionTextTest) { composition_text_confirmed_or_cleared_ = false; EXPECT_STR_EQ("1234567890-678-", model.text()); - model.SetText(base::string16()); + model.SetText(base::string16(), 0); model.SetCompositionText(composition); model.MoveCursor(gfx::CHARACTER_BREAK, gfx::CURSOR_LEFT, gfx::SELECTION_NONE); EXPECT_TRUE(composition_text_confirmed_or_cleared_); @@ -1145,7 +1392,7 @@ TEST_F(TextfieldModelTest, CompositionTextTest) { composition_text_confirmed_or_cleared_ = false; EXPECT_STR_EQ("676788678", model.text()); - model.SetText(base::string16()); + model.SetText(base::string16(), 0); model.SetCompositionText(composition); model.MoveCursor(gfx::WORD_BREAK, gfx::CURSOR_RIGHT, gfx::SELECTION_NONE); EXPECT_TRUE(composition_text_confirmed_or_cleared_); @@ -1268,7 +1515,7 @@ TEST_F(TextfieldModelTest, UndoRedo_BasicTest) { EXPECT_EQ(1U, model.GetCursorPosition()); // Delete =============================== - model.SetText(base::ASCIIToUTF16("ABCDE")); + model.SetText(base::ASCIIToUTF16("ABCDE"), 0); model.ClearEditHistory(); model.MoveCursorTo(2); EXPECT_TRUE(model.Delete()); @@ -1300,31 +1547,34 @@ TEST_F(TextfieldModelTest, UndoRedo_BasicTest) { TEST_F(TextfieldModelTest, UndoRedo_SetText) { // This is to test the undo/redo behavior of omnibox. TextfieldModel model(nullptr); - model.InsertChar('w'); + // Simulate typing www.y while www.google.com and www.youtube.com are + // autocompleted. + model.InsertChar('w'); // w| EXPECT_STR_EQ("w", model.text()); EXPECT_EQ(1U, model.GetCursorPosition()); - model.SetText(base::ASCIIToUTF16("www.google.com")); - EXPECT_EQ(14U, model.GetCursorPosition()); + model.SetText(base::ASCIIToUTF16("www.google.com"), 1); // w|ww.google.com + model.SelectRange(gfx::Range(14, 1)); // w[ww.google.com] + EXPECT_EQ(1U, model.GetCursorPosition()); EXPECT_STR_EQ("www.google.com", model.text()); - model.SelectRange(gfx::Range(14, 1)); - model.InsertChar('w'); + model.InsertChar('w'); // ww| EXPECT_STR_EQ("ww", model.text()); - model.SetText(base::ASCIIToUTF16("www.google.com")); - model.SelectRange(gfx::Range(14, 2)); - model.InsertChar('w'); + model.SetText(base::ASCIIToUTF16("www.google.com"), 2); // ww|w.google.com + model.SelectRange(gfx::Range(14, 2)); // ww[w.google.com] + model.InsertChar('w'); // www| EXPECT_STR_EQ("www", model.text()); - model.SetText(base::ASCIIToUTF16("www.google.com")); - model.SelectRange(gfx::Range(14, 3)); - model.InsertChar('.'); + model.SetText(base::ASCIIToUTF16("www.google.com"), 3); // www|.google.com + model.SelectRange(gfx::Range(14, 3)); // www[.google.com] + model.InsertChar('.'); // www.| EXPECT_STR_EQ("www.", model.text()); - model.SetText(base::ASCIIToUTF16("www.google.com")); - model.SelectRange(gfx::Range(14, 4)); - model.InsertChar('y'); + model.SetText(base::ASCIIToUTF16("www.google.com"), 4); // www.|google.com + model.SelectRange(gfx::Range(14, 4)); // www.[google.com] + model.InsertChar('y'); // www.y| EXPECT_STR_EQ("www.y", model.text()); - model.SetText(base::ASCIIToUTF16("www.youtube.com")); + model.SetText(base::ASCIIToUTF16("www.youtube.com"), 5); // www.y|outube.com EXPECT_STR_EQ("www.youtube.com", model.text()); - EXPECT_EQ(15U, model.GetCursorPosition()); + EXPECT_EQ(5U, model.GetCursorPosition()); + // Undo until the initial edit. EXPECT_TRUE(model.Undo()); EXPECT_STR_EQ("www.google.com", model.text()); EXPECT_EQ(4U, model.GetCursorPosition()); @@ -1341,6 +1591,8 @@ TEST_F(TextfieldModelTest, UndoRedo_SetText) { EXPECT_STR_EQ("", model.text()); EXPECT_EQ(0U, model.GetCursorPosition()); EXPECT_FALSE(model.Undo()); + + // Redo until the last edit. EXPECT_TRUE(model.Redo()); EXPECT_STR_EQ("www.google.com", model.text()); EXPECT_EQ(1U, model.GetCursorPosition()); @@ -1365,27 +1617,29 @@ TEST_F(TextfieldModelTest, UndoRedo_BackspaceThenSetText) { model.InsertChar('w'); EXPECT_STR_EQ("w", model.text()); EXPECT_EQ(1U, model.GetCursorPosition()); - model.SetText(base::ASCIIToUTF16("www.google.com")); - EXPECT_EQ(14U, model.GetCursorPosition()); + model.SetText(base::ASCIIToUTF16("www.google.com"), 1); EXPECT_STR_EQ("www.google.com", model.text()); - model.SetText(base::ASCIIToUTF16("www.google.com")); // Confirm the text. + EXPECT_EQ(1U, model.GetCursorPosition()); model.MoveCursor(gfx::LINE_BREAK, gfx::CURSOR_RIGHT, gfx::SELECTION_NONE); EXPECT_EQ(14U, model.GetCursorPosition()); EXPECT_TRUE(model.Backspace()); EXPECT_TRUE(model.Backspace()); EXPECT_STR_EQ("www.google.c", model.text()); // Autocomplete sets the text. - model.SetText(base::ASCIIToUTF16("www.google.com/search=www.google.c")); + model.SetText(base::ASCIIToUTF16("www.google.com/search=www.google.c"), 12); EXPECT_STR_EQ("www.google.com/search=www.google.c", model.text()); + EXPECT_EQ(12U, model.GetCursorPosition()); EXPECT_TRUE(model.Undo()); EXPECT_STR_EQ("www.google.c", model.text()); + EXPECT_EQ(12U, model.GetCursorPosition()); EXPECT_TRUE(model.Undo()); EXPECT_STR_EQ("www.google.com", model.text()); + EXPECT_EQ(14U, model.GetCursorPosition()); } TEST_F(TextfieldModelTest, UndoRedo_CutCopyPasteTest) { TextfieldModel model(nullptr); - model.SetText(base::ASCIIToUTF16("ABCDE")); + model.SetText(base::ASCIIToUTF16("ABCDE"), 5); EXPECT_FALSE(model.Redo()); // There is nothing to redo. // Test Cut. model.SelectRange(gfx::Range(1, 3)); // A[BC]DE @@ -1478,7 +1732,7 @@ TEST_F(TextfieldModelTest, UndoRedo_CutCopyPasteTest) { gfx::Range(1, 3))); // Test Copy. ResetModel(&model); - model.SetText(base::ASCIIToUTF16("12345")); // 12345| + model.SetText(base::ASCIIToUTF16("12345"), 5); // 12345| EXPECT_STR_EQ("12345", model.text()); EXPECT_EQ(5U, model.GetCursorPosition()); model.SelectRange(gfx::Range(1, 3)); // 1[23]45 @@ -1553,7 +1807,7 @@ TEST_F(TextfieldModelTest, UndoRedo_CursorTest) { TEST_F(TextfieldModelTest, Undo_SelectionTest) { gfx::Range range = gfx::Range(2, 4); TextfieldModel model(nullptr); - model.SetText(base::ASCIIToUTF16("abcdef")); + model.SetText(base::ASCIIToUTF16("abcdef"), 0); model.SelectRange(range); EXPECT_EQ(model.render_text()->selection(), range); @@ -1585,7 +1839,7 @@ TEST_F(TextfieldModelTest, Undo_SelectionTest) { model.MoveCursorTo(model.text().length()); EXPECT_TRUE(model.Backspace()); model.SelectRange(gfx::Range(1, 3)); - model.SetText(base::ASCIIToUTF16("[set]")); + model.SetText(base::ASCIIToUTF16("[set]"), 0); EXPECT_TRUE(model.Undo()); EXPECT_STR_EQ("abcde", model.text()); EXPECT_EQ(model.render_text()->selection(), gfx::Range(1, 3)); @@ -1642,28 +1896,28 @@ TEST_F(TextfieldModelTest, UndoRedo_ReplaceTest) { { SCOPED_TRACE("Select forwards and insert."); TextfieldModel model(nullptr); - model.SetText(base::ASCIIToUTF16("abcd")); + model.SetText(base::ASCIIToUTF16("abcd"), 4); model.SelectRange(gfx::Range(1, 3)); RunInsertReplaceTest(&model); } { SCOPED_TRACE("Select reversed and insert."); TextfieldModel model(nullptr); - model.SetText(base::ASCIIToUTF16("abcd")); + model.SetText(base::ASCIIToUTF16("abcd"), 4); model.SelectRange(gfx::Range(3, 1)); RunInsertReplaceTest(&model); } { SCOPED_TRACE("Select forwards and overwrite."); TextfieldModel model(nullptr); - model.SetText(base::ASCIIToUTF16("abcd")); + model.SetText(base::ASCIIToUTF16("abcd"), 4); model.SelectRange(gfx::Range(1, 3)); RunOverwriteReplaceTest(&model); } { SCOPED_TRACE("Select reversed and overwrite."); TextfieldModel model(nullptr); - model.SetText(base::ASCIIToUTF16("abcd")); + model.SetText(base::ASCIIToUTF16("abcd"), 4); model.SelectRange(gfx::Range(3, 1)); RunOverwriteReplaceTest(&model); } @@ -1679,7 +1933,7 @@ TEST_F(TextfieldModelTest, UndoRedo_CompositionText) { ui::ImeTextSpan::Thickness::kThin)); composition.selection = gfx::Range(2, 3); - model.SetText(base::ASCIIToUTF16("ABCDE")); + model.SetText(base::ASCIIToUTF16("ABCDE"), 0); model.MoveCursor(gfx::LINE_BREAK, gfx::CURSOR_RIGHT, gfx::SELECTION_NONE); model.InsertChar('x'); EXPECT_STR_EQ("ABCDEx", model.text()); @@ -1718,11 +1972,11 @@ TEST_F(TextfieldModelTest, UndoRedo_CompositionText) { // Call SetText with the same text as the result. ResetModel(&model); - model.SetText(base::ASCIIToUTF16("ABCDE")); + model.SetText(base::ASCIIToUTF16("ABCDE"), 0); model.MoveCursor(gfx::LINE_BREAK, gfx::CURSOR_RIGHT, gfx::SELECTION_NONE); model.SetCompositionText(composition); EXPECT_STR_EQ("ABCDEabc", model.text()); - model.SetText(base::ASCIIToUTF16("ABCDEabc")); + model.SetText(base::ASCIIToUTF16("ABCDEabc"), 0); EXPECT_STR_EQ("ABCDEabc", model.text()); EXPECT_TRUE(model.Undo()); EXPECT_STR_EQ("ABCDE", model.text()); @@ -1732,11 +1986,11 @@ TEST_F(TextfieldModelTest, UndoRedo_CompositionText) { // Call SetText with a different result; the composition should be forgotten. ResetModel(&model); - model.SetText(base::ASCIIToUTF16("ABCDE")); + model.SetText(base::ASCIIToUTF16("ABCDE"), 0); model.MoveCursor(gfx::LINE_BREAK, gfx::CURSOR_RIGHT, gfx::SELECTION_NONE); model.SetCompositionText(composition); EXPECT_STR_EQ("ABCDEabc", model.text()); - model.SetText(base::ASCIIToUTF16("1234")); + model.SetText(base::ASCIIToUTF16("1234"), 0); EXPECT_STR_EQ("1234", model.text()); EXPECT_TRUE(model.Undo()); EXPECT_STR_EQ("ABCDE", model.text()); @@ -1747,6 +2001,298 @@ TEST_F(TextfieldModelTest, UndoRedo_CompositionText) { // TODO(oshima): Test the behavior with an IME. } +TEST_F(TextfieldModelTest, UndoRedo_TypingWithSecondarySelections) { + TextfieldModel model(nullptr); + + // Type 'ab cd' as 'prefix ab xy suffix' and 'prefix ab cd suffix' are + // autocompleted. + // Type 'a', autocomplete [prefix ]a[b xy suffix] + model.InsertChar('a'); + model.SetText(base::ASCIIToUTF16("prefix ab xy suffix"), 8); + model.SelectRange({19, 8}); + model.SelectRange({0, 7}, false); + + // Type 'ab', autocomplete [prefix ]ab[ xy suffix] + model.InsertChar('b'); + model.SetText(base::ASCIIToUTF16("prefix ab xy suffix"), 9); + model.SelectRange({19, 9}); + model.SelectRange({0, 7}, false); + + // Type 'ab ', autocomplete [prefix ]ab [xy suffix] + model.InsertChar(' '); + model.SetText(base::ASCIIToUTF16("prefix ab xy suffix"), 10); + model.SelectRange({19, 10}); + model.SelectRange({0, 7}, false); + + // Type 'ab c', autocomplete changed to [prefix ]ab c[d suffix] + model.InsertChar('c'); + model.SetText(base::ASCIIToUTF16("prefix ab cd suffix"), 11); + model.SelectRange({19, 11}); + model.SelectRange({0, 7}, false); + + // Type 'ab cd', autocomplete [prefix ]ab cd[ suffix] + model.InsertChar('d'); + model.SetText(base::ASCIIToUTF16("prefix ab cd suffix"), 12); + model.SelectRange({19, 12}); + model.SelectRange({0, 7}, false); + + // Undo 3 times + EXPECT_TRUE(model.Undo()); // [prefix ]ab c[d suffix] + EXPECT_STR_EQ("prefix ab cd suffix", model.text()); + EXPECT_EQ(11U, model.GetCursorPosition()); + VerifyAllSelectionTexts(&model, {"d suffix", "prefix "}); + + EXPECT_TRUE(model.Undo()); // [prefix ]ab [xy suffix] + EXPECT_STR_EQ("prefix ab xy suffix", model.text()); + EXPECT_EQ(10U, model.GetCursorPosition()); + VerifyAllSelectionTexts(&model, {"xy suffix", "prefix "}); + + EXPECT_TRUE(model.Undo()); // [prefix ]ab[ xy suffix] + EXPECT_STR_EQ("prefix ab xy suffix", model.text()); + EXPECT_EQ(9U, model.GetCursorPosition()); + VerifyAllSelectionTexts(&model, {" xy suffix", "prefix "}); + + // Redo 3 times + EXPECT_TRUE(model.Redo()); // [prefix ]ab [xy suffix] + EXPECT_STR_EQ("prefix ab xy suffix", model.text()); + EXPECT_EQ(10U, model.GetCursorPosition()); + EXPECT_FALSE(model.HasSelection()); + + EXPECT_TRUE(model.Redo()); // [prefix ]ab c[d suffix] + EXPECT_STR_EQ("prefix ab cd suffix", model.text()); + EXPECT_EQ(11U, model.GetCursorPosition()); + EXPECT_FALSE(model.HasSelection()); + + EXPECT_TRUE(model.Redo()); // [prefix ]ab cd[ suffix] + EXPECT_STR_EQ("prefix ab cd suffix", model.text()); + EXPECT_EQ(12U, model.GetCursorPosition()); + EXPECT_FALSE(model.HasSelection()); +} + +TEST_F(TextfieldModelTest, UndoRedo_MergingEditsWithSecondarySelections) { + TextfieldModel model(nullptr); + + // Test all possible merge combinations involving secondary selections. + // I.e. an initial [replace or delete] edit with secondary selections, + // followed by a second and third [insert, replace, or delete] edits, which + // are [continuous and discontinuous] respectively. Some cases of the third, + // discontinuous edit have been omitted when the the second edit would not + // been merged anyways. + + // Note, the cursor and selections depend on whether we're traversing forward + // or backwards through edit history. I.e., `undo(); redo();` can result in a + // different outcome than `redo(); undo();`. In general, if our edit history + // consists of 3 edits: A->B, C->D, & E->F, then undo will traverse + // F->E->C->A, while redo will traverse A->B->D->F. Though, B & C and D & E + // will have the same text, they can have different cursors and selections. + + // Replacement with secondary selections followed by insertions + model.SetText(base::ASCIIToUTF16("prefix infix suffix"), 13); + model.SelectRange({18, 13}); + model.SelectRange({1, 6}, false); // p[refix] infix [suffi]x + // Replace + model.InsertChar('1'); // p infix 1|x + // Continuous insert (should merge) + model.InsertChar('3'); // p infix 13|x + // Discontinuous insert (should not merge) + model.SelectRange({9, 9}); // p infix 1|3x + model.InsertChar('2'); // p infix 12|3x + EXPECT_STR_EQ("p infix 123x", model.text()); + EXPECT_FALSE(model.HasSelection()); + // Edit history should be + // p[refix] infix [suffi]x -> p infix 13|x + // p infix 1|3x -> p infix 12|3x + // Undo 2 times + EXPECT_TRUE(model.Undo()); // p infix 1|3x + EXPECT_STR_EQ("p infix 13x", model.text()); + EXPECT_EQ(9U, model.GetCursorPosition()); + EXPECT_FALSE(model.HasSelection()); + EXPECT_TRUE(model.Undo()); // p[refix] infix [suffi]x + EXPECT_STR_EQ("prefix infix suffix", model.text()); + EXPECT_EQ(13U, model.GetCursorPosition()); + VerifyAllSelectionTexts(&model, {"suffi", "refix"}); + // Redo 2 times + EXPECT_TRUE(model.Redo()); // p infix 13|x + EXPECT_STR_EQ("p infix 13x", model.text()); + EXPECT_EQ(10U, model.GetCursorPosition()); + EXPECT_FALSE(model.HasSelection()); + EXPECT_TRUE(model.Redo()); // p infix 12|3x + EXPECT_STR_EQ("p infix 123x", model.text()); + EXPECT_EQ(10U, model.GetCursorPosition()); + EXPECT_FALSE(model.HasSelection()); + EXPECT_FALSE(model.Redo()); + + // Replacement with secondary selections followed by replacements + model.SetText(base::ASCIIToUTF16("prefix infix suffix"), 13); + model.SelectRange({15, 13}); + model.SelectRange({1, 6}, false); // p[refix] infix [su]ffix + // Replace + model.InsertChar('1'); // p infix 1|ffix + // Continuous multiple characters, and backwards replace (should merge) + model.SelectRange({11, 9}); // p infix 1[ff]ix + model.InsertChar('3'); // p infix 13|ix + // Discontinuous but adjacent replace (should not merge) + model.SelectRange({10, 9}); // p infix 1[3]ix + model.InsertChar('2'); // p infix 12|ix + EXPECT_STR_EQ("p infix 12ix", model.text()); + EXPECT_FALSE(model.HasSelection()); + // Edit history should be + // p[refix] infix [su]ffix -> p infix 13|ix + // p infix 1[3]ix -> p infix 12|ix + // Undo 2 times + EXPECT_TRUE(model.Undo()); // p infix 1[3]ix + EXPECT_STR_EQ("p infix 13ix", model.text()); + EXPECT_EQ(9U, model.GetCursorPosition()); + VerifyAllSelectionTexts(&model, {"3"}); + EXPECT_TRUE(model.Undo()); // p[refix] infix [su]ffix + EXPECT_STR_EQ("prefix infix suffix", model.text()); + EXPECT_EQ(13U, model.GetCursorPosition()); + VerifyAllSelectionTexts(&model, {"su", "refix"}); + // Redo 2 times + EXPECT_TRUE(model.Redo()); // p infix 13|ix + EXPECT_STR_EQ("p infix 13ix", model.text()); + EXPECT_EQ(10U, model.GetCursorPosition()); + EXPECT_FALSE(model.HasSelection()); + EXPECT_TRUE(model.Redo()); // p infix 12|ix + EXPECT_STR_EQ("p infix 12ix", model.text()); + EXPECT_EQ(10U, model.GetCursorPosition()); + EXPECT_FALSE(model.HasSelection()); + EXPECT_FALSE(model.Redo()); + + // Replacement with secondary selections followed by deletion + model.SetText(base::ASCIIToUTF16("prefix infix suffix"), 13); + model.SelectRange({15, 13}); + model.SelectRange({1, 6}, false); // p[refix] infix [su]ffix + // Replace + model.InsertChar('1'); // p infix 1|ffix + // Continuous delete (should not merge) + model.Delete(false); // p infix 1|fix + EXPECT_STR_EQ("p infix 1fix", model.text()); + EXPECT_FALSE(model.HasSelection()); + // Edit history should be + // p[refix] infix [su]ffix -> p infix 1|ffix + // p infix 1|ffix -> p infix 1|fix + // Undo 2 times + EXPECT_TRUE(model.Undo()); // p infix 1|ffix + EXPECT_STR_EQ("p infix 1ffix", model.text()); + EXPECT_EQ(9U, model.GetCursorPosition()); + EXPECT_FALSE(model.HasSelection()); + EXPECT_TRUE(model.Undo()); // p[refix] infix [su]ffix + EXPECT_STR_EQ("prefix infix suffix", model.text()); + EXPECT_EQ(13U, model.GetCursorPosition()); + VerifyAllSelectionTexts(&model, {"su", "refix"}); + // Redo 2 times + EXPECT_TRUE(model.Redo()); // p infix 1|ffix + EXPECT_STR_EQ("p infix 1ffix", model.text()); + EXPECT_EQ(9U, model.GetCursorPosition()); + EXPECT_FALSE(model.HasSelection()); + EXPECT_TRUE(model.Redo()); // p infix 1|fix + EXPECT_STR_EQ("p infix 1fix", model.text()); + EXPECT_EQ(9U, model.GetCursorPosition()); + EXPECT_FALSE(model.HasSelection()); + EXPECT_FALSE(model.Redo()); + + // Deletion with secondary selections followed by insertion + model.SetText(base::ASCIIToUTF16("prefix infix suffix"), 13); + model.SelectRange({15, 13}); + model.SelectRange({1, 6}, false); // p[refix] infix [su]ffix + // Delete + model.Delete(false); // p infix |ffix + // Continuous insert (should not merge) + model.InsertChar('1'); // p infix 1|ffix + EXPECT_STR_EQ("p infix 1ffix", model.text()); + EXPECT_FALSE(model.HasSelection()); + // Edit history should be + // p[refix] infix [su]ffix -> p infix |ffix + // p infix |ffix -> p infix 1|ffix + // Undo 2 times + EXPECT_TRUE(model.Undo()); // p infix |ffix + EXPECT_STR_EQ("p infix ffix", model.text()); + EXPECT_EQ(8U, model.GetCursorPosition()); + EXPECT_FALSE(model.HasSelection()); + EXPECT_TRUE(model.Undo()); // p[refix] infix [su]ffix + EXPECT_STR_EQ("prefix infix suffix", model.text()); + EXPECT_EQ(13U, model.GetCursorPosition()); + VerifyAllSelectionTexts(&model, {"su", "refix"}); + // Redo 2 times + EXPECT_TRUE(model.Redo()); // p infix |ffix + EXPECT_STR_EQ("p infix ffix", model.text()); + EXPECT_EQ(8U, model.GetCursorPosition()); + EXPECT_FALSE(model.HasSelection()); + EXPECT_TRUE(model.Redo()); // p infix 1|ffix + EXPECT_STR_EQ("p infix 1ffix", model.text()); + EXPECT_EQ(9U, model.GetCursorPosition()); + EXPECT_FALSE(model.HasSelection()); + EXPECT_FALSE(model.Redo()); + + // Deletion with secondary selections followed by replacement + model.SetText(base::ASCIIToUTF16("prefix infix suffix"), 13); + model.SelectRange({15, 13}); + model.SelectRange({1, 6}, false); // p[refix] infix [su]ffix + // Delete + model.Delete(false); // p infix |ffix + // Continuous replacement (should not merge) + model.SelectRange({8, 9}); // p infix [f]fix + model.InsertChar('1'); // p infix 1|fix + EXPECT_STR_EQ("p infix 1fix", model.text()); + EXPECT_FALSE(model.HasSelection()); + // Edit history should be + // p[refix] infix [su]ffix -> p infix |ffix + // p infix [f]fix -> p infix 1|fix + // Undo 2 times + EXPECT_TRUE(model.Undo()); // p infix [f]fix + EXPECT_STR_EQ("p infix ffix", model.text()); + EXPECT_EQ(9U, model.GetCursorPosition()); + VerifyAllSelectionTexts(&model, {"f"}); + EXPECT_TRUE(model.Undo()); // p[refix] infix [su]ffix + EXPECT_STR_EQ("prefix infix suffix", model.text()); + EXPECT_EQ(13U, model.GetCursorPosition()); + VerifyAllSelectionTexts(&model, {"su", "refix"}); + // Redo 2 times + EXPECT_TRUE(model.Redo()); // p infix |ffix + EXPECT_STR_EQ("p infix ffix", model.text()); + EXPECT_EQ(8U, model.GetCursorPosition()); + EXPECT_FALSE(model.HasSelection()); + EXPECT_TRUE(model.Redo()); // p infix 1|fix + EXPECT_STR_EQ("p infix 1fix", model.text()); + EXPECT_EQ(9U, model.GetCursorPosition()); + EXPECT_FALSE(model.HasSelection()); + EXPECT_FALSE(model.Redo()); + + // Deletion with secondary selections followed by deletion + model.SetText(base::ASCIIToUTF16("prefix infix suffix"), 13); + model.SelectRange({15, 13}); + model.SelectRange({1, 6}, false); // p[refix] infix [su]ffix + // Delete + model.Delete(false); // p infix |ffix + // Continuous delete (should not merge) + model.Delete(false); // p infix |fix + EXPECT_STR_EQ("p infix fix", model.text()); + EXPECT_FALSE(model.HasSelection()); + // Edit history should be + // p[refix] infix [su]ffix -> p infix |ffix + // p infix |ffix -> p infix |fix + // Undo 2 times + EXPECT_TRUE(model.Undo()); // p infix |ffix + EXPECT_STR_EQ("p infix ffix", model.text()); + EXPECT_EQ(8U, model.GetCursorPosition()); + EXPECT_FALSE(model.HasSelection()); + EXPECT_TRUE(model.Undo()); // p[refix] infix [su]ffix + EXPECT_STR_EQ("prefix infix suffix", model.text()); + EXPECT_EQ(13U, model.GetCursorPosition()); + VerifyAllSelectionTexts(&model, {"su", "refix"}); + // Redo 2 times + EXPECT_TRUE(model.Redo()); // p infix |ffix + EXPECT_STR_EQ("p infix ffix", model.text()); + EXPECT_EQ(8U, model.GetCursorPosition()); + EXPECT_FALSE(model.HasSelection()); + EXPECT_TRUE(model.Redo()); // p infix |fix + EXPECT_STR_EQ("p infix fix", model.text()); + EXPECT_EQ(8U, model.GetCursorPosition()); + EXPECT_FALSE(model.HasSelection()); + EXPECT_FALSE(model.Redo()); +} + // Tests that clipboard text with leading, trailing and interspersed tabs // spaces etc is pasted correctly. Leading and trailing tabs should be // stripped. Text separated by multiple tabs/spaces should be left alone. @@ -1902,7 +2448,7 @@ TEST_F(TextfieldModelTest, Transpose) { const TestCase& test_case = all_tests[i][j]; - model.SetText(test_strings[i]); + model.SetText(test_strings[i], 0); model.SelectRange(test_case.range); EXPECT_EQ(test_case.range, model.render_text()->selection()); model.Transpose(); @@ -1915,32 +2461,49 @@ TEST_F(TextfieldModelTest, Transpose) { TEST_F(TextfieldModelTest, Yank) { TextfieldModel model(nullptr); - model.SetText(base::ASCIIToUTF16("abcde")); + model.SetText(base::ASCIIToUTF16("abcdefgh"), 0); model.SelectRange(gfx::Range(1, 3)); // Delete selection but don't add to kill buffer. model.Delete(false); - EXPECT_STR_EQ("ade", model.text()); + EXPECT_STR_EQ("adefgh", model.text()); // Since the kill buffer is empty, yank should cause no change. - model.Yank(); - EXPECT_STR_EQ("ade", model.text()); + EXPECT_FALSE(model.Yank()); + EXPECT_STR_EQ("adefgh", model.text()); + + // With a nonempty selection and an empty kill buffer, yank should delete the + // selection. + model.SelectRange(gfx::Range(4, 5)); + EXPECT_TRUE(model.Yank()); + EXPECT_STR_EQ("adefh", model.text()); + + // With multiple selections and an empty kill buffer, yank should delete the + // selections. + model.SelectRange(gfx::Range(2, 3)); + model.SelectRange(gfx::Range(4, 5), false); + EXPECT_TRUE(model.Yank()); + EXPECT_STR_EQ("adf", model.text()); + + // The kill buffer should remain empty after yanking without a kill buffer. + EXPECT_FALSE(model.Yank()); + EXPECT_STR_EQ("adf", model.text()); // Delete selection and add to kill buffer. model.SelectRange(gfx::Range(0, 1)); model.Delete(true); - EXPECT_STR_EQ("de", model.text()); + EXPECT_STR_EQ("df", model.text()); // Yank twice. - model.Yank(); - model.Yank(); - EXPECT_STR_EQ("aade", model.text()); + EXPECT_TRUE(model.Yank()); + EXPECT_TRUE(model.Yank()); + EXPECT_STR_EQ("aadf", model.text()); // Ensure an empty deletion does not modify the kill buffer. model.SelectRange(gfx::Range(4)); model.Delete(true); - model.Yank(); - EXPECT_STR_EQ("aadea", model.text()); + EXPECT_TRUE(model.Yank()); + EXPECT_STR_EQ("aadfa", model.text()); // Backspace twice but don't add to kill buffer. model.Backspace(false); @@ -1948,7 +2511,7 @@ TEST_F(TextfieldModelTest, Yank) { EXPECT_STR_EQ("aad", model.text()); // Ensure kill buffer is not modified. - model.Yank(); + EXPECT_TRUE(model.Yank()); EXPECT_STR_EQ("aada", model.text()); // Backspace twice, each time modifying the kill buffer. @@ -1957,13 +2520,13 @@ TEST_F(TextfieldModelTest, Yank) { EXPECT_STR_EQ("aa", model.text()); // Ensure yanking inserts the modified kill buffer text. - model.Yank(); + EXPECT_TRUE(model.Yank()); EXPECT_STR_EQ("aad", model.text()); } TEST_F(TextfieldModelTest, SetCompositionFromExistingText) { TextfieldModel model(nullptr); - model.SetText(base::ASCIIToUTF16("abcde")); + model.SetText(base::ASCIIToUTF16("abcde"), 0); model.SetCompositionFromExistingText(gfx::Range(0, 1)); EXPECT_TRUE(model.HasCompositionText()); @@ -1979,7 +2542,7 @@ TEST_F(TextfieldModelTest, SetCompositionFromExistingText) { TEST_F(TextfieldModelTest, SetCompositionFromExistingText_Empty) { TextfieldModel model(nullptr); - model.SetText(base::ASCIIToUTF16("abc")); + model.SetText(base::ASCIIToUTF16("abc"), 0); model.SetCompositionFromExistingText(gfx::Range(0, 2)); EXPECT_TRUE(model.HasCompositionText()); @@ -1991,12 +2554,12 @@ TEST_F(TextfieldModelTest, SetCompositionFromExistingText_Empty) { TEST_F(TextfieldModelTest, SetCompositionFromExistingText_OutOfBounds) { TextfieldModel model(nullptr); - model.SetText(base::string16()); + model.SetText(base::string16(), 0); model.SetCompositionFromExistingText(gfx::Range(0, 2)); EXPECT_FALSE(model.HasCompositionText()); - model.SetText(base::ASCIIToUTF16("abc")); + model.SetText(base::ASCIIToUTF16("abc"), 0); model.SetCompositionFromExistingText(gfx::Range(1, 4)); EXPECT_FALSE(model.HasCompositionText()); } diff --git a/chromium/ui/views/controls/textfield/textfield_test_api.cc b/chromium/ui/views/controls/textfield/textfield_test_api.cc index 3f756dfaad7..099f22e9bcf 100644 --- a/chromium/ui/views/controls/textfield/textfield_test_api.cc +++ b/chromium/ui/views/controls/textfield/textfield_test_api.cc @@ -31,7 +31,7 @@ void TextfieldTestApi::ResetTouchSelectionController() { } void TextfieldTestApi::SetCursorViewRect(gfx::Rect bounds) { - textfield_->cursor_view_.SetBoundsRect(bounds); + textfield_->cursor_view_->SetBoundsRect(bounds); } bool TextfieldTestApi::IsTextDirectionCheckedInContextMenu( @@ -40,4 +40,8 @@ bool TextfieldTestApi::IsTextDirectionCheckedInContextMenu( textfield_->text_services_context_menu_.get(), direction); } +bool TextfieldTestApi::ShouldShowCursor() const { + return textfield_->ShouldShowCursor(); +} + } // namespace views diff --git a/chromium/ui/views/controls/textfield/textfield_test_api.h b/chromium/ui/views/controls/textfield/textfield_test_api.h index 6f0496c5771..3f346da6059 100644 --- a/chromium/ui/views/controls/textfield/textfield_test_api.h +++ b/chromium/ui/views/controls/textfield/textfield_test_api.h @@ -46,14 +46,18 @@ class TextfieldTestApi { return textfield_->cursor_blink_timer_.IsRunning(); } - gfx::Rect GetCursorViewRect() { return textfield_->cursor_view_.bounds(); } + gfx::Rect GetCursorViewRect() { return textfield_->cursor_view_->bounds(); } void SetCursorViewRect(gfx::Rect bounds); - bool IsCursorVisible() const { return textfield_->cursor_view_.GetVisible(); } + bool IsCursorVisible() const { + return textfield_->cursor_view_->GetVisible(); + } bool IsTextDirectionCheckedInContextMenu( base::i18n::TextDirection direction) const; + bool ShouldShowCursor() const; + private: Textfield* textfield_; diff --git a/chromium/ui/views/controls/textfield/textfield_unittest.cc b/chromium/ui/views/controls/textfield/textfield_unittest.cc index 4a7a614b9d9..456cecaba14 100644 --- a/chromium/ui/views/controls/textfield/textfield_unittest.cc +++ b/chromium/ui/views/controls/textfield/textfield_unittest.cc @@ -13,6 +13,7 @@ #include <vector> #include "base/command_line.h" +#include "base/feature_list.h" #include "base/format_macros.h" #include "base/i18n/rtl.h" #include "base/pickle.h" @@ -53,6 +54,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/views_features.h" #include "ui/views/widget/widget.h" #include "ui/views/widget/widget_utils.h" #include "url/gurl.h" @@ -779,6 +781,19 @@ class TextfieldTest : public ViewsTestBase, public TextfieldController { // an event when it updates the cursor position. void MoveMouseTo(const gfx::Point& where) { mouse_position_ = where; } + // Tap on the textfield. + void TapAtCursor(ui::EventPointerType pointer_type) { + ui::GestureEventDetails tap_down_details(ui::ET_GESTURE_TAP_DOWN); + tap_down_details.set_primary_pointer_type(pointer_type); + GestureEventForTest tap_down(GetCursorPositionX(0), 0, tap_down_details); + textfield_->OnGestureEvent(&tap_down); + + ui::GestureEventDetails tap_up_details(ui::ET_GESTURE_TAP); + tap_up_details.set_primary_pointer_type(pointer_type); + GestureEventForTest tap_up(GetCursorPositionX(0), 0, tap_up_details); + textfield_->OnGestureEvent(&tap_up); + } + // We need widget to populate wrapper class. Widget* widget_ = nullptr; @@ -1155,6 +1170,19 @@ TEST_F(TextfieldTest, MoveParagraphForwardBackwardAndModifySelection) { #endif } +TEST_F(TextfieldTest, ModifySelectionWithMultipleSelections) { + InitTextfield(); + textfield_->SetText(ASCIIToUTF16("0123456 89")); + textfield_->SetSelectedRange(gfx::Range(3, 5)); + textfield_->SetSelectedRange(gfx::Range(8, 9), false); + + test_api_->ExecuteTextEditCommand( + ui::TextEditCommand::MOVE_RIGHT_AND_MODIFY_SELECTION); + EXPECT_EQ(gfx::Range(3, 6), textfield_->GetSelectedRange()); + EXPECT_EQ(6U, textfield_->GetCursorPosition()); + EXPECT_EQ(0U, textfield_->GetSelectionModel().secondary_selections().size()); +} + TEST_F(TextfieldTest, InsertionDeletionTest) { // Insert a test string in a textfield. InitTextfield(); @@ -1245,6 +1273,36 @@ TEST_F(TextfieldTest, DeletionWithSelection) { } } +// Test that deletion operations behave correctly with multiple selections. +TEST_F(TextfieldTest, DeletionWithMultipleSelections) { + struct { + ui::KeyboardCode key; + bool shift; + } cases[] = { + {ui::VKEY_BACK, false}, + {ui::VKEY_BACK, true}, + {ui::VKEY_DELETE, false}, + {ui::VKEY_DELETE, true}, + }; + + InitTextfield(); + // [Ctrl] ([Alt] on Mac) + [Delete]/[Backspace] should delete the active + // selection, regardless of [Shift]. + for (size_t i = 0; i < base::size(cases); ++i) { + SCOPED_TRACE(base::StringPrintf("Testing cases[%" PRIuS "]", i)); + textfield_->SetText(ASCIIToUTF16("one two three")); + // Select: o[ne] [two] th[re]e + textfield_->SetSelectedRange(gfx::Range(4, 7)); + textfield_->SetSelectedRange(gfx::Range(10, 12), false); + textfield_->SetSelectedRange(gfx::Range(1, 3), false); + SendWordEvent(cases[i].key, cases[i].shift); + EXPECT_STR_EQ("o the", textfield_->GetText()); + EXPECT_EQ(gfx::Range(2), textfield_->GetSelectedRange()); + EXPECT_EQ(0U, + textfield_->GetSelectionModel().secondary_selections().size()); + } +} + // Test deletions not covered by other tests with key events. TEST_F(TextfieldTest, DeletionWithEditCommands) { struct { @@ -1283,11 +1341,11 @@ TEST_F(TextfieldTest, PasswordTest) { SetClipboardText(ui::ClipboardBuffer::kCopyPaste, "foo"); // Cut and copy should be disabled. - EXPECT_FALSE(textfield_->IsCommandIdEnabled(IDS_APP_CUT)); - textfield_->ExecuteCommand(IDS_APP_CUT, 0); + EXPECT_FALSE(textfield_->IsCommandIdEnabled(Textfield::kCut)); + textfield_->ExecuteCommand(Textfield::kCut, 0); SendKeyEvent(ui::VKEY_X, false, true); - EXPECT_FALSE(textfield_->IsCommandIdEnabled(IDS_APP_COPY)); - textfield_->ExecuteCommand(IDS_APP_COPY, 0); + EXPECT_FALSE(textfield_->IsCommandIdEnabled(Textfield::kCopy)); + textfield_->ExecuteCommand(Textfield::kCopy, 0); SendKeyEvent(ui::VKEY_C, false, true); SendAlternateCopy(); EXPECT_STR_EQ("foo", GetClipboardText(ui::ClipboardBuffer::kCopyPaste)); @@ -1297,8 +1355,8 @@ TEST_F(TextfieldTest, PasswordTest) { SendKeyEvent(ui::VKEY_DELETE, true, false); // Paste should work normally. - EXPECT_TRUE(textfield_->IsCommandIdEnabled(IDS_APP_PASTE)); - textfield_->ExecuteCommand(IDS_APP_PASTE, 0); + EXPECT_TRUE(textfield_->IsCommandIdEnabled(Textfield::kPaste)); + textfield_->ExecuteCommand(Textfield::kPaste, 0); SendKeyEvent(ui::VKEY_V, false, true); SendAlternatePaste(); EXPECT_STR_EQ("foo", GetClipboardText(ui::ClipboardBuffer::kCopyPaste)); @@ -1517,6 +1575,42 @@ TEST_F(TextfieldTest, CursorMovement) { EXPECT_STR_EQ("one two", last_contents_); } +TEST_F(TextfieldTest, CursorMovementWithMultipleSelections) { + InitTextfield(); + textfield_->SetText(ASCIIToUTF16("012 456 890 234 678")); + // [p] [s] + textfield_->SetSelectedRange({4, 7}); + textfield_->SetSelectedRange({12, 15}, false); + + test_api_->ExecuteTextEditCommand(ui::TextEditCommand::MOVE_LEFT); + EXPECT_EQ(gfx::Range(4, 4), textfield_->GetSelectedRange()); + EXPECT_EQ(0U, textfield_->GetSelectionModel().secondary_selections().size()); + + textfield_->SetSelectedRange({4, 7}); + textfield_->SetSelectedRange({12, 15}, false); + + test_api_->ExecuteTextEditCommand(ui::TextEditCommand::MOVE_RIGHT); + EXPECT_EQ(gfx::Range(7, 7), textfield_->GetSelectedRange()); + EXPECT_EQ(0U, textfield_->GetSelectionModel().secondary_selections().size()); +} + +TEST_F(TextfieldTest, ShouldShowCursor) { + InitTextfield(); + textfield_->SetText(ASCIIToUTF16("word1 word2")); + + // should show cursor when there's no primary selection + textfield_->SetSelectedRange({4, 4}); + EXPECT_TRUE(test_api_->ShouldShowCursor()); + textfield_->SetSelectedRange({1, 3}, false); + EXPECT_TRUE(test_api_->ShouldShowCursor()); + + // should not show cursor when there's a primary selection + textfield_->SetSelectedRange({4, 7}); + EXPECT_FALSE(test_api_->ShouldShowCursor()); + textfield_->SetSelectedRange({1, 3}, false); + EXPECT_FALSE(test_api_->ShouldShowCursor()); +} + TEST_F(TextfieldTest, FocusTraversalTest) { InitTextfields(3); textfield_->RequestFocus(); @@ -1760,7 +1854,7 @@ TEST_F(TextfieldTest, DragAndDrop_AcceptDrop) { bad_data.SetPickledData(fmt, base::Pickle()); bad_data.SetFileContents(base::FilePath(L"x"), "x"); bad_data.SetHtml(base::string16(ASCIIToUTF16("x")), GURL("x.org")); - ui::OSExchangeData::DownloadFileInfo download(base::FilePath(), nullptr); + ui::DownloadFileInfo download(base::FilePath(), nullptr); bad_data.SetDownloadFileInfo(&download); EXPECT_FALSE(textfield_->CanDrop(bad_data)); } @@ -1964,24 +2058,24 @@ TEST_F(TextfieldTest, ReadOnlyTest) { // Cut should be disabled. SetClipboardText(ui::ClipboardBuffer::kCopyPaste, "Test"); - EXPECT_FALSE(textfield_->IsCommandIdEnabled(IDS_APP_CUT)); - textfield_->ExecuteCommand(IDS_APP_CUT, 0); + EXPECT_FALSE(textfield_->IsCommandIdEnabled(Textfield::kCut)); + textfield_->ExecuteCommand(Textfield::kCut, 0); SendKeyEvent(ui::VKEY_X, false, true); SendAlternateCut(); EXPECT_STR_EQ("Test", GetClipboardText(ui::ClipboardBuffer::kCopyPaste)); EXPECT_STR_EQ("read only", textfield_->GetText()); // Paste should be disabled. - EXPECT_FALSE(textfield_->IsCommandIdEnabled(IDS_APP_PASTE)); - textfield_->ExecuteCommand(IDS_APP_PASTE, 0); + EXPECT_FALSE(textfield_->IsCommandIdEnabled(Textfield::kPaste)); + textfield_->ExecuteCommand(Textfield::kPaste, 0); SendKeyEvent(ui::VKEY_V, false, true); SendAlternatePaste(); EXPECT_STR_EQ("read only", textfield_->GetText()); // Copy should work normally. SetClipboardText(ui::ClipboardBuffer::kCopyPaste, "Test"); - EXPECT_TRUE(textfield_->IsCommandIdEnabled(IDS_APP_COPY)); - textfield_->ExecuteCommand(IDS_APP_COPY, 0); + EXPECT_TRUE(textfield_->IsCommandIdEnabled(Textfield::kCopy)); + textfield_->ExecuteCommand(Textfield::kCopy, 0); EXPECT_STR_EQ("read only", GetClipboardText(ui::ClipboardBuffer::kCopyPaste)); SetClipboardText(ui::ClipboardBuffer::kCopyPaste, "Test"); SendKeyEvent(ui::VKEY_C, false, true); @@ -2297,11 +2391,11 @@ TEST_F(TextfieldTest, Yank) { TEST_F(TextfieldTest, CutCopyPaste) { InitTextfield(); - // Ensure IDS_APP_CUT cuts. + // Ensure kCut cuts. textfield_->SetText(ASCIIToUTF16("123")); textfield_->SelectAll(false); - EXPECT_TRUE(textfield_->IsCommandIdEnabled(IDS_APP_CUT)); - textfield_->ExecuteCommand(IDS_APP_CUT, 0); + EXPECT_TRUE(textfield_->IsCommandIdEnabled(Textfield::kCut)); + textfield_->ExecuteCommand(Textfield::kCut, 0); EXPECT_STR_EQ("123", GetClipboardText(ui::ClipboardBuffer::kCopyPaste)); EXPECT_STR_EQ("", textfield_->GetText()); EXPECT_EQ(ui::ClipboardBuffer::kCopyPaste, GetAndResetCopiedToClipboard()); @@ -2337,11 +2431,11 @@ TEST_F(TextfieldTest, CutCopyPaste) { EXPECT_STR_EQ("123", textfield_->GetText()); EXPECT_EQ(ui::ClipboardBuffer::kMaxValue, GetAndResetCopiedToClipboard()); - // Ensure IDS_APP_COPY copies. + // Ensure kCopy copies. textfield_->SetText(ASCIIToUTF16("789")); textfield_->SelectAll(false); - EXPECT_TRUE(textfield_->IsCommandIdEnabled(IDS_APP_COPY)); - textfield_->ExecuteCommand(IDS_APP_COPY, 0); + EXPECT_TRUE(textfield_->IsCommandIdEnabled(Textfield::kCopy)); + textfield_->ExecuteCommand(Textfield::kCopy, 0); EXPECT_STR_EQ("789", GetClipboardText(ui::ClipboardBuffer::kCopyPaste)); EXPECT_EQ(ui::ClipboardBuffer::kCopyPaste, GetAndResetCopiedToClipboard()); @@ -2363,12 +2457,12 @@ TEST_F(TextfieldTest, CutCopyPaste) { EXPECT_STR_EQ("345", textfield_->GetText()); EXPECT_EQ(ui::ClipboardBuffer::kCopyPaste, GetAndResetCopiedToClipboard()); - // Ensure IDS_APP_PASTE, [Ctrl]+[V], and [Shift]+[Insert] pastes; + // Ensure kPaste, [Ctrl]+[V], and [Shift]+[Insert] pastes; // also ensure that [Ctrl]+[Alt]+[V] does nothing. SetClipboardText(ui::ClipboardBuffer::kCopyPaste, "abc"); textfield_->SetText(base::string16()); - EXPECT_TRUE(textfield_->IsCommandIdEnabled(IDS_APP_PASTE)); - textfield_->ExecuteCommand(IDS_APP_PASTE, 0); + EXPECT_TRUE(textfield_->IsCommandIdEnabled(Textfield::kPaste)); + textfield_->ExecuteCommand(Textfield::kPaste, 0); EXPECT_STR_EQ("abc", textfield_->GetText()); SendKeyEvent(ui::VKEY_V, false, true); EXPECT_STR_EQ("abcabc", textfield_->GetText()); @@ -3662,12 +3756,7 @@ TEST_F(TextfieldTest, FocusReasonTouchTap) { 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); - + TapAtCursor(ui::EventPointerType::kTouch); EXPECT_EQ(ui::TextInputClient::FOCUS_REASON_TOUCH, textfield_->GetFocusReason()); } @@ -3678,11 +3767,7 @@ TEST_F(TextfieldTest, FocusReasonPenTap) { 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); - + TapAtCursor(ui::EventPointerType::kPen); EXPECT_EQ(ui::TextInputClient::FOCUS_REASON_PEN, textfield_->GetFocusReason()); } @@ -3693,23 +3778,8 @@ TEST_F(TextfieldTest, FocusReasonMultipleEvents) { 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); - } - + TapAtCursor(ui::EventPointerType::kPen); + TapAtCursor(ui::EventPointerType::kTouch); EXPECT_EQ(ui::TextInputClient::FOCUS_REASON_PEN, textfield_->GetFocusReason()); } @@ -3721,13 +3791,8 @@ TEST_F(TextfieldTest, FocusReasonFocusBlurFocus) { 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); - + TapAtCursor(ui::EventPointerType::kPen); widget_->GetFocusManager()->ClearFocus(); - textfield_->RequestFocus(); EXPECT_EQ(ui::TextInputClient::FOCUS_REASON_OTHER, @@ -3737,11 +3802,7 @@ TEST_F(TextfieldTest, FocusReasonFocusBlurFocus) { TEST_F(TextfieldTest, KeyboardObserverForPenInput) { InitTextfield(); - 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); - + TapAtCursor(ui::EventPointerType::kPen); EXPECT_EQ(1, input_method_->count_show_virtual_keyboard()); } @@ -3796,4 +3857,14 @@ TEST_F(TextfieldTest, TextChangedCallbackTest) { EXPECT_TRUE(text_changed); } +// Tests that invalid characters like non-displayable characters are filtered +// out when inserted into the text field. +TEST_F(TextfieldTest, InsertInvalidCharsTest) { + InitTextfield(); + + textfield_->InsertText(ASCIIToUTF16("\babc\ndef\t")); + + EXPECT_EQ(textfield_->GetText(), ASCIIToUTF16("abcdef")); +} + } // namespace views diff --git a/chromium/ui/views/controls/tree/tree_view.cc b/chromium/ui/views/controls/tree/tree_view.cc index 7d26bdf9f58..bd84a3f38d7 100644 --- a/chromium/ui/views/controls/tree/tree_view.cc +++ b/chromium/ui/views/controls/tree/tree_view.cc @@ -41,7 +41,6 @@ #include "ui/views/controls/scroll_view.h" #include "ui/views/controls/textfield/textfield.h" #include "ui/views/controls/tree/tree_view_controller.h" -#include "ui/views/layout/layout_provider.h" #include "ui/views/style/platform_style.h" #include "ui/views/vector_icons.h" @@ -193,14 +192,7 @@ void TreeView::StartEditing(TreeModelNode* node) { DCHECK(!editing_); editing_ = true; if (!editor_) { - LayoutProvider* provider = LayoutProvider::Get(); - gfx::Insets text_insets( - provider->GetDistanceMetric(DISTANCE_CONTROL_VERTICAL_TEXT_PADDING), - provider->GetDistanceMetric( - DISTANCE_TEXTFIELD_HORIZONTAL_TEXT_PADDING)); editor_ = new Textfield; - editor_->SetBorder(views::CreatePaddedBorder( - views::CreateSolidBorder(1, gfx::kGoogleBlue700), text_insets)); // Add the editor immediately as GetPreferredSize returns the wrong thing if // not parented. AddChildView(editor_); @@ -895,7 +887,7 @@ void TreeView::PopulateAccessibilityData(InternalNode* node, } else { // !IsRoot(node)) && node->parent() != nullptr. - if (node->parent()->is_expanded()) { + if (IsExpanded(node->parent()->model_node())) { int depth = 0; row = GetRowForInternalNode(node, &depth); if (depth >= 0) { @@ -922,7 +914,6 @@ void TreeView::PopulateAccessibilityData(InternalNode* node, data->AddAction(ax::mojom::Action::kFocus); data->AddAction(ax::mojom::Action::kScrollToMakeVisible); gfx::Rect node_bounds = GetBackgroundBoundsForNode(node); - View::ConvertRectToScreen(this, &node_bounds); data->relative_bounds.bounds = gfx::RectF(node_bounds); } else { data->AddState(ax::mojom::State::kInvisible); diff --git a/chromium/ui/views/controls/tree/tree_view.h b/chromium/ui/views/controls/tree/tree_view.h index 89c559a3bfb..b03b860f7b2 100644 --- a/chromium/ui/views/controls/tree/tree_view.h +++ b/chromium/ui/views/controls/tree/tree_view.h @@ -134,7 +134,7 @@ class VIEWS_EXPORT TreeView : public View, // Maps a node to a row, returns -1 if node is not valid. int GetRowForNode(ui::TreeModelNode* node); - views::Textfield* editor() { return editor_; } + Textfield* editor() { return editor_; } // Replaces this TreeView's TreeViewDrawingProvider with |provider|. void SetDrawingProvider(std::unique_ptr<TreeViewDrawingProvider> provider); 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 5e1559e61bc..0cebc532c69 100644 --- a/chromium/ui/views/controls/views_text_services_context_menu.cc +++ b/chromium/ui/views/controls/views_text_services_context_menu.cc @@ -6,7 +6,7 @@ #include <memory> -#include "base/logging.h" +#include "base/notreached.h" #include "ui/views/controls/views_text_services_context_menu_base.h" namespace views { diff --git a/chromium/ui/views/controls/webview/BUILD.gn b/chromium/ui/views/controls/webview/BUILD.gn index b52fdbf769a..66ba75adfcc 100644 --- a/chromium/ui/views/controls/webview/BUILD.gn +++ b/chromium/ui/views/controls/webview/BUILD.gn @@ -59,7 +59,7 @@ jumbo_component("webview") { "//ui/views", ] - if (is_linux || is_android) { + if (is_linux || is_android || is_fuchsia) { sources += [ "unhandled_keyboard_event_handler_default.cc" ] } } diff --git a/chromium/ui/views/controls/webview/unhandled_keyboard_event_handler.cc b/chromium/ui/views/controls/webview/unhandled_keyboard_event_handler.cc index 85a1cc6e07a..cff5956b04a 100644 --- a/chromium/ui/views/controls/webview/unhandled_keyboard_event_handler.cc +++ b/chromium/ui/views/controls/webview/unhandled_keyboard_event_handler.cc @@ -23,7 +23,7 @@ bool UnhandledKeyboardEventHandler::HandleKeyboardEvent( // Previous calls to TranslateMessage can generate Char events as well as // RawKeyDown events, even if the latter triggered an accelerator. In these // cases, we discard the Char events. - if (event.GetType() == blink::WebInputEvent::kChar && + if (event.GetType() == blink::WebInputEvent::Type::kChar && ignore_next_char_event_) { ignore_next_char_event_ = false; return false; @@ -32,7 +32,7 @@ bool UnhandledKeyboardEventHandler::HandleKeyboardEvent( // always generate a Char event. ignore_next_char_event_ = false; - if (event.GetType() == blink::WebInputEvent::kRawKeyDown) { + if (event.GetType() == blink::WebInputEvent::Type::kRawKeyDown) { ui::Accelerator accelerator = ui::GetAcceleratorFromNativeWebKeyboardEvent(event); diff --git a/chromium/ui/views/controls/webview/web_dialog_view.cc b/chromium/ui/views/controls/webview/web_dialog_view.cc index a8dab41a057..f8c7d7670ef 100644 --- a/chromium/ui/views/controls/webview/web_dialog_view.cc +++ b/chromium/ui/views/controls/webview/web_dialog_view.cc @@ -387,13 +387,14 @@ content::WebContents* WebDialogView::OpenURLFromTab( void WebDialogView::AddNewContents( content::WebContents* source, std::unique_ptr<content::WebContents> new_contents, + const GURL& target_url, 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); + WebDialogWebContentsDelegate::AddNewContents( + source, std::move(new_contents), target_url, 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 b2c7fed8463..5b04647c4a3 100644 --- a/chromium/ui/views/controls/webview/web_dialog_view.h +++ b/chromium/ui/views/controls/webview/web_dialog_view.h @@ -140,6 +140,7 @@ class WEBVIEW_EXPORT WebDialogView : public ClientView, const content::OpenURLParams& params) override; void AddNewContents(content::WebContents* source, std::unique_ptr<content::WebContents> new_contents, + const GURL& target_url, WindowOpenDisposition disposition, const gfx::Rect& initial_rect, bool user_gesture, diff --git a/chromium/ui/views/controls/webview/web_dialog_view_unittest.cc b/chromium/ui/views/controls/webview/web_dialog_view_unittest.cc index ceb97226b3d..cb149be77fa 100644 --- a/chromium/ui/views/controls/webview/web_dialog_view_unittest.cc +++ b/chromium/ui/views/controls/webview/web_dialog_view_unittest.cc @@ -14,6 +14,7 @@ #include "content/public/test/browser_task_environment.h" #include "content/public/test/browser_test_utils.h" #include "content/public/test/test_browser_context.h" +#include "content/public/test/test_renderer_host.h" #include "content/test/test_content_browser_client.h" #include "content/test/test_web_contents.h" #include "testing/gtest/include/gtest/gtest.h" @@ -127,6 +128,7 @@ class WebDialogViewUnitTest : public views::test::WidgetTest { } private: + content::RenderViewHostTestEnabler test_render_host_factories_; content::TestContentBrowserClient test_browser_client_; std::unique_ptr<content::TestBrowserContext> browser_context_; // These are raw pointers (vs unique pointers) because the views diff --git a/chromium/ui/views/corewm/DEPS b/chromium/ui/views/corewm/DEPS index 1ce5357a428..059e550da2c 100644 --- a/chromium/ui/views/corewm/DEPS +++ b/chromium/ui/views/corewm/DEPS @@ -23,7 +23,7 @@ specific_include_rules = { "desktop_capture_controller_unittest.cc": [ "+ui/views/test/native_widget_factory.h", - "+ui/views/test/views_interactive_ui_test_base.h", + "+ui/views/test/widget_test.h", "+ui/views/view.h", "+ui/views/widget/desktop_aura/desktop_native_widget_aura.h", "+ui/views/widget/desktop_aura/desktop_screen_position_client.h", diff --git a/chromium/ui/views/corewm/desktop_capture_controller_unittest.cc b/chromium/ui/views/corewm/desktop_capture_controller_unittest.cc index 2e4d5119392..d04c5baca0b 100644 --- a/chromium/ui/views/corewm/desktop_capture_controller_unittest.cc +++ b/chromium/ui/views/corewm/desktop_capture_controller_unittest.cc @@ -4,7 +4,6 @@ #include <memory> -#include "base/logging.h" #include "base/macros.h" #include "ui/aura/env.h" #include "ui/aura/test/test_window_delegate.h" @@ -13,7 +12,7 @@ #include "ui/events/event.h" #include "ui/events/test/event_generator.h" #include "ui/views/test/native_widget_factory.h" -#include "ui/views/test/views_interactive_ui_test_base.h" +#include "ui/views/test/widget_test.h" #include "ui/views/view.h" #include "ui/views/widget/desktop_aura/desktop_native_widget_aura.h" #include "ui/views/widget/desktop_aura/desktop_screen_position_client.h" @@ -26,7 +25,7 @@ namespace views { -using DesktopCaptureControllerTest = ViewsInteractiveUITestBase; +using DesktopCaptureControllerTest = test::DesktopWidgetTestInteractive; // This class provides functionality to verify whether the View instance // received the gesture event. @@ -103,7 +102,7 @@ TEST_F(DesktopCaptureControllerTest, CaptureWindowInputEventTest) { params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; params.bounds = gfx::Rect(50, 50, 650, 650); params.native_widget = test::CreatePlatformNativeWidgetImpl( - params, widget1.get(), test::kStubCapture, nullptr); + widget1.get(), test::kStubCapture, nullptr); widget1->Init(std::move(params)); internal::RootView* root1 = static_cast<internal::RootView*>(widget1->GetRootView()); @@ -125,7 +124,7 @@ TEST_F(DesktopCaptureControllerTest, CaptureWindowInputEventTest) { params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; params.bounds = gfx::Rect(50, 50, 650, 650); params.native_widget = test::CreatePlatformNativeWidgetImpl( - params, widget2.get(), test::kStubCapture, nullptr); + widget2.get(), test::kStubCapture, nullptr); widget2->Init(std::move(params)); internal::RootView* root2 = diff --git a/chromium/ui/views/corewm/tooltip_aura.cc b/chromium/ui/views/corewm/tooltip_aura.cc index c115ea2bf2d..2ae616b9896 100644 --- a/chromium/ui/views/corewm/tooltip_aura.cc +++ b/chromium/ui/views/corewm/tooltip_aura.cc @@ -54,8 +54,7 @@ bool CanUseTranslucentTooltipWidget() { } // Creates a widget of type TYPE_TOOLTIP -views::Widget* CreateTooltipWidget(aura::Window* tooltip_window, - const gfx::Rect& bounds) { +views::Widget* CreateTooltipWidget(aura::Window* tooltip_window) { views::Widget* widget = new views::Widget; views::Widget::InitParams params; // For aura, since we set the type to TYPE_TOOLTIP, the widget will get @@ -65,7 +64,6 @@ views::Widget* CreateTooltipWidget(aura::Window* tooltip_window, DCHECK(params.context); params.z_order = ui::ZOrderLevel::kFloatingUIElement; params.accept_events = false; - params.bounds = bounds; if (CanUseTranslucentTooltipWidget()) params.opacity = views::Widget::InitParams::WindowOpacity::kTranslucent; params.shadow_type = views::Widget::InitParams::ShadowType::kNone; @@ -237,15 +235,10 @@ void TooltipAura::SetText(aura::Window* window, tooltip_view_->SetMaxWidth(GetMaxWidth(location)); tooltip_view_->SetText(tooltip_text); - const gfx::Rect adjusted_bounds = - GetTooltipBounds(location, tooltip_view_->GetPreferredSize()); - if (!widget_) { - widget_ = CreateTooltipWidget(tooltip_window_, adjusted_bounds); + widget_ = CreateTooltipWidget(tooltip_window_); widget_->SetContentsView(tooltip_view_.get()); widget_->AddObserver(this); - } else { - widget_->SetBounds(adjusted_bounds); } ui::NativeTheme* native_theme = widget_->GetNativeTheme(); @@ -263,6 +256,17 @@ void TooltipAura::SetText(aura::Window* window, color_utils::GetResultingPaintColor(foreground_color, background_color); tooltip_view_->SetBackgroundColor(background_color, foreground_color); tooltip_view_->SetForegroundColor(foreground_color); + + // Calculate the tooltip preferred size after all tooltip attributes are + // updated - tooltip updates (for example setting text color) may invalidate + // the tooltip render text layout, which would make layout run just done to + // calculate the tooltip string size get immendiately disregarded. + // This also addresses https://crbug.com/2181825 (after color update, + // GetPreferredSize() will generate fresh render text layout, even if the + // actual tooltip text hasn't changed). + const gfx::Rect adjusted_bounds = + GetTooltipBounds(location, tooltip_view_->GetPreferredSize()); + widget_->SetBounds(adjusted_bounds); } void TooltipAura::Show() { diff --git a/chromium/ui/views/corewm/tooltip_controller.cc b/chromium/ui/views/corewm/tooltip_controller.cc index 5b911f138a5..c19d2296ff9 100644 --- a/chromium/ui/views/corewm/tooltip_controller.cc +++ b/chromium/ui/views/corewm/tooltip_controller.cc @@ -141,9 +141,12 @@ int TooltipController::GetMaxWidth(const gfx::Point& location) const { } void TooltipController::UpdateTooltip(aura::Window* target) { - // If tooltip is visible, we may want to hide it. If it is not, we are ok. - if (tooltip_window_ == target && tooltip_->IsVisible()) + // Ensure relevant tooltip is updated when it is visible or scheduled to + // show. Otherwise, a stale tooltip might be shown. + if (tooltip_window_ == target && + (tooltip_->IsVisible() || tooltip_defer_timer_.IsRunning())) { UpdateIfRequired(); + } // Reset |tooltip_window_at_mouse_press_| if the moving within the same window // but over a region that has different tooltip text. diff --git a/chromium/ui/views/corewm/tooltip_controller_test_helper.cc b/chromium/ui/views/corewm/tooltip_controller_test_helper.cc index ebe66420ebe..d64125582fe 100644 --- a/chromium/ui/views/corewm/tooltip_controller_test_helper.cc +++ b/chromium/ui/views/corewm/tooltip_controller_test_helper.cc @@ -44,6 +44,11 @@ bool TooltipControllerTestHelper::IsTooltipVisible() { return controller_->IsTooltipVisible(); } +void TooltipControllerTestHelper::SetTooltipShowDelayEnable( + bool tooltip_show_delay) { + controller_->tooltip_show_delayed_ = tooltip_show_delay; +} + TooltipTestView::TooltipTestView() = default; TooltipTestView::~TooltipTestView() = default; diff --git a/chromium/ui/views/corewm/tooltip_controller_test_helper.h b/chromium/ui/views/corewm/tooltip_controller_test_helper.h index e62aaad9f23..3c2909548f2 100644 --- a/chromium/ui/views/corewm/tooltip_controller_test_helper.h +++ b/chromium/ui/views/corewm/tooltip_controller_test_helper.h @@ -38,6 +38,7 @@ class TooltipControllerTestHelper { void FireTooltipShownTimer(); bool IsTooltipShownTimerRunning(); bool IsTooltipVisible(); + void SetTooltipShowDelayEnable(bool tooltip_show_delay); private: TooltipController* controller_; diff --git a/chromium/ui/views/corewm/tooltip_controller_unittest.cc b/chromium/ui/views/corewm/tooltip_controller_unittest.cc index eee55c8bb63..30ba48c792c 100644 --- a/chromium/ui/views/corewm/tooltip_controller_unittest.cc +++ b/chromium/ui/views/corewm/tooltip_controller_unittest.cc @@ -325,6 +325,36 @@ TEST_F(TooltipControllerTest, DontShowEmptyTooltips) { EXPECT_FALSE(helper_->IsTooltipVisible()); } +TEST_F(TooltipControllerTest, TooltipUpdateWhenTooltipDeferTimerIsRunning) { + view_->set_tooltip_text(ASCIIToUTF16("Tooltip Text for view 1")); + EXPECT_EQ(base::string16(), helper_->GetTooltipText()); + EXPECT_EQ(nullptr, helper_->GetTooltipWindow()); + + TooltipTestView* view2 = PrepareSecondView(); + view2->set_tooltip_text(ASCIIToUTF16("Tooltip Text for view 2")); + + aura::Window* window = GetWindow(); + + // Tooltips show up with delay + helper_->SetTooltipShowDelayEnable(true); + + // Tooltip 1 is scheduled and invisibled + generator_->MoveMouseRelativeTo(window, view_->bounds().CenterPoint()); + EXPECT_FALSE(helper_->IsTooltipVisible()); + EXPECT_FALSE(helper_->IsTooltipShownTimerRunning()); + + // Tooltip 2 is scheduled and invisible, the expected tooltip is tooltip 2 + generator_->MoveMouseRelativeTo(window, view2->bounds().CenterPoint()); + EXPECT_FALSE(helper_->IsTooltipVisible()); + EXPECT_FALSE(helper_->IsTooltipShownTimerRunning()); + base::string16 expected_tooltip = ASCIIToUTF16("Tooltip Text for view 2"); + EXPECT_EQ(expected_tooltip, wm::GetTooltipText(window)); + EXPECT_EQ(expected_tooltip, helper_->GetTooltipText()); + EXPECT_EQ(window, helper_->GetTooltipWindow()); + + helper_->SetTooltipShowDelayEnable(false); +} + TEST_F(TooltipControllerTest, TooltipHidesOnKeyPressAndStaysHiddenUntilChange) { view_->set_tooltip_text(ASCIIToUTF16("Tooltip Text for view 1")); EXPECT_EQ(base::string16(), helper_->GetTooltipText()); diff --git a/chromium/ui/views/debug_utils.cc b/chromium/ui/views/debug_utils.cc index d176fd40a8e..63355b3b6f4 100644 --- a/chromium/ui/views/debug_utils.cc +++ b/chromium/ui/views/debug_utils.cc @@ -144,12 +144,16 @@ std::string PrintViewGraphImpl(const View* view) { void PrintViewHierarchy(const View* view) { std::ostringstream out; - out << "View hierarchy:\n"; - PrintViewHierarchyImp(view, 0, &out); + PrintViewHierarchy(view, &out); // Error so users in the field can generate and upload logs. LOG(ERROR) << out.str(); } +void PrintViewHierarchy(const View* view, std::ostringstream* out) { + *out << "View hierarchy:\n"; + PrintViewHierarchyImp(view, 0, out); +} + void PrintFocusHierarchy(const View* view) { std::ostringstream out; out << "Focus hierarchy:\n"; diff --git a/chromium/ui/views/debug_utils.h b/chromium/ui/views/debug_utils.h index 3051359a207..6d0d9cd0d54 100644 --- a/chromium/ui/views/debug_utils.h +++ b/chromium/ui/views/debug_utils.h @@ -16,6 +16,9 @@ class View; // Log the view hierarchy. VIEWS_EXPORT void PrintViewHierarchy(const View* view); +// Print the view hierarchy to |out|. +VIEWS_EXPORT void PrintViewHierarchy(const View* view, std::ostringstream* out); + // Log the focus traversal hierarchy. VIEWS_EXPORT void PrintFocusHierarchy(const View* view); diff --git a/chromium/ui/views/event_monitor_aura.cc b/chromium/ui/views/event_monitor_aura.cc index 8ea72524463..69edc44bc48 100644 --- a/chromium/ui/views/event_monitor_aura.cc +++ b/chromium/ui/views/event_monitor_aura.cc @@ -6,7 +6,7 @@ #include <memory> -#include "base/logging.h" +#include "base/check_op.h" #include "base/scoped_observer.h" #include "ui/aura/env.h" #include "ui/aura/window.h" diff --git a/chromium/ui/views/event_monitor_mac.mm b/chromium/ui/views/event_monitor_mac.mm index cfa72698b19..d16287300d9 100644 --- a/chromium/ui/views/event_monitor_mac.mm +++ b/chromium/ui/views/event_monitor_mac.mm @@ -6,7 +6,7 @@ #import <Cocoa/Cocoa.h> -#include "base/logging.h" +#include "base/check.h" #include "base/memory/ptr_util.h" #include "ui/display/screen.h" #include "ui/events/event.h" diff --git a/chromium/ui/views/examples/BUILD.gn b/chromium/ui/views/examples/BUILD.gn index d2223b73a62..ba6f3d7603c 100644 --- a/chromium/ui/views/examples/BUILD.gn +++ b/chromium/ui/views/examples/BUILD.gn @@ -4,6 +4,8 @@ import("//build/config/jumbo.gni") import("//build/config/ui.gni") +import("//testing/test.gni") +import("//tools/grit/grit_rule.gni") jumbo_component("views_examples_lib") { testonly = true @@ -21,8 +23,12 @@ jumbo_component("views_examples_lib") { "button_sticker_sheet.h", "checkbox_example.cc", "checkbox_example.h", + "colored_dialog_example.cc", + "colored_dialog_example.h", "combobox_example.cc", "combobox_example.h", + "create_examples.cc", + "create_examples.h", "dialog_example.cc", "dialog_example.h", "example_base.cc", @@ -39,6 +45,8 @@ jumbo_component("views_examples_lib") { "layout_example_base.h", "link_example.cc", "link_example.h", + "login_bubble_dialog.cc", + "login_bubble_dialog.h", "menu_example.cc", "menu_example.h", "message_box_example.cc", @@ -85,6 +93,8 @@ jumbo_component("views_examples_lib") { ] deps = [ + ":views_examples_resources_grd", + ":views_examples_resources_pak", "//base", "//cc/paint", "//skia", @@ -96,6 +106,7 @@ jumbo_component("views_examples_lib") { "//ui/native_theme", "//ui/resources", "//ui/resources:ui_test_pak", + "//ui/strings:ui_strings", "//ui/views", ] @@ -108,32 +119,47 @@ jumbo_component("views_examples_lib") { } } -executable("views_examples_exe") { +source_set("views_examples_proc") { testonly = true - sources = [ "examples_main.cc" ] + sources = [ + "examples_exit_code.h", + "examples_main_proc.cc", + "examples_main_proc.h", + ] deps = [ ":views_examples_lib", "//base", - "//base:i18n", "//base/test:test_support", - "//build/win:default_exe_manifest", + "//cc/paint", "//components/viz/host", "//components/viz/service", "//mojo/core/embedder", + "//skia", "//ui/base", - "//ui/base/ime/init", "//ui/compositor", "//ui/compositor:test_support", "//ui/gfx", - "//ui/gl", - "//ui/gl/init", - "//ui/resources:ui_test_pak", "//ui/views", "//ui/views:test_support", ] + if (is_win || is_mac || (is_linux && !is_chromeos)) { + sources += [ + "examples_skia_gold_pixel_diff.cc", + "examples_skia_gold_pixel_diff.h", + ] + deps += [ + "//ui/base:pixel_diff_test_support", + "//ui/snapshot", + ] + } + + if (is_win) { + deps += [ "//build/win:default_exe_manifest" ] + } + if (use_aura) { deps += [ "//ui/aura", @@ -145,6 +171,21 @@ executable("views_examples_exe") { } } +executable("views_examples") { + testonly = true + + sources = [ "examples_main.cc" ] + + deps = [ + ":views_examples_lib", + ":views_examples_proc", + "//base", + "//base/test:test_support", + "//build/win:default_exe_manifest", + "//ui/resources:ui_test_pak", + ] +} + jumbo_component("views_examples_with_content_lib") { testonly = true sources = [ @@ -159,6 +200,8 @@ jumbo_component("views_examples_with_content_lib") { deps = [ ":views_examples_lib", + ":views_examples_resources_grd", + ":views_examples_resources_pak", "//base", "//content", "//skia", @@ -169,10 +212,10 @@ jumbo_component("views_examples_with_content_lib") { ] } -executable("views_examples_with_content_exe") { +executable("views_examples_with_content") { testonly = true - sources = [ "examples_with_content_main_exe.cc" ] + sources = [ "examples_with_content_main.cc" ] defines = [ "VIEWS_EXAMPLES_WITH_CONTENT_IMPLEMENTATION" ] @@ -191,6 +234,10 @@ executable("views_examples_with_content_exe") { configs += [ "//build/config/win:windowed" ] configs -= [ "//build/config/win:console" ] } + + if (is_mac) { + deps += [ "//sandbox/mac:seatbelt" ] + } } copy("copy_content_resources") { @@ -198,3 +245,61 @@ copy("copy_content_resources") { sources = [ "$root_gen_dir/content/content_resources.pak" ] outputs = [ "$root_out_dir/content_resources.pak" ] } + +grit("views_examples_resources_grd") { + testonly = true + source = "views_examples_resources.grd" + outputs = [ + "grit/views_examples_resources.h", + "views_examples_resources.pak", + ] +} + +copy("views_examples_resources_pak") { + testonly = true + sources = [ "$target_gen_dir/views_examples_resources.pak" ] + outputs = [ "$root_out_dir/views_examples_resources.pak" ] + public_deps = [ ":views_examples_resources_grd" ] +} + +test("views_examples_unittests") { + sources = [ + "examples_unittest.cc", + "examples_unittest_main.cc", + ] + + deps = [ + ":views_examples_lib", + ":views_examples_proc", + "//base", + "//base/test:test_support", + "//components/viz/host", + "//components/viz/service", + "//mojo/core/embedder", + "//ui/base", + "//ui/base/ime/init", + "//ui/compositor", + "//ui/compositor:test_support", + "//ui/gfx", + "//ui/gl", + "//ui/gl/init", + "//ui/resources:ui_test_pak", + "//ui/snapshot", + "//ui/views", + "//ui/views:test_support", + ] + + if (use_aura) { + deps += [ + "//ui/aura", + "//ui/wm", + ] + } + + data_deps = [ + ":views_examples_resources_pak", + "//ui/resources:ui_test_pak_data", + ] + + data = [ "$root_out_dir/views_examples_resources.pak" ] +} diff --git a/chromium/ui/views/examples/DEPS b/chromium/ui/views/examples/DEPS index 7d637be55cc..cf9717c4951 100644 --- a/chromium/ui/views/examples/DEPS +++ b/chromium/ui/views/examples/DEPS @@ -1,4 +1,5 @@ include_rules = [ + "+components/viz/common/features.h", "+components/viz/host", "+components/viz/service", # In-process viz service. "+content/public", @@ -7,5 +8,6 @@ include_rules = [ "+sandbox", "+ui/gl/gl_switches.h", # Disable Direct Composition Workaround. "+ui/gl/init/gl_factory.h", # To initialize GL bindings. + "+ui/snapshot", # Enable Skia Gold testing "+ui/views_content_client", ] diff --git a/chromium/ui/views/examples/box_layout_example.cc b/chromium/ui/views/examples/box_layout_example.cc index 61c39340f07..92665320bf8 100644 --- a/chromium/ui/views/examples/box_layout_example.cc +++ b/chromium/ui/views/examples/box_layout_example.cc @@ -38,25 +38,25 @@ void BoxLayoutExample::CreateAdditionalControls(int vertical_pos) { static const char* cross_axis_values[4] = {"Stretch", "Start", "Center", "End"}; - orientation_ = CreateCombobox(base::ASCIIToUTF16("Orientation"), - orientation_values, 2, &vertical_pos); - main_axis_alignment_ = CreateCombobox(base::ASCIIToUTF16("Main axis"), - main_axis_values, 3, &vertical_pos); - cross_axis_alignment_ = CreateCombobox(base::ASCIIToUTF16("Cross axis"), - cross_axis_values, 4, &vertical_pos); + orientation_ = CreateAndAddCombobox(base::ASCIIToUTF16("Orientation"), + orientation_values, 2, &vertical_pos); + main_axis_alignment_ = CreateAndAddCombobox( + base::ASCIIToUTF16("Main axis"), main_axis_values, 3, &vertical_pos); + cross_axis_alignment_ = CreateAndAddCombobox( + base::ASCIIToUTF16("Cross axis"), cross_axis_values, 4, &vertical_pos); between_child_spacing_ = - CreateTextfield(base::ASCIIToUTF16("Child spacing"), &vertical_pos); + CreateAndAddTextfield(base::ASCIIToUTF16("Child spacing"), &vertical_pos); default_flex_ = - CreateTextfield(base::ASCIIToUTF16("Default flex"), &vertical_pos); - min_cross_axis_size_ = - CreateTextfield(base::ASCIIToUTF16("Min cross axis"), &vertical_pos); + CreateAndAddTextfield(base::ASCIIToUTF16("Default flex"), &vertical_pos); + min_cross_axis_size_ = CreateAndAddTextfield( + base::ASCIIToUTF16("Min cross axis"), &vertical_pos); CreateMarginsTextFields(base::ASCIIToUTF16("Insets"), &border_insets_, &vertical_pos); - collapse_margins_ = - CreateCheckbox(base::ASCIIToUTF16("Collapse margins"), &vertical_pos); + collapse_margins_ = CreateAndAddCheckbox( + base::ASCIIToUTF16("Collapse margins"), &vertical_pos); UpdateLayoutManager(); } diff --git a/chromium/ui/views/examples/bubble_example.cc b/chromium/ui/views/examples/bubble_example.cc index f7877482ecd..040cb595a18 100644 --- a/chromium/ui/views/examples/bubble_example.cc +++ b/chromium/ui/views/examples/bubble_example.cc @@ -78,7 +78,7 @@ class ExampleBubble : public BubbleDialogDelegateView { void Init() override { SetLayoutManager(std::make_unique<BoxLayout>( BoxLayout::Orientation::kVertical, gfx::Insets(50))); - AddChildView(new Label(GetArrowName(arrow()))); + AddChildView(std::make_unique<Label>(GetArrowName(arrow()))); } private: @@ -94,18 +94,18 @@ BubbleExample::~BubbleExample() = default; void BubbleExample::CreateExampleView(View* container) { container->SetLayoutManager(std::make_unique<BoxLayout>( BoxLayout::Orientation::kHorizontal, gfx::Insets(), 10)); - no_shadow_ = new LabelButton(this, ASCIIToUTF16("No Shadow")); - container->AddChildView(no_shadow_); - no_shadow_opaque_ = new LabelButton(this, ASCIIToUTF16("Opaque Border")); - container->AddChildView(no_shadow_opaque_); - big_shadow_ = new LabelButton(this, ASCIIToUTF16("Big Shadow")); - container->AddChildView(big_shadow_); - small_shadow_ = new LabelButton(this, ASCIIToUTF16("Small Shadow")); - container->AddChildView(small_shadow_); - no_assets_ = new LabelButton(this, ASCIIToUTF16("No Assets")); - container->AddChildView(no_assets_); - persistent_ = new LabelButton(this, ASCIIToUTF16("Persistent")); - container->AddChildView(persistent_); + no_shadow_ = container->AddChildView( + std::make_unique<LabelButton>(this, ASCIIToUTF16("No Shadow"))); + no_shadow_opaque_ = container->AddChildView( + std::make_unique<LabelButton>(this, ASCIIToUTF16("Opaque Border"))); + big_shadow_ = container->AddChildView( + std::make_unique<LabelButton>(this, ASCIIToUTF16("Big Shadow"))); + small_shadow_ = container->AddChildView( + std::make_unique<LabelButton>(this, ASCIIToUTF16("Small Shadow"))); + no_assets_ = container->AddChildView( + std::make_unique<LabelButton>(this, ASCIIToUTF16("No Assets"))); + persistent_ = container->AddChildView( + std::make_unique<LabelButton>(this, ASCIIToUTF16("Persistent"))); } void BubbleExample::ButtonPressed(Button* sender, const ui::Event& event) { diff --git a/chromium/ui/views/examples/button_sticker_sheet.cc b/chromium/ui/views/examples/button_sticker_sheet.cc index 8fca38898e1..1780375bb8b 100644 --- a/chromium/ui/views/examples/button_sticker_sheet.cc +++ b/chromium/ui/views/examples/button_sticker_sheet.cc @@ -33,7 +33,8 @@ GridLayout* MakeStretchyGridLayout(View* host, int ncols) { const GridLayout::Alignment kColumnStretchesHorizontally = GridLayout::FILL; const GridLayout::Alignment kColumnStretchesVertically = GridLayout::FILL; const float kColumnDoesNotResize = 0.0; - const GridLayout::SizeType kColumnUsesFixedSize = GridLayout::FIXED; + const GridLayout::ColumnSize kColumnUsesFixedSize = + GridLayout::ColumnSize::kFixed; const int kColumnWidth = 96; GridLayout* layout = diff --git a/chromium/ui/views/examples/checkbox_example.cc b/chromium/ui/views/examples/checkbox_example.cc index 953115abec5..676f0f79cc7 100644 --- a/chromium/ui/views/examples/checkbox_example.cc +++ b/chromium/ui/views/examples/checkbox_example.cc @@ -21,9 +21,9 @@ CheckboxExample::CheckboxExample() : ExampleBase("Checkbox") {} CheckboxExample::~CheckboxExample() = default; void CheckboxExample::CreateExampleView(View* container) { - button_ = new Checkbox(base::ASCIIToUTF16("Checkbox"), this); container->SetLayoutManager(std::make_unique<FillLayout>()); - container->AddChildView(button_); + button_ = container->AddChildView( + std::make_unique<Checkbox>(base::ASCIIToUTF16("Checkbox"), this)); } void CheckboxExample::ButtonPressed(Button* sender, const ui::Event& event) { diff --git a/chromium/ui/views/examples/colored_dialog_example.cc b/chromium/ui/views/examples/colored_dialog_example.cc new file mode 100644 index 00000000000..de07088cb51 --- /dev/null +++ b/chromium/ui/views/examples/colored_dialog_example.cc @@ -0,0 +1,176 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "ui/views/examples/colored_dialog_example.h" + +#include <memory> +#include <utility> + +#include "base/containers/adapters.h" +#include "ui/base/l10n/l10n_util.h" +#include "ui/base/ui_base_types.h" +#include "ui/gfx/paint_vector_icon.h" +#include "ui/gfx/vector_icon_types.h" +#include "ui/native_theme/native_theme_color_id.h" +#include "ui/views/controls/button/checkbox.h" +#include "ui/views/controls/button/md_text_button.h" +#include "ui/views/controls/label.h" +#include "ui/views/controls/textfield/textfield.h" +#include "ui/views/examples/grit/views_examples_resources.h" +#include "ui/views/layout/box_layout.h" +#include "ui/views/layout/fill_layout.h" +#include "ui/views/layout/layout_provider.h" +#include "ui/views/vector_icons.h" +#include "ui/views/widget/widget.h" + +namespace views { +namespace examples { + +class ThemeTrackingCheckbox : public views::Checkbox, + public views::ButtonListener { + public: + explicit ThemeTrackingCheckbox(const base::string16& label) + : Checkbox(label, this) {} + ThemeTrackingCheckbox(const ThemeTrackingCheckbox&) = delete; + ThemeTrackingCheckbox& operator=(const ThemeTrackingCheckbox&) = delete; + ~ThemeTrackingCheckbox() override = default; + + // views::Checkbox + void OnThemeChanged() override { + views::Checkbox::OnThemeChanged(); + + SetChecked(GetNativeTheme()->ShouldUseDarkColors()); + } + + // ButtonListener + void ButtonPressed(views::Button* sender, const ui::Event& event) override { + GetNativeTheme()->set_use_dark_colors(GetChecked()); + GetWidget()->ThemeChanged(); + } +}; + +class TextVectorImageButton : public views::MdTextButton { + public: + TextVectorImageButton(ButtonListener* listener, + const base::string16& text, + const gfx::VectorIcon& icon) + : MdTextButton(listener, style::CONTEXT_BUTTON_MD), icon_(icon) { + SetText(text); + } + TextVectorImageButton(const TextVectorImageButton&) = delete; + TextVectorImageButton& operator=(const TextVectorImageButton&) = delete; + ~TextVectorImageButton() override = default; + + void OnThemeChanged() override { + views::MdTextButton::OnThemeChanged(); + + // Use the text color for the associated vector image. + SetImage(views::Button::ButtonState::STATE_NORMAL, + gfx::CreateVectorIcon(icon_, label()->GetEnabledColor())); + } + + private: + const gfx::VectorIcon& icon_; +}; + +ColoredDialog::ColoredDialog(AcceptCallback accept_callback) { + SetAcceptCallback(base::BindOnce( + [](ColoredDialog* dialog, AcceptCallback callback) { + std::move(callback).Run(dialog->textfield_->GetText()); + }, + base::Unretained(this), std::move(accept_callback))); + + SetTitle(l10n_util::GetStringUTF16(IDS_COLORED_DIALOG_TITLE)); + + SetLayoutManager(std::make_unique<views::FillLayout>()); + set_margins(views::LayoutProvider::Get()->GetDialogInsetsForContentType( + views::CONTROL, views::CONTROL)); + + textfield_ = AddChildView(std::make_unique<views::Textfield>()); + textfield_->SetPlaceholderText( + l10n_util::GetStringUTF16(IDS_COLORED_DIALOG_TEXTFIELD_PLACEHOLDER)); + textfield_->SetAccessibleName( + l10n_util::GetStringUTF16(IDS_COLORED_DIALOG_TEXTFIELD_AX_LABEL)); + textfield_->set_controller(this); + + SetButtonLabel(ui::DIALOG_BUTTON_OK, + l10n_util::GetStringUTF16(IDS_COLORED_DIALOG_SUBMIT_BUTTON)); + SetButtonEnabled(ui::DIALOG_BUTTON_OK, false); +} + +ColoredDialog::~ColoredDialog() = default; + +ui::ModalType ColoredDialog::GetModalType() const { + return ui::MODAL_TYPE_WINDOW; +} + +bool ColoredDialog::ShouldShowCloseButton() const { + return false; +} + +void ColoredDialog::ContentsChanged(Textfield* sender, + const base::string16& new_contents) { + SetButtonEnabled(ui::DIALOG_BUTTON_OK, !textfield_->GetText().empty()); + DialogModelChanged(); +} + +ColoredDialogChooser::ColoredDialogChooser() { + views::LayoutProvider* provider = views::LayoutProvider::Get(); + const int vertical_spacing = + provider->GetDistanceMetric(views::DISTANCE_UNRELATED_CONTROL_VERTICAL); + auto* layout = SetLayoutManager(std::make_unique<views::BoxLayout>( + views::BoxLayout::Orientation::kVertical, gfx::Insets(), + vertical_spacing)); + layout->set_cross_axis_alignment( + views::BoxLayout::CrossAxisAlignment::kStart); + + AddChildView(std::make_unique<ThemeTrackingCheckbox>( + l10n_util::GetStringUTF16(IDS_COLORED_DIALOG_CHOOSER_CHECKBOX))); + + AddChildView(std::make_unique<TextVectorImageButton>( + this, l10n_util::GetStringUTF16(IDS_COLORED_DIALOG_CHOOSER_BUTTON), + views::kInfoIcon)); + + confirmation_label_ = AddChildView( + std::make_unique<views::Label>(base::string16(), style::CONTEXT_LABEL)); + confirmation_label_->SetVisible(false); +} + +ColoredDialogChooser::~ColoredDialogChooser() = default; + +void ColoredDialogChooser::ButtonPressed(Button* sender, + const ui::Event& event) { + // Create the colored dialog. + views::Widget* widget = DialogDelegate::CreateDialogWidget( + new ColoredDialog(base::BindOnce(&ColoredDialogChooser::OnFeedbackSubmit, + base::Unretained(this))), + nullptr, GetWidget()->GetNativeView()); + widget->Show(); +} + +void ColoredDialogChooser::OnFeedbackSubmit(base::string16 text) { + constexpr base::TimeDelta kConfirmationDuration = + base::TimeDelta::FromSeconds(3); + + confirmation_label_->SetText(l10n_util::GetStringFUTF16( + IDS_COLORED_DIALOG_CHOOSER_CONFIRM_LABEL, text)); + confirmation_label_->SetVisible(true); + + confirmation_timer_.Start( + FROM_HERE, kConfirmationDuration, + base::BindOnce([](views::View* view) { view->SetVisible(false); }, + confirmation_label_)); +} + +ColoredDialogExample::ColoredDialogExample() : ExampleBase("Colored Dialog") {} + +ColoredDialogExample::~ColoredDialogExample() = default; + +void ColoredDialogExample::CreateExampleView(views::View* container) { + container->SetLayoutManager(std::make_unique<views::FillLayout>()); + container->AddChildView(std::make_unique<ColoredDialogChooser>()); +} + +} // namespace examples +} // namespace views diff --git a/chromium/ui/views/examples/colored_dialog_example.h b/chromium/ui/views/examples/colored_dialog_example.h new file mode 100644 index 00000000000..82b5c0a571c --- /dev/null +++ b/chromium/ui/views/examples/colored_dialog_example.h @@ -0,0 +1,77 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef UI_VIEWS_EXAMPLES_COLORED_DIALOG_EXAMPLE_H_ +#define UI_VIEWS_EXAMPLES_COLORED_DIALOG_EXAMPLE_H_ + +#include "base/timer/timer.h" +#include "ui/views/controls/button/button.h" +#include "ui/views/controls/textfield/textfield_controller.h" +#include "ui/views/examples/example_base.h" +#include "ui/views/view.h" +#include "ui/views/window/dialog_delegate.h" + +namespace views { + +class Button; +class Label; + +namespace examples { + +class ColoredDialog : public views::DialogDelegateView, + public views::TextfieldController { + public: + using AcceptCallback = base::OnceCallback<void(base::string16)>; + + explicit ColoredDialog(AcceptCallback accept_callback); + ColoredDialog(const ColoredDialog&) = delete; + ColoredDialog& operator=(const ColoredDialog&) = delete; + ~ColoredDialog() override; + + protected: + // views::DialogDelegateView + ui::ModalType GetModalType() const override; + bool ShouldShowCloseButton() const override; + + // views::TextfieldController + void ContentsChanged(Textfield* sender, + const base::string16& new_contents) override; + + private: + views::Textfield* textfield_; +}; + +class ColoredDialogChooser : public views::View, public views::ButtonListener { + public: + ColoredDialogChooser(); + ColoredDialogChooser(const ColoredDialogChooser&) = delete; + ColoredDialogChooser& operator=(const ColoredDialogChooser&) = delete; + ~ColoredDialogChooser() override; + + // ButtonListener + void ButtonPressed(views::Button* sender, const ui::Event& event) override; + + private: + void OnFeedbackSubmit(base::string16 text); + + views::Label* confirmation_label_; + base::OneShotTimer confirmation_timer_; +}; + +// An example that exercises BubbleDialogDelegateView or DialogDelegateView. +class VIEWS_EXAMPLES_EXPORT ColoredDialogExample : public ExampleBase { + public: + ColoredDialogExample(); + ColoredDialogExample(const ColoredDialogExample&) = delete; + ColoredDialogExample& operator=(const ColoredDialogExample&) = delete; + ~ColoredDialogExample() override; + + // ExampleBase + void CreateExampleView(views::View* container) override; +}; + +} // namespace examples +} // namespace views + +#endif // UI_VIEWS_EXAMPLES_COLORED_DIALOG_EXAMPLE_H_ diff --git a/chromium/ui/views/examples/combobox_example.cc b/chromium/ui/views/examples/combobox_example.cc index 178a465e430..dc1e8bba76f 100644 --- a/chromium/ui/views/examples/combobox_example.cc +++ b/chromium/ui/views/examples/combobox_example.cc @@ -41,20 +41,19 @@ ComboboxExample::ComboboxExample() : ExampleBase("Combo Box") {} ComboboxExample::~ComboboxExample() = default; void ComboboxExample::CreateExampleView(View* container) { - combobox_ = new Combobox(std::make_unique<ComboboxModelExample>()); + container->SetLayoutManager(std::make_unique<BoxLayout>( + BoxLayout::Orientation::kVertical, gfx::Insets(10, 0), 5)); + + combobox_ = container->AddChildView( + std::make_unique<Combobox>(std::make_unique<ComboboxModelExample>())); combobox_->set_listener(this); combobox_->SetSelectedIndex(3); - auto* disabled_combobox = - new Combobox(std::make_unique<ComboboxModelExample>()); + auto* disabled_combobox = container->AddChildView( + std::make_unique<Combobox>(std::make_unique<ComboboxModelExample>())); disabled_combobox->set_listener(this); disabled_combobox->SetSelectedIndex(4); disabled_combobox->SetEnabled(false); - - container->SetLayoutManager(std::make_unique<BoxLayout>( - BoxLayout::Orientation::kVertical, gfx::Insets(10, 0), 5)); - container->AddChildView(combobox_); - container->AddChildView(disabled_combobox); } void ComboboxExample::OnPerformAction(Combobox* combobox) { diff --git a/chromium/ui/views/examples/create_examples.cc b/chromium/ui/views/examples/create_examples.cc new file mode 100644 index 00000000000..b9805665713 --- /dev/null +++ b/chromium/ui/views/examples/create_examples.cc @@ -0,0 +1,80 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "ui/views/examples/create_examples.h" + +#include <utility> + +#include "ui/views/examples/ax_example.h" +#include "ui/views/examples/box_layout_example.h" +#include "ui/views/examples/bubble_example.h" +#include "ui/views/examples/button_example.h" +#include "ui/views/examples/button_sticker_sheet.h" +#include "ui/views/examples/checkbox_example.h" +#include "ui/views/examples/colored_dialog_example.h" +#include "ui/views/examples/combobox_example.h" +#include "ui/views/examples/dialog_example.h" +#include "ui/views/examples/flex_layout_example.h" +#include "ui/views/examples/label_example.h" +#include "ui/views/examples/link_example.h" +#include "ui/views/examples/login_bubble_dialog.h" +#include "ui/views/examples/menu_example.h" +#include "ui/views/examples/message_box_example.h" +#include "ui/views/examples/multiline_example.h" +#include "ui/views/examples/native_theme_example.h" +#include "ui/views/examples/progress_bar_example.h" +#include "ui/views/examples/radio_button_example.h" +#include "ui/views/examples/scroll_view_example.h" +#include "ui/views/examples/slider_example.h" +#include "ui/views/examples/tabbed_pane_example.h" +#include "ui/views/examples/table_example.h" +#include "ui/views/examples/text_example.h" +#include "ui/views/examples/textfield_example.h" +#include "ui/views/examples/throbber_example.h" +#include "ui/views/examples/toggle_button_example.h" +#include "ui/views/examples/tree_view_example.h" +#include "ui/views/examples/vector_example.h" +#include "ui/views/examples/widget_example.h" + +namespace views { +namespace examples { + +// Creates the default set of examples. +ExampleVector CreateExamples(ExampleVector extra_examples) { + ExampleVector examples = std::move(extra_examples); + examples.push_back(std::make_unique<AxExample>()); + examples.push_back(std::make_unique<BoxLayoutExample>()); + examples.push_back(std::make_unique<BubbleExample>()); + examples.push_back(std::make_unique<ButtonExample>()); + examples.push_back(std::make_unique<ButtonStickerSheet>()); + examples.push_back(std::make_unique<CheckboxExample>()); + examples.push_back(std::make_unique<ColoredDialogExample>()); + examples.push_back(std::make_unique<ComboboxExample>()); + examples.push_back(std::make_unique<DialogExample>()); + examples.push_back(std::make_unique<FlexLayoutExample>()); + examples.push_back(std::make_unique<LabelExample>()); + examples.push_back(std::make_unique<LinkExample>()); + examples.push_back(std::make_unique<LoginBubbleDialogExample>()); + examples.push_back(std::make_unique<MenuExample>()); + examples.push_back(std::make_unique<MessageBoxExample>()); + examples.push_back(std::make_unique<MultilineExample>()); + examples.push_back(std::make_unique<NativeThemeExample>()); + examples.push_back(std::make_unique<ProgressBarExample>()); + examples.push_back(std::make_unique<RadioButtonExample>()); + examples.push_back(std::make_unique<ScrollViewExample>()); + examples.push_back(std::make_unique<SliderExample>()); + examples.push_back(std::make_unique<TabbedPaneExample>()); + examples.push_back(std::make_unique<TableExample>()); + examples.push_back(std::make_unique<TextExample>()); + examples.push_back(std::make_unique<TextfieldExample>()); + examples.push_back(std::make_unique<ToggleButtonExample>()); + examples.push_back(std::make_unique<ThrobberExample>()); + examples.push_back(std::make_unique<TreeViewExample>()); + examples.push_back(std::make_unique<VectorExample>()); + examples.push_back(std::make_unique<WidgetExample>()); + return examples; +} + +} // namespace examples +} // namespace views diff --git a/chromium/ui/views/examples/create_examples.h b/chromium/ui/views/examples/create_examples.h new file mode 100644 index 00000000000..39f1d02181e --- /dev/null +++ b/chromium/ui/views/examples/create_examples.h @@ -0,0 +1,24 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef UI_VIEWS_EXAMPLES_CREATE_EXAMPLES_H_ +#define UI_VIEWS_EXAMPLES_CREATE_EXAMPLES_H_ + +#include <memory> +#include <vector> + +#include "ui/views/examples/example_base.h" +#include "ui/views/examples/views_examples_export.h" + +namespace views { +namespace examples { + +// Creates the default set of examples. +ExampleVector VIEWS_EXAMPLES_EXPORT +CreateExamples(ExampleVector extra_examples = ExampleVector()); + +} // namespace examples +} // namespace views + +#endif // UI_VIEWS_EXAMPLES_CREATE_EXAMPLES_H_ diff --git a/chromium/ui/views/examples/dialog_example.cc b/chromium/ui/views/examples/dialog_example.cc index efdc6f90351..70e7fd417d7 100644 --- a/chromium/ui/views/examples/dialog_example.cc +++ b/chromium/ui/views/examples/dialog_example.cc @@ -55,7 +55,7 @@ class DialogExample::Delegate : public virtual DialogType { this->AddChildView(body); if (parent_->has_extra_button_->GetChecked()) { - DialogDelegate::SetExtraView(MdTextButton::CreateSecondaryUiButton( + DialogDelegate::SetExtraView(MdTextButton::Create( nullptr, parent_->extra_button_label_->GetText())); } @@ -137,13 +137,13 @@ void DialogExample::CreateExampleView(View* container) { container->SetLayoutManager(std::make_unique<views::GridLayout>()); ColumnSet* column_set = layout->AddColumnSet(kFieldsColumnId); column_set->AddColumn(GridLayout::LEADING, GridLayout::FILL, kFixed, - GridLayout::USE_PREF, 0, 0); + GridLayout::ColumnSize::kUsePreferred, 0, 0); column_set->AddPaddingColumn(kFixed, horizontal_spacing); column_set->AddColumn(GridLayout::FILL, GridLayout::FILL, kStretchy, - GridLayout::USE_PREF, 0, 0); + GridLayout::ColumnSize::kUsePreferred, 0, 0); column_set->AddPaddingColumn(kFixed, horizontal_spacing); column_set->AddColumn(GridLayout::FILL, GridLayout::FILL, kFixed, - GridLayout::USE_PREF, 0, 0); + GridLayout::ColumnSize::kUsePreferred, 0, 0); StartTextfieldRow(layout, &title_, "Dialog Title", "Title"); StartTextfieldRow(layout, &body_, "Dialog Body Text", "Body Text"); @@ -169,13 +169,13 @@ void DialogExample::CreateExampleView(View* container) { column_set = layout->AddColumnSet(kButtonsColumnId); column_set->AddColumn(GridLayout::CENTER, GridLayout::CENTER, kStretchy, - GridLayout::USE_PREF, 0, 0); + GridLayout::ColumnSize::kUsePreferred, 0, 0); layout->StartRowWithPadding( kFixed, kButtonsColumnId, kFixed, provider->GetDistanceMetric(views::DISTANCE_UNRELATED_CONTROL_VERTICAL)); - show_ = layout->AddView( - MdTextButton::CreateSecondaryUiButton(this, base::ASCIIToUTF16("Show"))); + show_ = + layout->AddView(MdTextButton::Create(this, base::ASCIIToUTF16("Show"))); } void DialogExample::StartRowWithLabel(GridLayout* layout, const char* label) { diff --git a/chromium/ui/views/examples/example_base.h b/chromium/ui/views/examples/example_base.h index 231011b6101..69e9105692a 100644 --- a/chromium/ui/views/examples/example_base.h +++ b/chromium/ui/views/examples/example_base.h @@ -6,6 +6,7 @@ #define UI_VIEWS_EXAMPLES_EXAMPLE_BASE_H_ #include <string> +#include <vector> #include "base/macros.h" #include "ui/views/examples/views_examples_export.h" @@ -38,6 +39,8 @@ class VIEWS_EXAMPLES_EXPORT ExampleBase { DISALLOW_COPY_AND_ASSIGN(ExampleBase); }; +using ExampleVector = std::vector<std::unique_ptr<ExampleBase>>; + } // namespace examples } // namespace views diff --git a/chromium/ui/views/examples/examples_exit_code.h b/chromium/ui/views/examples/examples_exit_code.h new file mode 100644 index 00000000000..3ef5e79a5e5 --- /dev/null +++ b/chromium/ui/views/examples/examples_exit_code.h @@ -0,0 +1,25 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef UI_VIEWS_EXAMPLES_EXAMPLES_EXIT_CODE_H_ +#define UI_VIEWS_EXAMPLES_EXAMPLES_EXIT_CODE_H_ + +namespace views { +namespace examples { + +enum class ExamplesExitCode { + // Comparison succeeded. + kSucceeded = 0, + // Screenshot image empty. + kImageEmpty, + // Comparison failed. + kFailed, + // No comparison attempted. + kNone, +}; + +} // namespace examples +} // namespace views + +#endif // UI_VIEWS_EXAMPLES_EXAMPLES_EXIT_CODE_H_ diff --git a/chromium/ui/views/examples/examples_main.cc b/chromium/ui/views/examples/examples_main.cc index a3e2c965c77..82ec4ab5200 100644 --- a/chromium/ui/views/examples/examples_main.cc +++ b/chromium/ui/views/examples/examples_main.cc @@ -2,168 +2,19 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include <memory> - #include "base/at_exit.h" -#include "base/base_switches.h" #include "base/command_line.h" -#include "base/feature_list.h" -#include "base/files/file_path.h" -#include "base/i18n/icu_util.h" -#include "base/memory/ptr_util.h" -#include "base/path_service.h" -#include "base/power_monitor/power_monitor.h" -#include "base/power_monitor/power_monitor_device_source.h" -#include "base/run_loop.h" -#include "base/test/scoped_run_loop_timeout.h" -#include "base/test/task_environment.h" -#include "base/test/test_discardable_memory_allocator.h" #include "base/test/test_timeouts.h" -#include "build/build_config.h" -#include "components/viz/host/host_frame_sink_manager.h" -#include "components/viz/service/display_embedder/server_shared_bitmap_manager.h" -#include "components/viz/service/frame_sinks/frame_sink_manager_impl.h" -#include "mojo/core/embedder/embedder.h" -#include "ui/base/ime/init/input_method_initializer.h" -#include "ui/base/resource/resource_bundle.h" -#include "ui/base/ui_base_paths.h" -#include "ui/compositor/test/in_process_context_factory.h" -#include "ui/display/screen.h" -#include "ui/gfx/font_util.h" -#include "ui/gl/gl_switches.h" -#include "ui/gl/init/gl_factory.h" -#include "ui/views/buildflags.h" -#include "ui/views/examples/example_base.h" -#include "ui/views/examples/examples_window.h" -#include "ui/views/test/desktop_test_views_delegate.h" - -#if defined(USE_AURA) -#include "ui/aura/env.h" -#include "ui/wm/core/wm_state.h" -#endif - -#if BUILDFLAG(ENABLE_DESKTOP_AURA) -#include "ui/views/widget/desktop_aura/desktop_screen.h" -#endif - -#if defined(OS_WIN) -#include "ui/base/win/scoped_ole_initializer.h" -#endif - -#if defined(USE_OZONE) -#include "ui/ozone/public/ozone_platform.h" -#endif - -#if defined(USE_X11) -#include "ui/gfx/x/x11_connection.h" // nogncheck -#endif - -base::LazyInstance<base::TestDiscardableMemoryAllocator>::DestructorAtExit - g_discardable_memory_allocator = LAZY_INSTANCE_INITIALIZER; +#include "ui/views/examples/examples_main_proc.h" int main(int argc, char** argv) { -#if defined(OS_WIN) - ui::ScopedOleInitializer ole_initializer; -#endif - base::CommandLine::Init(argc, argv); - base::CommandLine* command_line = base::CommandLine::ForCurrentProcess(); - - // Disabling Direct Composition works around the limitation that - // InProcessContextFactory doesn't work with Direct Composition, causing the - // window to not render. See http://crbug.com/936249. - command_line->AppendSwitch(switches::kDisableDirectComposition); - - base::FeatureList::InitializeInstance( - command_line->GetSwitchValueASCII(switches::kEnableFeatures), - command_line->GetSwitchValueASCII(switches::kDisableFeatures)); - - base::AtExitManager at_exit; - - mojo::core::Init(); - -#if defined(USE_OZONE) - ui::OzonePlatform::InitParams params; - params.single_process = true; - ui::OzonePlatform::InitializeForGPU(params); -#endif - -#if defined(USE_X11) - // This demo uses InProcessContextFactory which uses X on a separate Gpu - // thread. - gfx::InitializeThreadedX11(); -#endif - - gl::init::InitializeGLOneOff(); - - // The use of base::test::TaskEnvironment below relies on the timeout - // values from TestTimeouts. This ensures they're properly initialized. + // The use of base::test::TaskEnvironment in the following function relies on + // the timeout values from TestTimeouts. TestTimeouts::Initialize(); - // Viz depends on the task environment to correctly tear down. - base::test::TaskEnvironment task_environment( - base::test::TaskEnvironment::MainThreadType::UI); - - // The ContextFactory must exist before any Compositors are created. - viz::HostFrameSinkManager host_frame_sink_manager; - viz::ServerSharedBitmapManager shared_bitmap_manager; - viz::FrameSinkManagerImpl frame_sink_manager(&shared_bitmap_manager); - host_frame_sink_manager.SetLocalManager(&frame_sink_manager); - frame_sink_manager.SetLocalClient(&host_frame_sink_manager); - auto context_factory = std::make_unique<ui::InProcessContextFactory>( - &host_frame_sink_manager, &frame_sink_manager); - context_factory->set_use_test_surface(false); - - base::i18n::InitializeICU(); - - ui::RegisterPathProvider(); - - base::FilePath 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( - g_discardable_memory_allocator.Pointer()); - - base::PowerMonitor::Initialize( - std::make_unique<base::PowerMonitorDeviceSource>()); - - gfx::InitializeFonts(); - -#if defined(USE_AURA) - std::unique_ptr<aura::Env> env = aura::Env::CreateInstance(); - aura::Env::GetInstance()->set_context_factory(context_factory.get()); -#endif - ui::InitializeInputMethodForTesting(); - - { - views::DesktopTestViewsDelegate views_delegate; -#if defined(USE_AURA) - wm::WMState wm_state; -#endif -#if BUILDFLAG(ENABLE_DESKTOP_AURA) - std::unique_ptr<display::Screen> desktop_screen( - views::CreateDesktopScreen()); - display::Screen::SetScreenInstance(desktop_screen.get()); -#endif - - // This app isn't a test and shouldn't timeout. - base::test::ScopedDisableRunLoopTimeout disable_timeout; - - base::RunLoop run_loop; - views::examples::ShowExamplesWindow(run_loop.QuitClosure()); - - run_loop.Run(); - - ui::ResourceBundle::CleanupSharedInstance(); - } - - ui::ShutdownInputMethod(); - -#if defined(USE_AURA) - env.reset(); -#endif + base::AtExitManager at_exit; - return 0; + return static_cast<int>(views::examples::ExamplesMainProc()); } diff --git a/chromium/ui/views/examples/examples_main_proc.cc b/chromium/ui/views/examples/examples_main_proc.cc new file mode 100644 index 00000000000..fe417da38a3 --- /dev/null +++ b/chromium/ui/views/examples/examples_main_proc.cc @@ -0,0 +1,198 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "ui/views/examples/examples_main_proc.h" + +#include <memory> + +#include "base/base_switches.h" +#include "base/bind.h" +#include "base/command_line.h" +#include "base/feature_list.h" +#include "base/files/file_path.h" +#include "base/i18n/icu_util.h" +#include "base/lazy_instance.h" +#include "base/memory/ptr_util.h" +#include "base/path_service.h" +#include "base/power_monitor/power_monitor.h" +#include "base/power_monitor/power_monitor_device_source.h" +#include "base/run_loop.h" +#include "base/test/scoped_run_loop_timeout.h" +#include "base/test/task_environment.h" +#include "base/test/test_discardable_memory_allocator.h" +#include "base/test/test_timeouts.h" +#include "build/build_config.h" +#include "components/viz/host/host_frame_sink_manager.h" +#include "components/viz/service/display_embedder/server_shared_bitmap_manager.h" +#include "components/viz/service/frame_sinks/frame_sink_manager_impl.h" +#include "mojo/core/embedder/embedder.h" +#include "ui/base/ime/init/input_method_initializer.h" +#include "ui/base/resource/resource_bundle.h" +#include "ui/base/ui_base_paths.h" +#include "ui/compositor/compositor_switches.h" +#include "ui/compositor/test/in_process_context_factory.h" +#include "ui/compositor/test/test_context_factories.h" +#include "ui/display/screen.h" +#include "ui/gfx/font_util.h" +#include "ui/gfx/image/image.h" +#include "ui/gl/gl_switches.h" +#include "ui/gl/init/gl_factory.h" +#include "ui/views/buildflags.h" +#include "ui/views/examples/example_base.h" +#include "ui/views/examples/examples_window.h" +#include "ui/views/test/desktop_test_views_delegate.h" +#include "ui/views/widget/any_widget_observer.h" +#include "ui/views/widget/widget.h" + +#if defined(USE_AURA) +#include "ui/aura/env.h" +#include "ui/wm/core/wm_state.h" +#endif + +#if BUILDFLAG(ENABLE_DESKTOP_AURA) +#include "ui/views/widget/desktop_aura/desktop_screen.h" +#endif + +#if defined(OS_WIN) +#include "ui/base/win/scoped_ole_initializer.h" +#include "ui/views/examples/examples_skia_gold_pixel_diff.h" +#endif + +#if defined(USE_OZONE) +#include "ui/ozone/public/ozone_platform.h" +#endif + +namespace views { +namespace examples { + +base::LazyInstance<base::TestDiscardableMemoryAllocator>::DestructorAtExit + g_discardable_memory_allocator = LAZY_INSTANCE_INITIALIZER; + +ExamplesExitCode ExamplesMainProc(bool under_test) { +#if defined(OS_WIN) + ui::ScopedOleInitializer ole_initializer; +#endif + + base::CommandLine* command_line = base::CommandLine::ForCurrentProcess(); + + // Disabling Direct Composition works around the limitation that + // InProcessContextFactory doesn't work with Direct Composition, causing the + // window to not render. See http://crbug.com/936249. + command_line->AppendSwitch(switches::kDisableDirectComposition); + + base::FeatureList::InitializeInstance( + command_line->GetSwitchValueASCII(switches::kEnableFeatures), + command_line->GetSwitchValueASCII(switches::kDisableFeatures)); + + if (under_test) + command_line->AppendSwitch(switches::kEnablePixelOutputInTests); + + mojo::core::Init(); + +#if defined(USE_OZONE) + ui::OzonePlatform::InitParams params; + params.single_process = true; + ui::OzonePlatform::InitializeForGPU(params); +#endif + + gl::init::InitializeGLOneOff(); + + // Viz depends on the task environment to correctly tear down. + base::test::TaskEnvironment task_environment( + base::test::TaskEnvironment::MainThreadType::UI); + + // The ContextFactory must exist before any Compositors are created. + auto context_factories = + std::make_unique<ui::TestContextFactories>(under_test); + context_factories->SetUseTestSurface(false); + + base::i18n::InitializeICU(); + + ui::RegisterPathProvider(); + + base::FilePath ui_test_pak_path; + CHECK(base::PathService::Get(ui::UI_TEST_PAK, &ui_test_pak_path)); + ui::ResourceBundle::InitSharedInstanceWithPakPath(ui_test_pak_path); + + base::FilePath views_examples_resources_pak_path; + CHECK(base::PathService::Get(base::DIR_MODULE, + &views_examples_resources_pak_path)); + ui::ResourceBundle::GetSharedInstance().AddDataPackFromPath( + views_examples_resources_pak_path.AppendASCII( + "views_examples_resources.pak"), + ui::SCALE_FACTOR_100P); + + base::DiscardableMemoryAllocator::SetInstance( + g_discardable_memory_allocator.Pointer()); + + base::PowerMonitor::Initialize( + std::make_unique<base::PowerMonitorDeviceSource>()); + + gfx::InitializeFonts(); + +#if defined(USE_AURA) + std::unique_ptr<aura::Env> env = aura::Env::CreateInstance(); + aura::Env::GetInstance()->set_context_factory( + context_factories->GetContextFactory()); +#endif + ui::InitializeInputMethodForTesting(); + + ExamplesExitCode compare_result = ExamplesExitCode::kSucceeded; + + { + views::DesktopTestViewsDelegate views_delegate; +#if defined(USE_AURA) + wm::WMState wm_state; +#endif +#if BUILDFLAG(ENABLE_DESKTOP_AURA) + std::unique_ptr<display::Screen> desktop_screen = + base::WrapUnique(views::CreateDesktopScreen()); + display::Screen::SetScreenInstance(desktop_screen.get()); +#endif + + base::RunLoop run_loop(base::RunLoop::Type::kNestableTasksAllowed); + +#if defined(OS_WIN) + ExamplesSkiaGoldPixelDiff pixel_diff; + views::AnyWidgetObserver widget_observer{ + views::test::AnyWidgetTestPasskey()}; + + // If this app isn't a test, it shouldn't timeout. + auto disable_timeout = + std::make_unique<base::test::ScopedDisableRunLoopTimeout>(); + + if (under_test) { + pixel_diff.Init("ViewsExamples"); + widget_observer.set_shown_callback( + base::BindRepeating(&ExamplesSkiaGoldPixelDiff::OnExamplesWindowShown, + base::Unretained(&pixel_diff))); + // Enable the timeout since we're not running in a test. + disable_timeout.reset(); + } +#else + base::test::ScopedDisableRunLoopTimeout disable_timeout; +#endif + + views::examples::ShowExamplesWindow(run_loop.QuitClosure()); + + run_loop.Run(); + +#if defined(OS_WIN) + compare_result = pixel_diff.get_result(); +#endif + + ui::ResourceBundle::CleanupSharedInstance(); + } + + ui::ShutdownInputMethod(); + +#if defined(USE_AURA) + env.reset(); +#endif + + return compare_result; +} + +} // namespace examples +} // namespace views diff --git a/chromium/ui/views/examples/examples_main_proc.h b/chromium/ui/views/examples/examples_main_proc.h new file mode 100644 index 00000000000..61b258f262a --- /dev/null +++ b/chromium/ui/views/examples/examples_main_proc.h @@ -0,0 +1,18 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef UI_VIEWS_EXAMPLES_EXAMPLES_MAIN_PROC_H_ +#define UI_VIEWS_EXAMPLES_EXAMPLES_MAIN_PROC_H_ + +#include "ui/views/examples/examples_exit_code.h" + +namespace views { +namespace examples { + +ExamplesExitCode ExamplesMainProc(bool under_test = false); + +} // namespace examples +} // namespace views + +#endif // UI_VIEWS_EXAMPLES_EXAMPLES_MAIN_PROC_H_ diff --git a/chromium/ui/views/examples/examples_skia_gold_pixel_diff.cc b/chromium/ui/views/examples/examples_skia_gold_pixel_diff.cc new file mode 100644 index 00000000000..ffabc89ae2b --- /dev/null +++ b/chromium/ui/views/examples/examples_skia_gold_pixel_diff.cc @@ -0,0 +1,60 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "ui/views/examples/examples_skia_gold_pixel_diff.h" + +#include "base/run_loop.h" +#include "ui/snapshot/snapshot.h" +#include "ui/views/examples/examples_window.h" + +#if defined(USE_AURA) +#include "ui/snapshot/snapshot_aura.h" +#endif + +namespace views { +namespace examples { + +ExamplesSkiaGoldPixelDiff::ExamplesSkiaGoldPixelDiff() = default; +ExamplesSkiaGoldPixelDiff::~ExamplesSkiaGoldPixelDiff() = default; + +ExamplesExitCode ExamplesSkiaGoldPixelDiff::CompareScreenshot( + const std::string& screenshot_name, + const views::Widget* widget) const { + base::RunLoop run_loop(base::RunLoop::Type::kNestableTasksAllowed); + gfx::Rect widget_bounds = widget->GetRootView()->bounds(); +#if defined(USE_AURA) + ui::GrabWindowSnapshotAsyncAura( +#else + ui::GrabWindowSnapshotAsync( +#endif + widget->GetNativeWindow(), widget_bounds, + base::BindOnce( + [](gfx::Image* screenshot, base::OnceClosure quit_loop, + gfx::Image image) { + *screenshot = image; + std::move(quit_loop).Run(); + }, + &screenshot_, run_loop.QuitClosure())); + run_loop.Run(); + if (screenshot_.IsEmpty()) + return ExamplesExitCode::kImageEmpty; + return SkiaGoldPixelDiff::CompareScreenshot(screenshot_name, + *screenshot_.ToSkBitmap()) + ? ExamplesExitCode::kSucceeded + : ExamplesExitCode::kFailed; +} + +void ExamplesSkiaGoldPixelDiff::DoScreenshot(views::Widget* widget) { + result_ = CompareScreenshot("ExampleWindow", widget); + widget->Close(); +} + +void ExamplesSkiaGoldPixelDiff::OnExamplesWindowShown(views::Widget* widget) { + if (widget->GetName() == views::examples::kExamplesWidgetName) { + DoScreenshot(widget); + } +} + +} // namespace examples +} // namespace views diff --git a/chromium/ui/views/examples/examples_skia_gold_pixel_diff.h b/chromium/ui/views/examples/examples_skia_gold_pixel_diff.h new file mode 100644 index 00000000000..4bb102d97eb --- /dev/null +++ b/chromium/ui/views/examples/examples_skia_gold_pixel_diff.h @@ -0,0 +1,39 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef UI_VIEWS_EXAMPLES_EXAMPLES_SKIA_GOLD_PIXEL_DIFF_H_ +#define UI_VIEWS_EXAMPLES_EXAMPLES_SKIA_GOLD_PIXEL_DIFF_H_ + +#include "ui/base/test/skia_gold_pixel_diff.h" + +#include "base/run_loop.h" +#include "ui/gfx/image/image.h" +#include "ui/views/examples/examples_exit_code.h" +#include "ui/views/widget/widget.h" + +namespace views { +namespace examples { + +class ExamplesSkiaGoldPixelDiff : public SkiaGoldPixelDiff { + public: + ExamplesSkiaGoldPixelDiff(); + ~ExamplesSkiaGoldPixelDiff() override; + + void OnExamplesWindowShown(views::Widget* widget); + + ExamplesExitCode get_result() const { return result_; } + + private: + ExamplesExitCode CompareScreenshot(const std::string& screenshot_name, + const views::Widget* widget) const; + void DoScreenshot(views::Widget* widget); + + mutable gfx::Image screenshot_; + ExamplesExitCode result_ = ExamplesExitCode::kNone; +}; + +} // namespace examples +} // namespace views + +#endif // UI_VIEWS_EXAMPLES_EXAMPLES_SKIA_GOLD_PIXEL_DIFF_H_ diff --git a/chromium/ui/views/examples/examples_unittest.cc b/chromium/ui/views/examples/examples_unittest.cc new file mode 100644 index 00000000000..054ef30a83f --- /dev/null +++ b/chromium/ui/views/examples/examples_unittest.cc @@ -0,0 +1,26 @@ +// Copyright 2020 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 "build/build_config.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "ui/views/examples/examples_exit_code.h" +#include "ui/views/examples/examples_main_proc.h" + +namespace views { +namespace examples { + +#if defined(OS_WIN) +#define TestViewsExamplesLaunches_MAYBE TestViewsExamplesLaunches +#else +#define TestViewsExamplesLaunches_MAYBE TestViewsExamplesLaunches_DISABLED +#endif + +TEST(ExamplesTest, TestViewsExamplesLaunches_MAYBE) { + const ExamplesExitCode exit_code = ExamplesMainProc(true); + // Check the status of the Skia Gold comparison. + EXPECT_EQ(ExamplesExitCode::kSucceeded, exit_code); +} + +} // namespace examples +} // namespace views diff --git a/chromium/ui/views/examples/examples_unittest_main.cc b/chromium/ui/views/examples/examples_unittest_main.cc new file mode 100644 index 00000000000..65ed57e7fd7 --- /dev/null +++ b/chromium/ui/views/examples/examples_unittest_main.cc @@ -0,0 +1,14 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/bind.h" +#include "base/test/launcher/unit_test_launcher.h" +#include "base/test/test_suite.h" + +int main(int argc, char** argv) { + base::TestSuite test_suite(argc, argv); + return base::LaunchUnitTests( + argc, argv, + base::BindOnce(&base::TestSuite::Run, base::Unretained(&test_suite))); +} diff --git a/chromium/ui/views/examples/examples_window.cc b/chromium/ui/views/examples/examples_window.cc index 4956b07f3ac..d2cde9bb4e0 100644 --- a/chromium/ui/views/examples/examples_window.cc +++ b/chromium/ui/views/examples/examples_window.cc @@ -10,43 +10,20 @@ #include <string> #include <utility> +#include "base/command_line.h" #include "base/macros.h" #include "base/run_loop.h" +#include "base/stl_util.h" +#include "base/strings/string_split.h" #include "base/strings/utf_string_conversions.h" #include "ui/base/l10n/l10n_util.h" #include "ui/base/models/combobox_model.h" #include "ui/base/ui_base_paths.h" #include "ui/views/background.h" #include "ui/views/controls/combobox/combobox.h" +#include "ui/views/controls/combobox/combobox_listener.h" #include "ui/views/controls/label.h" -#include "ui/views/examples/ax_example.h" -#include "ui/views/examples/box_layout_example.h" -#include "ui/views/examples/bubble_example.h" -#include "ui/views/examples/button_example.h" -#include "ui/views/examples/button_sticker_sheet.h" -#include "ui/views/examples/checkbox_example.h" -#include "ui/views/examples/combobox_example.h" -#include "ui/views/examples/dialog_example.h" -#include "ui/views/examples/flex_layout_example.h" -#include "ui/views/examples/label_example.h" -#include "ui/views/examples/link_example.h" -#include "ui/views/examples/menu_example.h" -#include "ui/views/examples/message_box_example.h" -#include "ui/views/examples/multiline_example.h" -#include "ui/views/examples/native_theme_example.h" -#include "ui/views/examples/progress_bar_example.h" -#include "ui/views/examples/radio_button_example.h" -#include "ui/views/examples/scroll_view_example.h" -#include "ui/views/examples/slider_example.h" -#include "ui/views/examples/tabbed_pane_example.h" -#include "ui/views/examples/table_example.h" -#include "ui/views/examples/text_example.h" -#include "ui/views/examples/textfield_example.h" -#include "ui/views/examples/throbber_example.h" -#include "ui/views/examples/toggle_button_example.h" -#include "ui/views/examples/tree_view_example.h" -#include "ui/views/examples/vector_example.h" -#include "ui/views/examples/widget_example.h" +#include "ui/views/examples/create_examples.h" #include "ui/views/layout/fill_layout.h" #include "ui/views/layout/grid_layout.h" #include "ui/views/widget/widget.h" @@ -55,55 +32,49 @@ namespace views { namespace examples { -using ExampleVector = std::vector<std::unique_ptr<ExampleBase>>; +const char kExamplesWidgetName[] = "ExamplesWidget"; namespace { -// Creates the default set of examples. -ExampleVector CreateExamples() { - ExampleVector examples; - examples.push_back(std::make_unique<AxExample>()); - examples.push_back(std::make_unique<BoxLayoutExample>()); - examples.push_back(std::make_unique<BubbleExample>()); - examples.push_back(std::make_unique<ButtonExample>()); - examples.push_back(std::make_unique<ButtonStickerSheet>()); - examples.push_back(std::make_unique<CheckboxExample>()); - examples.push_back(std::make_unique<ComboboxExample>()); - examples.push_back(std::make_unique<DialogExample>()); - examples.push_back(std::make_unique<FlexLayoutExample>()); - examples.push_back(std::make_unique<LabelExample>()); - examples.push_back(std::make_unique<LinkExample>()); - examples.push_back(std::make_unique<MenuExample>()); - examples.push_back(std::make_unique<MessageBoxExample>()); - examples.push_back(std::make_unique<MultilineExample>()); - examples.push_back(std::make_unique<NativeThemeExample>()); - examples.push_back(std::make_unique<ProgressBarExample>()); - examples.push_back(std::make_unique<RadioButtonExample>()); - examples.push_back(std::make_unique<ScrollViewExample>()); - examples.push_back(std::make_unique<SliderExample>()); - examples.push_back(std::make_unique<TabbedPaneExample>()); - examples.push_back(std::make_unique<TableExample>()); - examples.push_back(std::make_unique<TextExample>()); - examples.push_back(std::make_unique<TextfieldExample>()); - examples.push_back(std::make_unique<ToggleButtonExample>()); - examples.push_back(std::make_unique<ThrobberExample>()); - examples.push_back(std::make_unique<TreeViewExample>()); - examples.push_back(std::make_unique<VectorExample>()); - examples.push_back(std::make_unique<WidgetExample>()); - return examples; -} +const char kEnableExamples[] = "enable-examples"; + +ExampleVector GetExamplesToShow(ExampleVector examples) { + using StringVector = std::vector<std::string>; + base::CommandLine* command_line = base::CommandLine::ForCurrentProcess(); -struct ExampleTitleCompare { - bool operator()(const std::unique_ptr<ExampleBase>& a, - const std::unique_ptr<ExampleBase>& b) { + std::sort(examples.begin(), examples.end(), [](const auto& a, const auto& b) { return a->example_title() < b->example_title(); + }); + + std::string enable_examples = + command_line->GetSwitchValueASCII(kEnableExamples); + if (!enable_examples.empty()) { + StringVector enabled = + base::SplitString(enable_examples, ";,", base::TRIM_WHITESPACE, + base::SPLIT_WANT_NONEMPTY); + + // Transform list of examples to just the list of names. + StringVector example_names; + std::transform( + examples.begin(), examples.end(), std::back_inserter(example_names), + [](const auto& example) { return example->example_title(); }); + + std::sort(enabled.begin(), enabled.end()); + + // Get an intersection of list of titles between the full list and the list + // from the command-line. + StringVector valid_examples = + base::STLSetIntersection<StringVector>(enabled, example_names); + + // If there are still example names in the list, only include the examples + // from the list. + if (!valid_examples.empty()) { + base::EraseIf(examples, [valid_examples](auto& example) { + return std::find(valid_examples.begin(), valid_examples.end(), + example->example_title()) == valid_examples.end(); + }); + } } -}; - -ExampleVector GetExamplesToShow(ExampleVector extra) { - ExampleVector examples = CreateExamples(); - std::move(extra.begin(), extra.end(), std::back_inserter(examples)); - std::sort(examples.begin(), examples.end(), ExampleTitleCompare()); for (auto& example : examples) example->CreateExampleView(example->example_view()); @@ -158,13 +129,15 @@ class ExamplesWindowContents : public WidgetDelegateView, ColumnSet* column_set = layout->AddColumnSet(0); column_set->AddPaddingColumn(0, 5); column_set->AddColumn(GridLayout::FILL, GridLayout::FILL, 1, - GridLayout::USE_PREF, 0, 0); + GridLayout::ColumnSize::kUsePreferred, 0, 0); column_set->AddPaddingColumn(0, 5); layout->AddPaddingRow(0, 5); layout->StartRow(0 /* no expand */, 0); combobox_ = layout->AddView(std::move(combobox)); - if (combobox_model_->GetItemCount() > 0) { + auto item_count = combobox_model_->GetItemCount(); + if (item_count > 0) { + combobox_->SetVisible(item_count > 1); layout->StartRow(1, 0); auto example_shown = std::make_unique<View>(); example_shown->SetLayoutManager(std::make_unique<FillLayout>()); @@ -236,17 +209,18 @@ class ExamplesWindowContents : public WidgetDelegateView, ExamplesWindowContents* ExamplesWindowContents::instance_ = nullptr; void ShowExamplesWindow(base::OnceClosure on_close, - gfx::NativeWindow window_context, - ExampleVector extra_examples) { + ExampleVector examples, + gfx::NativeWindow window_context) { if (ExamplesWindowContents::instance()) { ExamplesWindowContents::instance()->GetWidget()->Activate(); } else { - ExampleVector examples = GetExamplesToShow(std::move(extra_examples)); + examples = GetExamplesToShow(std::move(examples)); Widget* widget = new Widget; Widget::InitParams params; params.delegate = new ExamplesWindowContents(std::move(on_close), std::move(examples)); params.context = window_context; + params.name = kExamplesWidgetName; widget->Init(std::move(params)); widget->Show(); } diff --git a/chromium/ui/views/examples/examples_window.h b/chromium/ui/views/examples/examples_window.h index d7198df6a3d..93cff092763 100644 --- a/chromium/ui/views/examples/examples_window.h +++ b/chromium/ui/views/examples/examples_window.h @@ -9,23 +9,25 @@ #include <string> #include <vector> +#include "base/callback_forward.h" #include "base/strings/stringprintf.h" #include "ui/gfx/native_widget_types.h" +#include "ui/views/examples/create_examples.h" +#include "ui/views/examples/example_base.h" #include "ui/views/examples/views_examples_export.h" namespace views { namespace examples { -class VIEWS_EXAMPLES_EXPORT ExampleBase; +VIEWS_EXAMPLES_EXPORT extern const char kExamplesWidgetName[]; // Shows a window with the views examples in it. |extra_examples| contains any // additional examples to add. |window_context| is used to determine where the // window should be created (see |Widget::InitParams::context| for details). VIEWS_EXAMPLES_EXPORT void ShowExamplesWindow( base::OnceClosure on_close, - gfx::NativeWindow window_context = nullptr, - std::vector<std::unique_ptr<ExampleBase>> extra_examples = - std::vector<std::unique_ptr<ExampleBase>>()); + ExampleVector examples = CreateExamples(), + gfx::NativeWindow window_context = nullptr); // Prints |string| in the status area, at the bottom of the window. VIEWS_EXAMPLES_EXPORT void LogStatus(const std::string& string); diff --git a/chromium/ui/views/examples/examples_window_with_content.cc b/chromium/ui/views/examples/examples_window_with_content.cc index c9391fa8bd8..009108cebad 100644 --- a/chromium/ui/views/examples/examples_window_with_content.cc +++ b/chromium/ui/views/examples/examples_window_with_content.cc @@ -9,6 +9,8 @@ #include <vector> #include "content/public/browser/browser_context.h" +#include "ui/views/examples/create_examples.h" +#include "ui/views/examples/example_base.h" #include "ui/views/examples/webview_example.h" namespace views { @@ -17,10 +19,10 @@ namespace examples { void ShowExamplesWindowWithContent(base::OnceClosure on_close, content::BrowserContext* browser_context, gfx::NativeWindow window_context) { - std::vector<std::unique_ptr<ExampleBase>> extra_examples; - extra_examples.push_back(std::make_unique<WebViewExample>(browser_context)); - ShowExamplesWindow(std::move(on_close), window_context, - std::move(extra_examples)); + ExampleVector examples; + examples.push_back(std::make_unique<WebViewExample>(browser_context)); + ShowExamplesWindow(std::move(on_close), CreateExamples(std::move(examples)), + window_context); } } // namespace examples diff --git a/chromium/ui/views/examples/examples_window_with_content.h b/chromium/ui/views/examples/examples_window_with_content.h index dbcb1fbd806..26ad63ac75e 100644 --- a/chromium/ui/views/examples/examples_window_with_content.h +++ b/chromium/ui/views/examples/examples_window_with_content.h @@ -5,6 +5,7 @@ #ifndef UI_VIEWS_EXAMPLES_EXAMPLES_WINDOW_WITH_CONTENT_H_ #define UI_VIEWS_EXAMPLES_EXAMPLES_WINDOW_WITH_CONTENT_H_ +#include "base/callback_forward.h" #include "ui/gfx/native_widget_types.h" #include "ui/views/examples/examples_window.h" #include "ui/views/examples/views_examples_with_content_export.h" diff --git a/chromium/ui/views/examples/examples_with_content_main_exe.cc b/chromium/ui/views/examples/examples_with_content_main.cc index 7f978cae9ec..501a2fb7131 100644 --- a/chromium/ui/views/examples/examples_with_content_main_exe.cc +++ b/chromium/ui/views/examples/examples_with_content_main.cc @@ -3,11 +3,19 @@ // found in the LICENSE file. #include "base/bind.h" +#include "base/command_line.h" +#include "base/files/file_path.h" +#include "base/path_service.h" #include "build/build_config.h" #include "content/public/browser/browser_context.h" +#include "ui/base/resource/resource_bundle.h" #include "ui/views/examples/examples_window_with_content.h" #include "ui/views_content_client/views_content_client.h" +#if defined(OS_MACOSX) +#include "sandbox/mac/seatbelt_exec.h" +#endif + #if defined(OS_WIN) #include "content/public/app/sandbox_helper_win.h" #include "sandbox/win/src/sandbox_types.h" @@ -15,6 +23,16 @@ namespace { +void OnResourcesLoaded() { + base::FilePath views_examples_resources_pak_path; + CHECK(base::PathService::Get(base::DIR_MODULE, + &views_examples_resources_pak_path)); + ui::ResourceBundle::GetSharedInstance().AddDataPackFromPath( + views_examples_resources_pak_path.AppendASCII( + "views_examples_resources.pak"), + ui::SCALE_FACTOR_100P); +} + void ShowContentExampleWindow(ui::ViewsContentClient* views_content_client, content::BrowserContext* browser_context, gfx::NativeWindow window_context) { @@ -43,9 +61,26 @@ int APIENTRY wWinMain(HINSTANCE instance, HINSTANCE, wchar_t*, int) { ui::ViewsContentClient views_content_client(instance, &sandbox_info); #else int main(int argc, const char** argv) { + base::CommandLine::Init(argc, argv); ui::ViewsContentClient views_content_client(argc, argv); #endif +#if defined(OS_MACOSX) + base::CommandLine* command_line = base::CommandLine::ForCurrentProcess(); + // ViewsContentClient expects a const char** argv and + // CreateFromArgumentsResult expects a regular char** argv. Given this is a + // test program, a refactor from either end didn't seem worth it. As a result, + // use a const_cast instead. + sandbox::SeatbeltExecServer::CreateFromArgumentsResult seatbelt = + sandbox::SeatbeltExecServer::CreateFromArguments( + command_line->GetProgram().value().c_str(), argc, + const_cast<char**>(argv)); + if (seatbelt.sandbox_required) + CHECK(seatbelt.server->InitializeSandbox()); +#endif + + views_content_client.set_on_resources_loaded_callback( + base::BindOnce(&OnResourcesLoaded)); views_content_client.set_on_pre_main_message_loop_run_callback(base::BindOnce( &ShowContentExampleWindow, base::Unretained(&views_content_client))); return views_content_client.RunMain(); diff --git a/chromium/ui/views/examples/flex_layout_example.cc b/chromium/ui/views/examples/flex_layout_example.cc index 3049fed0c78..2dedbc6a592 100644 --- a/chromium/ui/views/examples/flex_layout_example.cc +++ b/chromium/ui/views/examples/flex_layout_example.cc @@ -38,12 +38,12 @@ void FlexLayoutExample::CreateAdditionalControls(int vertical_pos) { static const char* const cross_axis_values[4] = {"Stretch", "Start", "Center", "End"}; - orientation_ = CreateCombobox(base::ASCIIToUTF16("Orientation"), - orientation_values, 2, &vertical_pos); - main_axis_alignment_ = CreateCombobox(base::ASCIIToUTF16("Main axis"), - main_axis_values, 3, &vertical_pos); - cross_axis_alignment_ = CreateCombobox(base::ASCIIToUTF16("Cross axis"), - cross_axis_values, 4, &vertical_pos); + orientation_ = CreateAndAddCombobox(base::ASCIIToUTF16("Orientation"), + orientation_values, 2, &vertical_pos); + main_axis_alignment_ = CreateAndAddCombobox( + base::ASCIIToUTF16("Main axis"), main_axis_values, 3, &vertical_pos); + cross_axis_alignment_ = CreateAndAddCombobox( + base::ASCIIToUTF16("Cross axis"), cross_axis_values, 4, &vertical_pos); CreateMarginsTextFields(base::ASCIIToUTF16("Interior margin"), &interior_margin_, &vertical_pos); @@ -51,10 +51,10 @@ void FlexLayoutExample::CreateAdditionalControls(int vertical_pos) { CreateMarginsTextFields(base::ASCIIToUTF16("Default margins"), &default_child_margins_, &vertical_pos); - collapse_margins_ = - CreateCheckbox(base::ASCIIToUTF16("Collapse margins"), &vertical_pos); + collapse_margins_ = CreateAndAddCheckbox( + base::ASCIIToUTF16("Collapse margins"), &vertical_pos); - ignore_default_main_axis_margins_ = CreateCheckbox( + ignore_default_main_axis_margins_ = CreateAndAddCheckbox( base::ASCIIToUTF16("Ignore main axis margins"), &vertical_pos); layout_ = layout_panel()->SetLayoutManager(std::make_unique<FlexLayout>()); diff --git a/chromium/ui/views/examples/label_example.cc b/chromium/ui/views/examples/label_example.cc index 8ec7651301f..61c25bb995e 100644 --- a/chromium/ui/views/examples/label_example.cc +++ b/chromium/ui/views/examples/label_example.cc @@ -167,9 +167,9 @@ void LabelExample::AddCustomLabel(View* container) { ColumnSet* column_set = layout->AddColumnSet(0); column_set->AddColumn(GridLayout::LEADING, GridLayout::FILL, 0.0f, - GridLayout::USE_PREF, 0, 0); + GridLayout::ColumnSize::kUsePreferred, 0, 0); column_set->AddColumn(GridLayout::FILL, GridLayout::FILL, 1.0f, - GridLayout::USE_PREF, 0, 0); + GridLayout::ColumnSize::kUsePreferred, 0, 0); layout->StartRow(0, 0); layout->AddView(std::make_unique<Label>(ASCIIToUTF16("Content: "))); @@ -189,11 +189,11 @@ void LabelExample::AddCustomLabel(View* container) { column_set = layout->AddColumnSet(1); column_set->AddColumn(GridLayout::LEADING, GridLayout::LEADING, 0, - GridLayout::USE_PREF, 0, 0); + GridLayout::ColumnSize::kUsePreferred, 0, 0); column_set->AddColumn(GridLayout::LEADING, GridLayout::LEADING, 0, - GridLayout::USE_PREF, 0, 0); + GridLayout::ColumnSize::kUsePreferred, 0, 0); column_set->AddColumn(GridLayout::LEADING, GridLayout::LEADING, 0, - GridLayout::USE_PREF, 0, 0); + GridLayout::ColumnSize::kUsePreferred, 0, 0); layout->StartRow(0, 1); multiline_ = layout->AddView( std::make_unique<Checkbox>(base::ASCIIToUTF16("Multiline"), this)); @@ -205,7 +205,7 @@ void LabelExample::AddCustomLabel(View* container) { column_set = layout->AddColumnSet(2); column_set->AddColumn(GridLayout::FILL, GridLayout::FILL, 1, - GridLayout::USE_PREF, 0, 0); + GridLayout::ColumnSize::kUsePreferred, 0, 0); layout->StartRow(0, 2); auto custom_label = std::make_unique<ExamplePreferredSizeLabel>(); custom_label->SetHorizontalAlignment(gfx::ALIGN_LEFT); diff --git a/chromium/ui/views/examples/layout_example_base.cc b/chromium/ui/views/examples/layout_example_base.cc index bb577f56a50..a2e85e23749 100644 --- a/chromium/ui/views/examples/layout_example_base.cc +++ b/chromium/ui/views/examples/layout_example_base.cc @@ -11,12 +11,14 @@ #include "base/strings/string16.h" #include "base/strings/string_number_conversions.h" #include "base/strings/utf_string_conversions.h" +#include "ui/base/l10n/l10n_util.h" #include "ui/views/border.h" #include "ui/views/controls/button/checkbox.h" #include "ui/views/controls/button/md_text_button.h" #include "ui/views/controls/combobox/combobox.h" #include "ui/views/controls/textfield/textfield.h" #include "ui/views/examples/example_combobox_model.h" +#include "ui/views/examples/grit/views_examples_resources.h" #include "ui/views/layout/box_layout.h" #include "ui/views/layout/fill_layout.h" #include "ui/views/view_class_properties.h" @@ -45,6 +47,20 @@ class FullPanel : public View { DISALLOW_COPY_AND_ASSIGN(FullPanel); }; +std::unique_ptr<Textfield> CreateCommonTextfield( + int vertical_pos, + int horizontal_pos, + TextfieldController* container) { + auto textfield = std::make_unique<Textfield>(); + textfield->SetPosition(gfx::Point(horizontal_pos, vertical_pos)); + textfield->SetDefaultWidthInChars(3); + textfield->SetTextInputType(ui::TEXT_INPUT_TYPE_NUMBER); + textfield->SizeToPreferredSize(); + textfield->SetText(base::ASCIIToUTF16("0")); + textfield->set_controller(container); + return textfield; +} + } // namespace LayoutExampleBase::ChildPanel::ChildPanel(LayoutExampleBase* example) @@ -125,146 +141,113 @@ void LayoutExampleBase::ChildPanel::ContentsChanged( } Textfield* LayoutExampleBase::ChildPanel::CreateTextfield() { - Textfield* textfield = new Textfield(); + auto textfield = std::make_unique<Textfield>(); textfield->SetDefaultWidthInChars(3); textfield->SizeToPreferredSize(); textfield->SetText(base::ASCIIToUTF16("0")); textfield->set_controller(this); textfield->SetVisible(false); - AddChildView(textfield); - return textfield; + return AddChildView(std::move(textfield)); } LayoutExampleBase::LayoutExampleBase(const char* title) : ExampleBase(title) {} LayoutExampleBase::~LayoutExampleBase() = default; -Combobox* LayoutExampleBase::CreateCombobox(const base::string16& label_text, - const char* const* items, - int count, - int* vertical_pos) { - Label* label = new Label(label_text); +Combobox* LayoutExampleBase::CreateAndAddCombobox( + const base::string16& label_text, + const char* const* items, + int count, + int* vertical_pos) { + auto label = std::make_unique<Label>(label_text); label->SetPosition(gfx::Point(kLayoutExampleLeftPadding, *vertical_pos)); label->SizeToPreferredSize(); - Combobox* combo_box = - new Combobox(std::make_unique<ExampleComboboxModel>(items, count)); + + auto combo_box = std::make_unique<Combobox>( + std::make_unique<ExampleComboboxModel>(items, count)); combo_box->SetPosition( gfx::Point(label->x() + label->width() + kLayoutExampleVerticalSpacing, *vertical_pos)); combo_box->SizeToPreferredSize(); combo_box->set_listener(this); label->SetSize(gfx::Size(label->width(), combo_box->height())); - control_panel_->AddChildView(label); - control_panel_->AddChildView(combo_box); - *vertical_pos += combo_box->height() + kLayoutExampleVerticalSpacing; - return combo_box; -} + control_panel_->AddChildView(std::move(label)); -Textfield* LayoutExampleBase::CreateRawTextfield(int vertical_pos, - bool add, - int* horizontal_pos) { - Textfield* text_field = new Textfield(); - text_field->SetPosition(gfx::Point(*horizontal_pos, vertical_pos)); - text_field->SetDefaultWidthInChars(3); - text_field->SetTextInputType(ui::TEXT_INPUT_TYPE_NUMBER); - text_field->SizeToPreferredSize(); - text_field->SetText(base::ASCIIToUTF16("0")); - text_field->set_controller(this); - *horizontal_pos += text_field->width() + kLayoutExampleVerticalSpacing; - if (add) - control_panel_->AddChildView(text_field); - return text_field; + auto* combo_box_ptr = control_panel_->AddChildView(std::move(combo_box)); + *vertical_pos += combo_box_ptr->height() + kLayoutExampleVerticalSpacing; + return combo_box_ptr; } -Textfield* LayoutExampleBase::CreateTextfield(const base::string16& label_text, - int* vertical_pos) { - Label* label = new Label(label_text); +Textfield* LayoutExampleBase::CreateAndAddTextfield( + const base::string16& label_text, + int* vertical_pos) { + auto label = std::make_unique<Label>(label_text); label->SetPosition(gfx::Point(kLayoutExampleLeftPadding, *vertical_pos)); label->SizeToPreferredSize(); int horizontal_pos = label->x() + label->width() + kLayoutExampleVerticalSpacing; - Textfield* text_field = - CreateRawTextfield(*vertical_pos, false, &horizontal_pos); - label->SetSize(gfx::Size(label->width(), text_field->height())); - control_panel_->AddChildView(label); - control_panel_->AddChildView(text_field); - *vertical_pos += text_field->height() + kLayoutExampleVerticalSpacing; - return text_field; -} - -void LayoutExampleBase::CreateMarginsTextFields( - const base::string16& label_text, - Textfield* textfields[4], - int* vertical_pos) { - textfields[0] = CreateTextfield(label_text, vertical_pos); - int center = textfields[0]->x() + textfields[0]->width() / 2; - int horizontal_pos = std::max( - 0, center - (textfields[0]->width() + kLayoutExampleVerticalSpacing / 2)); - textfields[1] = CreateRawTextfield(*vertical_pos, true, &horizontal_pos); - textfields[3] = CreateRawTextfield(*vertical_pos, true, &horizontal_pos); - *vertical_pos = textfields[1]->y() + textfields[1]->height() + - kLayoutExampleVerticalSpacing; - horizontal_pos = textfields[0]->x(); - textfields[2] = CreateRawTextfield(*vertical_pos, true, &horizontal_pos); - *vertical_pos = textfields[2]->y() + textfields[2]->height() + - kLayoutExampleVerticalSpacing; + std::unique_ptr<Textfield> textfield = + CreateCommonTextfield(*vertical_pos, horizontal_pos, this); + label->SetSize(gfx::Size(label->width(), textfield->height())); + control_panel_->AddChildView(std::move(label)); + auto* textfield_ptr = control_panel_->AddChildView(std::move(textfield)); + *vertical_pos += textfield_ptr->height() + kLayoutExampleVerticalSpacing; + return textfield_ptr; } void LayoutExampleBase::CreateMarginsTextFields( const base::string16& label_text, InsetTextfields* textfields, int* vertical_pos) { - textfields->top = CreateTextfield(label_text, vertical_pos); + textfields->top = CreateAndAddTextfield(label_text, vertical_pos); int center = textfields->top->x() + textfields->top->width() / 2; int horizontal_pos = std::max( 0, center - (textfields->top->width() + kLayoutExampleVerticalSpacing / 2)); - textfields->left = CreateRawTextfield(*vertical_pos, true, &horizontal_pos); - textfields->right = CreateRawTextfield(*vertical_pos, true, &horizontal_pos); + textfields->left = CreateAndAddRawTextfield(*vertical_pos, &horizontal_pos); + textfields->right = CreateAndAddRawTextfield(*vertical_pos, &horizontal_pos); *vertical_pos = textfields->left->y() + textfields->left->height() + kLayoutExampleVerticalSpacing; horizontal_pos = textfields->top->x(); - textfields->bottom = CreateRawTextfield(*vertical_pos, true, &horizontal_pos); + textfields->bottom = CreateAndAddRawTextfield(*vertical_pos, &horizontal_pos); *vertical_pos = textfields->bottom->y() + textfields->bottom->height() + kLayoutExampleVerticalSpacing; } -Checkbox* LayoutExampleBase::CreateCheckbox(const base::string16& label_text, - int* vertical_pos) { - Checkbox* checkbox = new Checkbox(label_text, this); +Checkbox* LayoutExampleBase::CreateAndAddCheckbox( + const base::string16& label_text, + int* vertical_pos) { + auto checkbox = std::make_unique<Checkbox>(label_text, this); checkbox->SetPosition(gfx::Point(kLayoutExampleLeftPadding, *vertical_pos)); checkbox->SizeToPreferredSize(); - control_panel_->AddChildView(checkbox); - *vertical_pos += checkbox->height() + kLayoutExampleVerticalSpacing; - return checkbox; + auto* checkbox_ptr = control_panel_->AddChildView(std::move(checkbox)); + *vertical_pos += checkbox_ptr->height() + kLayoutExampleVerticalSpacing; + return checkbox_ptr; } void LayoutExampleBase::CreateExampleView(View* container) { container->SetLayoutManager(std::make_unique<FillLayout>()); - View* full_panel = new FullPanel(); - container->AddChildView(full_panel); + View* full_panel = container->AddChildView(std::make_unique<FullPanel>()); auto* manager = full_panel->SetLayoutManager( std::make_unique<BoxLayout>(views::BoxLayout::Orientation::kHorizontal)); - layout_panel_ = new View(); + layout_panel_ = full_panel->AddChildView(std::make_unique<View>()); layout_panel_->SetBorder(CreateSolidBorder(1, SK_ColorLTGRAY)); - full_panel->AddChildView(layout_panel_); manager->SetFlexForView(layout_panel_, 3); - control_panel_ = new View(); - full_panel->AddChildView(control_panel_); + control_panel_ = full_panel->AddChildView(std::make_unique<View>()); manager->SetFlexForView(control_panel_, 1); int vertical_pos = kLayoutExampleVerticalSpacing; int horizontal_pos = kLayoutExampleLeftPadding; - auto add_button = - MdTextButton::CreateSecondaryUiButton(this, base::ASCIIToUTF16("Add")); + auto add_button = MdTextButton::Create( + this, l10n_util::GetStringUTF16(IDS_LAYOUT_BASE_ADD_LABEL)); add_button->SetPosition(gfx::Point(horizontal_pos, vertical_pos)); add_button->SizeToPreferredSize(); add_button_ = control_panel_->AddChildView(std::move(add_button)); horizontal_pos += add_button_->width() + kLayoutExampleVerticalSpacing; preferred_width_view_ = - CreateRawTextfield(vertical_pos, true, &horizontal_pos); + CreateAndAddRawTextfield(vertical_pos, &horizontal_pos); preferred_width_view_->SetY( vertical_pos + (add_button_->height() - preferred_width_view_->height()) / 2); @@ -272,7 +255,7 @@ void LayoutExampleBase::CreateExampleView(View* container) { base::NumberToString16(kLayoutExampleDefaultChildSize.width())); preferred_height_view_ = - CreateRawTextfield(vertical_pos, true, &horizontal_pos); + CreateAndAddRawTextfield(vertical_pos, &horizontal_pos); preferred_height_view_->SetY( vertical_pos + (add_button_->height() - preferred_height_view_->height()) / 2); @@ -339,5 +322,13 @@ gfx::Insets LayoutExampleBase::TextfieldsToInsets( std::max(0, right)); } +Textfield* LayoutExampleBase::CreateAndAddRawTextfield(int vertical_pos, + int* horizontal_pos) { + std::unique_ptr<Textfield> textfield = + CreateCommonTextfield(vertical_pos, *horizontal_pos, this); + *horizontal_pos += textfield->width() + kLayoutExampleVerticalSpacing; + return control_panel_->AddChildView(std::move(textfield)); +} + } // namespace examples } // namespace views diff --git a/chromium/ui/views/examples/layout_example_base.h b/chromium/ui/views/examples/layout_example_base.h index a6d1db36c49..7e41f985931 100644 --- a/chromium/ui/views/examples/layout_example_base.h +++ b/chromium/ui/views/examples/layout_example_base.h @@ -81,32 +81,17 @@ class VIEWS_EXAMPLES_EXPORT LayoutExampleBase : public ExampleBase, protected: View* layout_panel() { return layout_panel_; } - // Creates a Combobox with a label with |label_text| to the left. Adjust - // |vertical_pos| to |vertical_pos| + combo_box->height() + kSpacing. - Combobox* CreateCombobox(const base::string16& label_text, - const char* const* items, - int count, - int* vertical_pos); - - // Creates just a Textfield at the current position of |horizontal_pos| and - // |vertical_pos|. Update |horizontal_pos| to |horizontal_pos| + - // text_field->width() + kSpacing. - Textfield* CreateRawTextfield(int vertical_pos, - bool add, - int* horizontal_pos); - - // Creates a Textfield with a label with |label_text| to the left. Adjust - // |vertical_pos| to |vertical_pos| + combo_box->height() + kSpacing. - Textfield* CreateTextfield(const base::string16& label_text, - int* vertical_pos); - - // Creates a set of labeled Textfields with |label_text|, and four text fields - // arranged at compass points representing a set of insets. |vertical_pos| is - // updated to the bottom of the last Textfield + kSpacing, and |textfields| is - // populated with the fieds that are created. - void CreateMarginsTextFields(const base::string16& label_text, - Textfield* textfields[4], - int* vertical_pos); + // Creates and adds a Combobox with a label with |label_text| to the left. + // Adjust |vertical_pos| to |vertical_pos| + combo_box->height() + kSpacing. + Combobox* CreateAndAddCombobox(const base::string16& label_text, + const char* const* items, + int count, + int* vertical_pos); + + // Creates and adds a Textfield with a label with |label_text| to the left. + // Adjusts |vertical_pos| to |vertical_pos| + combo_box->height() + kSpacing. + Textfield* CreateAndAddTextfield(const base::string16& label_text, + int* vertical_pos); // Creates a set of labeled Textfields with |label_text|, and four text fields // arranged at compass points representing a set of insets. |vertical_pos| is @@ -116,9 +101,10 @@ class VIEWS_EXAMPLES_EXPORT LayoutExampleBase : public ExampleBase, InsetTextfields* textfields, int* vertical_pos); - // Creates a Checkbox with label |label_text|. Adjust |vertical_pos| to - // |vertical_pos| + checkbox->height() + kSpacing. - Checkbox* CreateCheckbox(const base::string16& label_text, int* vertical_pos); + // Creates and adds a Checkbox with label |label_text|. Adjust |vertical_pos| + // to |vertical_pos| + checkbox->height() + kSpacing. + Checkbox* CreateAndAddCheckbox(const base::string16& label_text, + int* vertical_pos); // ButtonListener: // Be sure to call LayoutExampleBase::ButtonPressed() to ensure the "add" @@ -145,6 +131,11 @@ class VIEWS_EXAMPLES_EXPORT LayoutExampleBase : public ExampleBase, virtual void UpdateLayoutManager() = 0; private: + // Creates and adds a Textfield at the current position of |horizontal_pos| + // and |vertical_pos|. Update |horizontal_pos| to |horizontal_pos| + + // text_field->width() + kSpacing. + Textfield* CreateAndAddRawTextfield(int vertical_pos, int* horizontal_pos); + View* layout_panel_ = nullptr; View* control_panel_ = nullptr; LabelButton* add_button_ = nullptr; diff --git a/chromium/ui/views/examples/link_example.cc b/chromium/ui/views/examples/link_example.cc index aa61e567010..553c40d0d7e 100644 --- a/chromium/ui/views/examples/link_example.cc +++ b/chromium/ui/views/examples/link_example.cc @@ -8,21 +8,29 @@ #include <utility> #include "base/strings/utf_string_conversions.h" +#include "ui/base/l10n/l10n_util.h" #include "ui/views/controls/link.h" #include "ui/views/examples/examples_window.h" +#include "ui/views/examples/grit/views_examples_resources.h" #include "ui/views/layout/fill_layout.h" #include "ui/views/view.h" +using l10n_util::GetStringUTF16; +using l10n_util::GetStringUTF8; + namespace views { namespace examples { -LinkExample::LinkExample() : ExampleBase("Link") {} +LinkExample::LinkExample() + : ExampleBase(GetStringUTF8(IDS_LINK_SELECT_LABEL).c_str()) {} LinkExample::~LinkExample() = default; void LinkExample::CreateExampleView(View* container) { - auto link = std::make_unique<Link>(base::ASCIIToUTF16("Click me!")); - link->set_callback(base::BindRepeating(&LogStatus, "Link clicked")); + auto link = + std::make_unique<Link>(GetStringUTF16(IDS_LINK_CLICK_PROMPT_LABEL)); + link->set_callback(base::BindRepeating( + &LogStatus, GetStringUTF8(IDS_LINK_CLICK_CONFIRMED_LABEL))); container->SetLayoutManager(std::make_unique<FillLayout>()); container->AddChildView(std::move(link)); diff --git a/chromium/ui/views/examples/login_bubble_dialog.cc b/chromium/ui/views/examples/login_bubble_dialog.cc new file mode 100644 index 00000000000..a83e00b9d78 --- /dev/null +++ b/chromium/ui/views/examples/login_bubble_dialog.cc @@ -0,0 +1,164 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "ui/views/examples/login_bubble_dialog.h" + +#include "base/bind.h" +#include "base/callback_forward.h" +#include "base/strings/strcat.h" +#include "base/strings/string16.h" +#include "ui/base/l10n/l10n_util.h" +#include "ui/views/border.h" +#include "ui/views/controls/button/md_text_button.h" +#include "ui/views/controls/label.h" +#include "ui/views/examples/grit/views_examples_resources.h" +#include "ui/views/layout/box_layout.h" +#include "ui/views/layout/grid_layout.h" +#include "ui/views/layout/layout_provider.h" + +using l10n_util::GetStringUTF16; +using l10n_util::GetStringUTF8; + +namespace views { +namespace examples { + +namespace { + +// Adds a label textfield pair to the login dialog's layout. +Textfield* AddFormRow(LoginBubbleDialogView* bubble, + GridLayout* layout, + const base::string16& label_text) { + layout->StartRow(0, 0); + Label* label = layout->AddView(std::make_unique<Label>(label_text)); + Textfield* textfield = layout->AddView(std::make_unique<Textfield>()); + textfield->SetAssociatedLabel(label); + textfield->set_controller(bubble); + constexpr int kDefaultTextfieldWidth = 30; + constexpr int kMinimumTextfieldWidth = 5; + textfield->SetDefaultWidthInChars(kDefaultTextfieldWidth); + textfield->SetMinimumWidthInChars(kMinimumTextfieldWidth); + return textfield; +} + +} // namespace + +// static +void LoginBubbleDialogView::Show(View* anchor_view, + BubbleBorder::Arrow anchor_position, + OnSubmitCallback accept_callback) { + BubbleDialogDelegateView::CreateBubble( + new LoginBubbleDialogView(anchor_view, anchor_position, + std::move(accept_callback))) + ->Show(); +} + +LoginBubbleDialogView::~LoginBubbleDialogView() = default; + +void LoginBubbleDialogView::ContentsChanged( + Textfield* sender, + const base::string16& new_contents) { + SetButtonEnabled(ui::DIALOG_BUTTON_OK, !username_->GetText().empty() && + !password_->GetText().empty()); + DialogModelChanged(); +} + +LoginBubbleDialogView::LoginBubbleDialogView( + View* anchor_view, + BubbleBorder::Arrow anchor_position, + OnSubmitCallback accept_callback) + : BubbleDialogDelegateView(anchor_view, anchor_position) { + SetButtonEnabled(ui::DIALOG_BUTTON_OK, false); + + const auto on_submit = [](const LoginBubbleDialogView* bubble_view, + OnSubmitCallback accept_callback) { + std::move(accept_callback) + .Run(bubble_view->username_->GetText(), + bubble_view->password_->GetText()); + }; + SetAcceptCallback(base::BindOnce(on_submit, base::Unretained(this), + std::move(accept_callback))); + + SetTitle(l10n_util::GetStringUTF16(IDS_LOGIN_TITLE_LABEL)); + SetButtonLabel(ui::DIALOG_BUTTON_OK, + l10n_util::GetStringUTF16(IDS_LOGIN_OK_BUTTON_LABEL)); + + const LayoutProvider* provider = LayoutProvider::Get(); + set_margins( + provider->GetDialogInsetsForContentType(views::CONTROL, views::CONTROL)); + const int related_control_padding = + provider->GetDistanceMetric(views::DISTANCE_RELATED_CONTROL_VERTICAL); + const int label_padding = + provider->GetDistanceMetric(views::DISTANCE_RELATED_LABEL_HORIZONTAL); + + GridLayout* layout = SetLayoutManager(std::make_unique<GridLayout>()); + ColumnSet* column_set = layout->AddColumnSet(0); + column_set->AddColumn(GridLayout::LEADING, GridLayout::FILL, + GridLayout::kFixedSize, + GridLayout::ColumnSize::kUsePreferred, 0, 0); + column_set->AddPaddingColumn(0, label_padding); + column_set->AddColumn(GridLayout::FILL, GridLayout::FILL, 1.0, + GridLayout::ColumnSize::kUsePreferred, 0, 0); + + username_ = AddFormRow(this, layout, + l10n_util::GetStringUTF16(IDS_LOGIN_USERNAME_LABEL)); + + layout->AddPaddingRow(0, related_control_padding); + + password_ = AddFormRow(this, layout, + l10n_util::GetStringUTF16(IDS_LOGIN_PASSWORD_LABEL)); + password_->SetTextInputType(ui::TEXT_INPUT_TYPE_PASSWORD); +} + +LoginBubbleDialogExample::LoginBubbleDialogExample() + : ExampleBase(GetStringUTF8(IDS_LOGIN_SELECT_LABEL).c_str()) {} + +LoginBubbleDialogExample::~LoginBubbleDialogExample() = default; + +void LoginBubbleDialogExample::CreateExampleView(View* container) { + const int related_control_padding = LayoutProvider::Get()->GetDistanceMetric( + views::DISTANCE_RELATED_CONTROL_VERTICAL); + const int label_padding = LayoutProvider::Get()->GetDistanceMetric( + views::DISTANCE_RELATED_LABEL_HORIZONTAL); + + GridLayout* layout = + container->SetLayoutManager(std::make_unique<GridLayout>()); + ColumnSet* column_set = layout->AddColumnSet(0); + column_set->AddColumn(GridLayout::LEADING, GridLayout::FILL, + GridLayout::kFixedSize, + GridLayout::ColumnSize::kUsePreferred, 0, 0); + column_set->AddPaddingColumn(0, label_padding); + column_set->AddColumn(GridLayout::FILL, GridLayout::FILL, 1.0, + GridLayout::ColumnSize::kUsePreferred, 0, 0); + + layout->StartRowWithPadding(0, 0, 0, related_control_padding); + button_ = layout->AddView( + MdTextButton::Create(this, GetStringUTF16(IDS_LOGIN_SHOW_BUTTON_LABEL))); + + layout->StartRowWithPadding(0, 0, 0, related_control_padding); + layout->AddView(std::make_unique<Label>( + l10n_util::GetStringUTF16(IDS_LOGIN_USERNAME_LABEL))); + username_label_ = layout->AddView(std::make_unique<Label>()); + + layout->StartRowWithPadding(0, 0, 0, related_control_padding); + layout->AddView(std::make_unique<Label>( + l10n_util::GetStringUTF16(IDS_LOGIN_PASSWORD_LABEL))); + password_label_ = layout->AddView(std::make_unique<Label>()); +} + +void LoginBubbleDialogExample::ButtonPressed(Button* sender, + const ui::Event& event) { + LoginBubbleDialogView::Show( + button_, BubbleBorder::TOP_LEFT, + base::BindOnce(&LoginBubbleDialogExample::OnSubmit, + base::Unretained(this))); +} + +void LoginBubbleDialogExample::OnSubmit(base::string16 username, + base::string16 password) { + username_label_->SetText(username); + password_label_->SetText(password); +} + +} // namespace examples +} // namespace views diff --git a/chromium/ui/views/examples/login_bubble_dialog.h b/chromium/ui/views/examples/login_bubble_dialog.h new file mode 100644 index 00000000000..d62ad3ef64b --- /dev/null +++ b/chromium/ui/views/examples/login_bubble_dialog.h @@ -0,0 +1,71 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef UI_VIEWS_EXAMPLES_LOGIN_BUBBLE_DIALOG_H_ +#define UI_VIEWS_EXAMPLES_LOGIN_BUBBLE_DIALOG_H_ + +#include "base/strings/string16.h" +#include "ui/views/bubble/bubble_border.h" +#include "ui/views/bubble/bubble_dialog_delegate_view.h" +#include "ui/views/controls/button/button.h" +#include "ui/views/controls/textfield/textfield.h" +#include "ui/views/controls/textfield/textfield_controller.h" +#include "ui/views/examples/example_base.h" + +namespace views { + +class LabelButton; + +namespace examples { + +class LoginBubbleDialogView : public BubbleDialogDelegateView, + public TextfieldController { + public: + using OnSubmitCallback = base::OnceCallback<void(base::string16 username, + base::string16 password)>; + + static void Show(View* anchor_view, + BubbleBorder::Arrow anchor_position, + OnSubmitCallback accept_callback); + + ~LoginBubbleDialogView() override; + + // TextfieldController: + void ContentsChanged(Textfield* sender, + const base::string16& new_contents) override; + + private: + LoginBubbleDialogView(View* anchor_view, + BubbleBorder::Arrow anchor_position, + OnSubmitCallback accept_callback); + + Textfield* username_ = nullptr; + Textfield* password_ = nullptr; +}; + +// Instantiates the login dialog example. +class LoginBubbleDialogExample : public ExampleBase, public ButtonListener { + public: + LoginBubbleDialogExample(); + ~LoginBubbleDialogExample() override; + + // ExampleBase: + void CreateExampleView(View* container) override; + + // ButtonListener: + void ButtonPressed(Button* sender, const ui::Event& event) override; + + // LoginBubbleDialogController: + void OnSubmit(base::string16 username, base::string16 password); + + private: + LabelButton* button_ = nullptr; + Label* username_label_ = nullptr; + Label* password_label_ = nullptr; +}; + +} // namespace examples +} // namespace views + +#endif // UI_VIEWS_EXAMPLES_LOGIN_BUBBLE_DIALOG_H_ diff --git a/chromium/ui/views/examples/menu_example.cc b/chromium/ui/views/examples/menu_example.cc index 01a37140511..76d851f1a6b 100644 --- a/chromium/ui/views/examples/menu_example.cc +++ b/chromium/ui/views/examples/menu_example.cc @@ -9,15 +9,18 @@ #include "base/macros.h" #include "base/strings/utf_string_conversions.h" +#include "ui/base/l10n/l10n_util.h" #include "ui/base/models/simple_menu_model.h" #include "ui/views/controls/button/button.h" #include "ui/views/controls/button/menu_button.h" #include "ui/views/controls/menu/menu_runner.h" +#include "ui/views/examples/grit/views_examples_resources.h" #include "ui/views/layout/fill_layout.h" #include "ui/views/view.h" #include "ui/views/widget/widget.h" -using base::ASCIIToUTF16; +using l10n_util::GetStringUTF16; +using l10n_util::GetStringUTF8; namespace views { namespace examples { @@ -77,23 +80,25 @@ class ExampleMenuButton : public MenuButton, public ButtonListener { // ExampleMenuModel --------------------------------------------------------- ExampleMenuModel::ExampleMenuModel() : ui::SimpleMenuModel(this) { - AddItem(COMMAND_DO_SOMETHING, ASCIIToUTF16("Do Something")); + AddItem(COMMAND_DO_SOMETHING, GetStringUTF16(IDS_MENU_DO_SOMETHING_LABEL)); AddSeparator(ui::NORMAL_SEPARATOR); - AddRadioItem(COMMAND_SELECT_ASCII, ASCIIToUTF16("ASCII"), + AddRadioItem(COMMAND_SELECT_ASCII, GetStringUTF16(IDS_MENU_ASCII_LABEL), GROUP_MAKE_DECISION); - AddRadioItem(COMMAND_SELECT_UTF8, ASCIIToUTF16("UTF-8"), GROUP_MAKE_DECISION); - AddRadioItem(COMMAND_SELECT_UTF16, ASCIIToUTF16("UTF-16"), + AddRadioItem(COMMAND_SELECT_UTF8, GetStringUTF16(IDS_MENU_UTF8_LABEL), + GROUP_MAKE_DECISION); + AddRadioItem(COMMAND_SELECT_UTF16, GetStringUTF16(IDS_MENU_UTF16_LABEL), GROUP_MAKE_DECISION); AddSeparator(ui::NORMAL_SEPARATOR); - AddCheckItem(COMMAND_CHECK_APPLE, ASCIIToUTF16("Apple")); - AddCheckItem(COMMAND_CHECK_ORANGE, ASCIIToUTF16("Orange")); - AddCheckItem(COMMAND_CHECK_KIWI, ASCIIToUTF16("Kiwi")); + AddCheckItem(COMMAND_CHECK_APPLE, GetStringUTF16(IDS_MENU_APPLE_LABEL)); + AddCheckItem(COMMAND_CHECK_ORANGE, GetStringUTF16(IDS_MENU_ORANGE_LABEL)); + AddCheckItem(COMMAND_CHECK_KIWI, GetStringUTF16(IDS_MENU_KIWI_LABEL)); AddSeparator(ui::NORMAL_SEPARATOR); - AddItem(COMMAND_GO_HOME, ASCIIToUTF16("Go Home")); + AddItem(COMMAND_GO_HOME, GetStringUTF16(IDS_MENU_GO_HOME_LABEL)); submenu_ = std::make_unique<ui::SimpleMenuModel>(this); - submenu_->AddItem(COMMAND_DO_SOMETHING, ASCIIToUTF16("Do Something 2")); - AddSubMenu(0, ASCIIToUTF16("Submenu"), submenu_.get()); + submenu_->AddItem(COMMAND_DO_SOMETHING, + GetStringUTF16(IDS_MENU_DO_SOMETHING_2_LABEL)); + AddSubMenu(0, GetStringUTF16(IDS_MENU_SUBMENU_LABEL), submenu_.get()); } bool ExampleMenuModel::IsCommandIdChecked(int command_id) const { @@ -189,16 +194,16 @@ ui::SimpleMenuModel* ExampleMenuButton::GetMenuModel() { } // namespace -MenuExample::MenuExample() : ExampleBase("Menu") {} +MenuExample::MenuExample() + : ExampleBase(GetStringUTF8(IDS_MENU_SELECT_LABEL).c_str()) {} MenuExample::~MenuExample() = default; void MenuExample::CreateExampleView(View* container) { // We add a button to open a menu. - ExampleMenuButton* menu_button = - new ExampleMenuButton(ASCIIToUTF16("Open a menu")); container->SetLayoutManager(std::make_unique<FillLayout>()); - container->AddChildView(menu_button); + container->AddChildView(std::make_unique<ExampleMenuButton>( + GetStringUTF16(IDS_MENU_BUTTON_LABEL))); } } // namespace examples diff --git a/chromium/ui/views/examples/message_box_example.cc b/chromium/ui/views/examples/message_box_example.cc index 496dfc95a29..13aa6925d66 100644 --- a/chromium/ui/views/examples/message_box_example.cc +++ b/chromium/ui/views/examples/message_box_example.cc @@ -8,18 +8,22 @@ #include <utility> #include "base/strings/utf_string_conversions.h" +#include "ui/base/l10n/l10n_util.h" #include "ui/views/controls/button/label_button.h" #include "ui/views/controls/message_box_view.h" #include "ui/views/examples/examples_window.h" +#include "ui/views/examples/grit/views_examples_resources.h" #include "ui/views/layout/grid_layout.h" #include "ui/views/view.h" -using base::ASCIIToUTF16; +using l10n_util::GetStringUTF16; +using l10n_util::GetStringUTF8; namespace views { namespace examples { -MessageBoxExample::MessageBoxExample() : ExampleBase("Message Box View") {} +MessageBoxExample::MessageBoxExample() + : ExampleBase(GetStringUTF8(IDS_MESSAGE_SELECT_LABEL).c_str()) {} MessageBoxExample::~MessageBoxExample() = default; @@ -28,38 +32,42 @@ void MessageBoxExample::CreateExampleView(View* container) { container->SetLayoutManager(std::make_unique<views::GridLayout>()); auto message_box_view = std::make_unique<MessageBoxView>( - MessageBoxView::InitParams(ASCIIToUTF16("Hello, world!"))); - message_box_view->SetCheckBoxLabel(ASCIIToUTF16("Check Box")); + MessageBoxView::InitParams(GetStringUTF16(IDS_MESSAGE_INTRO_LABEL))); + message_box_view->SetCheckBoxLabel( + GetStringUTF16(IDS_MESSAGE_CHECK_BOX_LABEL)); const int message_box_column = 0; ColumnSet* column_set = layout->AddColumnSet(message_box_column); column_set->AddColumn(GridLayout::FILL, GridLayout::FILL, 1, - GridLayout::USE_PREF, 0, 0); + GridLayout::ColumnSize::kUsePreferred, 0, 0); layout->StartRow(1 /* expand */, message_box_column); message_box_view_ = layout->AddView(std::move(message_box_view)); const int button_column = 1; column_set = layout->AddColumnSet(button_column); column_set->AddColumn(GridLayout::FILL, GridLayout::FILL, 0.5f, - GridLayout::USE_PREF, 0, 0); + GridLayout::ColumnSize::kUsePreferred, 0, 0); column_set->AddColumn(GridLayout::FILL, GridLayout::FILL, 0.5f, - GridLayout::USE_PREF, 0, 0); + GridLayout::ColumnSize::kUsePreferred, 0, 0); layout->StartRow(0 /* no expand */, button_column); - status_ = layout->AddView( - std::make_unique<LabelButton>(this, ASCIIToUTF16("Show Status"))); - toggle_ = layout->AddView( - std::make_unique<LabelButton>(this, ASCIIToUTF16("Toggle Checkbox"))); + status_ = layout->AddView(std::make_unique<LabelButton>( + this, GetStringUTF16(IDS_MESSAGE_STATUS_LABEL))); + toggle_ = layout->AddView(std::make_unique<LabelButton>( + this, GetStringUTF16(IDS_MESSAGE_TOGGLE_LABEL))); } void MessageBoxExample::ButtonPressed(Button* sender, const ui::Event& event) { if (sender == status_) { message_box_view_->SetCheckBoxLabel( - ASCIIToUTF16(message_box_view_->IsCheckBoxSelected() ? "on" : "off")); - LogStatus(message_box_view_->IsCheckBoxSelected() - ? "Check Box Selected" - : "Check Box Not Selected"); + message_box_view_->IsCheckBoxSelected() + ? GetStringUTF16(IDS_MESSAGE_ON_LABEL) + : GetStringUTF16(IDS_MESSAGE_OFF_LABEL)); + LogStatus( + message_box_view_->IsCheckBoxSelected() + ? GetStringUTF8(IDS_MESSAGE_CHECK_SELECTED_LABEL).c_str() + : GetStringUTF8(IDS_MESSAGE_CHECK_NOT_SELECTED_LABEL).c_str()); } else if (sender == toggle_) { message_box_view_->SetCheckBoxSelected( !message_box_view_->IsCheckBoxSelected()); diff --git a/chromium/ui/views/examples/multiline_example.cc b/chromium/ui/views/examples/multiline_example.cc index c8b056c9cdc..dea9016b42e 100644 --- a/chromium/ui/views/examples/multiline_example.cc +++ b/chromium/ui/views/examples/multiline_example.cc @@ -12,6 +12,7 @@ #include "base/macros.h" #include "base/strings/utf_string_conversions.h" +#include "ui/base/l10n/l10n_util.h" #include "ui/events/event.h" #include "ui/gfx/render_text.h" #include "ui/views/background.h" @@ -19,10 +20,12 @@ #include "ui/views/controls/button/checkbox.h" #include "ui/views/controls/label.h" #include "ui/views/controls/textfield/textfield.h" +#include "ui/views/examples/grit/views_examples_resources.h" #include "ui/views/layout/grid_layout.h" #include "ui/views/view.h" -using base::ASCIIToUTF16; +using l10n_util::GetStringUTF16; +using l10n_util::GetStringUTF8; namespace views { namespace examples { @@ -123,7 +126,8 @@ class MultilineExample::RenderTextView : public View { DISALLOW_COPY_AND_ASSIGN(RenderTextView); }; -MultilineExample::MultilineExample() : ExampleBase("Multiline RenderText") {} +MultilineExample::MultilineExample() + : ExampleBase(GetStringUTF8(IDS_MULTILINE_SELECT_LABEL).c_str()) {} MultilineExample::~MultilineExample() = default; @@ -142,12 +146,12 @@ void MultilineExample::CreateExampleView(View* container) { label->SetBorder(CreateSolidBorder(2, SK_ColorCYAN)); auto label_checkbox = - std::make_unique<Checkbox>(ASCIIToUTF16("views::Label:"), this); + std::make_unique<Checkbox>(GetStringUTF16(IDS_MULTILINE_LABEL), this); label_checkbox->SetChecked(true); label_checkbox->set_request_focus_on_press(false); - auto elision_checkbox = - std::make_unique<Checkbox>(ASCIIToUTF16("elide text?"), this); + auto elision_checkbox = std::make_unique<Checkbox>( + GetStringUTF16(IDS_MULTILINE_ELIDE_LABEL), this); elision_checkbox->SetChecked(false); elision_checkbox->set_request_focus_on_press(false); @@ -160,12 +164,13 @@ void MultilineExample::CreateExampleView(View* container) { ColumnSet* column_set = layout->AddColumnSet(0); column_set->AddColumn(GridLayout::LEADING, GridLayout::CENTER, 0.0f, - GridLayout::USE_PREF, 0, 0); + GridLayout::ColumnSize::kUsePreferred, 0, 0); column_set->AddColumn(GridLayout::FILL, GridLayout::FILL, 1.0f, - GridLayout::FIXED, 0, 0); + GridLayout::ColumnSize::kFixed, 0, 0); layout->StartRow(0, 0); - layout->AddView(std::make_unique<Label>(ASCIIToUTF16("gfx::RenderText:"))); + layout->AddView( + std::make_unique<Label>(GetStringUTF16(IDS_MULTILINE_RENDER_TEXT_LABEL))); render_text_view_ = layout->AddView(std::move(render_text_view)); layout->StartRow(0, 0); @@ -176,7 +181,8 @@ void MultilineExample::CreateExampleView(View* container) { elision_checkbox_ = layout->AddView(std::move(elision_checkbox)); layout->StartRow(0, 0); - layout->AddView(std::make_unique<Label>(ASCIIToUTF16("Sample Text:"))); + layout->AddView( + std::make_unique<Label>(GetStringUTF16(IDS_MULTILINE_SAMPLE_TEXT_LABEL))); textfield_ = layout->AddView(std::move(textfield)); } diff --git a/chromium/ui/views/examples/native_theme_example.cc b/chromium/ui/views/examples/native_theme_example.cc index f2fd54edc0c..04f32e5eb02 100644 --- a/chromium/ui/views/examples/native_theme_example.cc +++ b/chromium/ui/views/examples/native_theme_example.cc @@ -14,10 +14,12 @@ #include "base/strings/stringprintf.h" #include "base/strings/utf_string_conversions.h" +#include "ui/base/l10n/l10n_util.h" #include "ui/native_theme/native_theme.h" #include "ui/views/background.h" #include "ui/views/controls/label.h" #include "ui/views/controls/scroll_view.h" +#include "ui/views/examples/grit/views_examples_resources.h" #include "ui/views/layout/fill_layout.h" #include "ui/views/layout/grid_layout.h" @@ -69,9 +71,9 @@ std::unique_ptr<View> CreateAllColorsView() { auto* layout = container->SetLayoutManager(std::make_unique<GridLayout>()); auto* column_set = layout->AddColumnSet(0); column_set->AddColumn(GridLayout::FILL, GridLayout::FILL, 1.0, - GridLayout::USE_PREF, 0, 0); + GridLayout::ColumnSize::kUsePreferred, 0, 0); column_set->AddColumn(GridLayout::FILL, GridLayout::FILL, 1.0, - GridLayout::USE_PREF, 0, 0); + GridLayout::ColumnSize::kUsePreferred, 0, 0); InsertColorRow(layout, COLOR_LABEL_ARGS(kColorId_WindowBackground)); InsertColorRow(layout, COLOR_LABEL_ARGS(kColorId_DialogBackground)); InsertColorRow(layout, COLOR_LABEL_ARGS(kColorId_DialogForeground)); @@ -80,7 +82,6 @@ std::unique_ptr<View> CreateAllColorsView() { InsertColorRow(layout, COLOR_LABEL_ARGS(kColorId_UnfocusedBorderColor)); InsertColorRow(layout, COLOR_LABEL_ARGS(kColorId_ButtonEnabledColor)); InsertColorRow(layout, COLOR_LABEL_ARGS(kColorId_ButtonDisabledColor)); - InsertColorRow(layout, COLOR_LABEL_ARGS(kColorId_ButtonPressedShade)); InsertColorRow(layout, COLOR_LABEL_ARGS(kColorId_ButtonUncheckedColor)); InsertColorRow(layout, COLOR_LABEL_ARGS(kColorId_ProminentButtonColor)); InsertColorRow(layout, @@ -101,6 +102,7 @@ std::unique_ptr<View> CreateAllColorsView() { InsertColorRow(layout, COLOR_LABEL_ARGS(kColorId_MenuSeparatorColor)); InsertColorRow(layout, COLOR_LABEL_ARGS(kColorId_MenuBackgroundColor)); InsertColorRow(layout, COLOR_LABEL_ARGS(kColorId_MenuBorderColor)); + InsertColorRow(layout, COLOR_LABEL_ARGS(kColorId_MenuIconColor)); InsertColorRow(layout, COLOR_LABEL_ARGS(kColorId_HighlightedMenuItemBackgroundColor)); InsertColorRow(layout, @@ -168,7 +170,8 @@ std::unique_ptr<View> CreateAllColorsView() { } // namespace -NativeThemeExample::NativeThemeExample() : ExampleBase("Native Theme Colors") {} +NativeThemeExample::NativeThemeExample() + : ExampleBase(l10n_util::GetStringUTF8(IDS_THEME_SELECT_LABEL).c_str()) {} NativeThemeExample::~NativeThemeExample() = default; diff --git a/chromium/ui/views/examples/progress_bar_example.cc b/chromium/ui/views/examples/progress_bar_example.cc index d59bc19b851..4879f5fe217 100644 --- a/chromium/ui/views/examples/progress_bar_example.cc +++ b/chromium/ui/views/examples/progress_bar_example.cc @@ -10,15 +10,21 @@ #include "base/numerics/ranges.h" #include "base/strings/utf_string_conversions.h" +#include "ui/base/l10n/l10n_util.h" #include "ui/views/controls/button/md_text_button.h" #include "ui/views/controls/progress_bar.h" +#include "ui/views/examples/grit/views_examples_resources.h" #include "ui/views/layout/grid_layout.h" #include "ui/views/view.h" +using l10n_util::GetStringUTF16; +using l10n_util::GetStringUTF8; + namespace views { namespace examples { -ProgressBarExample::ProgressBarExample() : ExampleBase("Progress Bar") {} +ProgressBarExample::ProgressBarExample() + : ExampleBase(GetStringUTF8(IDS_PROGRESS_SELECT_LABEL).c_str()) {} ProgressBarExample::~ProgressBarExample() = default; @@ -28,13 +34,13 @@ void ProgressBarExample::CreateExampleView(View* container) { ColumnSet* column_set = layout->AddColumnSet(0); column_set->AddColumn(GridLayout::TRAILING, GridLayout::CENTER, 0, - GridLayout::USE_PREF, 0, 0); + GridLayout::ColumnSize::kUsePreferred, 0, 0); column_set->AddPaddingColumn(0, 8); column_set->AddColumn(GridLayout::FILL, GridLayout::CENTER, 1, - GridLayout::FIXED, 200, 0); + GridLayout::ColumnSize::kFixed, 200, 0); column_set->AddPaddingColumn(0, 8); column_set->AddColumn(GridLayout::LEADING, GridLayout::CENTER, 0, - GridLayout::USE_PREF, 0, 0); + GridLayout::ColumnSize::kUsePreferred, 0, 0); layout->StartRow(0, 0); minus_button_ = @@ -45,14 +51,14 @@ void ProgressBarExample::CreateExampleView(View* container) { layout->StartRowWithPadding(0, 0, 0, 10); layout->AddView( - std::make_unique<Label>(base::ASCIIToUTF16("Infinite loader:"))); + std::make_unique<Label>(GetStringUTF16(IDS_PROGRESS_LOADER_LABEL))); auto infinite_bar = std::make_unique<ProgressBar>(); infinite_bar->SetValue(-1); layout->AddView(std::move(infinite_bar)); layout->StartRowWithPadding(0, 0, 0, 10); - layout->AddView(std::make_unique<Label>( - base::ASCIIToUTF16("Infinite loader (very short):"))); + layout->AddView( + std::make_unique<Label>(GetStringUTF16(IDS_PROGRESS_LOADER_SHORT_LABEL))); auto shorter_bar = std::make_unique<ProgressBar>(2); shorter_bar->SetValue(-1); layout->AddView(std::move(shorter_bar)); diff --git a/chromium/ui/views/examples/radio_button_example.cc b/chromium/ui/views/examples/radio_button_example.cc index b9644096d1f..598c8b18a20 100644 --- a/chromium/ui/views/examples/radio_button_example.cc +++ b/chromium/ui/views/examples/radio_button_example.cc @@ -11,12 +11,17 @@ #include "base/stl_util.h" #include "base/strings/string_number_conversions.h" #include "base/strings/utf_string_conversions.h" +#include "ui/base/l10n/l10n_util.h" #include "ui/views/controls/button/label_button.h" #include "ui/views/controls/button/radio_button.h" #include "ui/views/examples/examples_window.h" +#include "ui/views/examples/grit/views_examples_resources.h" #include "ui/views/layout/grid_layout.h" #include "ui/views/view.h" +using l10n_util::GetStringUTF16; +using l10n_util::GetStringUTF8; + namespace views { namespace examples { @@ -28,7 +33,8 @@ const char* BoolToOnOff(bool value) { } // namespace -RadioButtonExample::RadioButtonExample() : ExampleBase("Radio Button") {} +RadioButtonExample::RadioButtonExample() + : ExampleBase(GetStringUTF8(IDS_RADIO_BUTTON_SELECT_LABEL).c_str()) {} RadioButtonExample::~RadioButtonExample() = default; @@ -37,7 +43,7 @@ void RadioButtonExample::CreateExampleView(View* container) { container->SetLayoutManager(std::make_unique<views::GridLayout>()); ColumnSet* column_set = layout->AddColumnSet(0); column_set->AddColumn(GridLayout::FILL, GridLayout::FILL, 1.0f, - GridLayout::USE_PREF, 0, 0); + GridLayout::ColumnSize::kUsePreferred, 0, 0); const int group = 1; for (size_t i = 0; i < 3; ++i) { layout->StartRow(0, 0); @@ -48,11 +54,11 @@ void RadioButtonExample::CreateExampleView(View* container) { } layout->StartRow(0, 0); - select_ = layout->AddView( - std::make_unique<LabelButton>(this, base::ASCIIToUTF16("Select"))); + select_ = layout->AddView(std::make_unique<LabelButton>( + this, GetStringUTF16(IDS_RADIO_BUTTON_SELECT_BUTTON_LABEL))); layout->StartRow(0, 0); - status_ = layout->AddView( - std::make_unique<LabelButton>(this, base::ASCIIToUTF16("Show Status"))); + status_ = layout->AddView(std::make_unique<LabelButton>( + this, GetStringUTF16(IDS_RADIO_BUTTON_STATUS_LABEL))); } void RadioButtonExample::ButtonPressed(Button* sender, const ui::Event& event) { diff --git a/chromium/ui/views/examples/scroll_view_example.cc b/chromium/ui/views/examples/scroll_view_example.cc index 48082b47df1..90a68d46c93 100644 --- a/chromium/ui/views/examples/scroll_view_example.cc +++ b/chromium/ui/views/examples/scroll_view_example.cc @@ -11,17 +11,20 @@ #include "base/strings/stringprintf.h" #include "base/strings/utf_string_conversions.h" #include "cc/paint/paint_flags.h" +#include "ui/base/l10n/l10n_util.h" #include "ui/gfx/canvas.h" #include "ui/gfx/color_utils.h" #include "ui/gfx/skia_paint_util.h" #include "ui/views/background.h" #include "ui/views/controls/button/label_button.h" #include "ui/views/controls/button/radio_button.h" +#include "ui/views/examples/grit/views_examples_resources.h" #include "ui/views/layout/box_layout.h" #include "ui/views/layout/grid_layout.h" #include "ui/views/view.h" -using base::ASCIIToUTF16; +using l10n_util::GetStringUTF16; +using l10n_util::GetStringUTF8; namespace views { namespace examples { @@ -42,8 +45,10 @@ class ScrollViewExample::ScrollableView : public View { views::BoxLayout::Orientation::kVertical)); container->AddChildView(std::move(view)); }; - add_child(std::make_unique<LabelButton>(nullptr, ASCIIToUTF16("Button"))); - add_child(std::make_unique<RadioButton>(ASCIIToUTF16("Radio Button"), 0)); + add_child(std::make_unique<LabelButton>( + nullptr, GetStringUTF16(IDS_SCROLL_VIEW_BUTTON_LABEL))); + add_child(std::make_unique<RadioButton>( + GetStringUTF16(IDS_SCROLL_VIEW_RADIO_BUTTON_LABEL), 0)); layout_manager->SetDefaultFlex(1); } @@ -67,7 +72,8 @@ class ScrollViewExample::ScrollableView : public View { DISALLOW_COPY_AND_ASSIGN(ScrollableView); }; -ScrollViewExample::ScrollViewExample() : ExampleBase("Scroll View") {} +ScrollViewExample::ScrollViewExample() + : ExampleBase(GetStringUTF8(IDS_SCROLL_VIEW_SELECT_LABEL).c_str()) {} ScrollViewExample::~ScrollViewExample() = default; @@ -83,7 +89,7 @@ void ScrollViewExample::CreateExampleView(View* container) { // Add scroll view. ColumnSet* column_set = layout->AddColumnSet(0); column_set->AddColumn(GridLayout::FILL, GridLayout::FILL, 1, - GridLayout::USE_PREF, 0, 0); + GridLayout::ColumnSize::kUsePreferred, 0, 0); layout->StartRow(1, 0); scroll_view_ = layout->AddView(std::move(scroll_view)); @@ -91,19 +97,19 @@ void ScrollViewExample::CreateExampleView(View* container) { column_set = layout->AddColumnSet(1); for (size_t i = 0; i < 5; i++) { column_set->AddColumn(GridLayout::FILL, GridLayout::FILL, 1, - GridLayout::USE_PREF, 0, 0); + GridLayout::ColumnSize::kUsePreferred, 0, 0); } layout->StartRow(0, 1); - wide_ = layout->AddView( - std::make_unique<LabelButton>(this, ASCIIToUTF16("Wide"))); - tall_ = layout->AddView( - std::make_unique<LabelButton>(this, ASCIIToUTF16("Tall"))); - big_square_ = layout->AddView( - std::make_unique<LabelButton>(this, ASCIIToUTF16("Big Square"))); - small_square_ = layout->AddView( - std::make_unique<LabelButton>(this, ASCIIToUTF16("Small Square"))); - scroll_to_ = layout->AddView( - std::make_unique<LabelButton>(this, ASCIIToUTF16("Scroll to"))); + wide_ = layout->AddView(std::make_unique<LabelButton>( + this, GetStringUTF16(IDS_SCROLL_VIEW_WIDE_LABEL))); + tall_ = layout->AddView(std::make_unique<LabelButton>( + this, GetStringUTF16(IDS_SCROLL_VIEW_TALL_LABEL))); + big_square_ = layout->AddView(std::make_unique<LabelButton>( + this, GetStringUTF16(IDS_SCROLL_VIEW_BIG_SQUARE_LABEL))); + small_square_ = layout->AddView(std::make_unique<LabelButton>( + this, GetStringUTF16(IDS_SCROLL_VIEW_SMALL_SQUARE_LABEL))); + scroll_to_ = layout->AddView(std::make_unique<LabelButton>( + this, GetStringUTF16(IDS_SCROLL_VIEW_SCROLL_TO_LABEL))); } void ScrollViewExample::ButtonPressed(Button* sender, const ui::Event& event) { diff --git a/chromium/ui/views/examples/slider_example.cc b/chromium/ui/views/examples/slider_example.cc index 2f151f3f5b5..4b23bee018f 100644 --- a/chromium/ui/views/examples/slider_example.cc +++ b/chromium/ui/views/examples/slider_example.cc @@ -8,29 +8,30 @@ #include "base/strings/stringprintf.h" #include "base/strings/utf_string_conversions.h" +#include "ui/base/l10n/l10n_util.h" #include "ui/gfx/geometry/insets.h" #include "ui/views/controls/label.h" #include "ui/views/controls/slider.h" +#include "ui/views/examples/grit/views_examples_resources.h" #include "ui/views/layout/box_layout.h" #include "ui/views/view.h" namespace views { namespace examples { -SliderExample::SliderExample() : ExampleBase("Slider") {} +SliderExample::SliderExample() + : ExampleBase(l10n_util::GetStringUTF8(IDS_SLIDER_SELECT_LABEL).c_str()) {} SliderExample::~SliderExample() = default; void SliderExample::CreateExampleView(View* container) { - label_ = new Label(); - slider_ = new views::Slider(this); + label_ = container->AddChildView(std::make_unique<Label>()); + slider_ = container->AddChildView(std::make_unique<Slider>(this)); slider_->SetValue(0.5); container->SetLayoutManager(std::make_unique<BoxLayout>( BoxLayout::Orientation::kHorizontal, gfx::Insets(3), 3)); - container->AddChildView(slider_); - container->AddChildView(label_); } void SliderExample::SliderValueChanged(Slider* sender, diff --git a/chromium/ui/views/examples/tabbed_pane_example.cc b/chromium/ui/views/examples/tabbed_pane_example.cc index 15fedb4ba88..b561f983ffc 100644 --- a/chromium/ui/views/examples/tabbed_pane_example.cc +++ b/chromium/ui/views/examples/tabbed_pane_example.cc @@ -8,27 +8,33 @@ #include <utility> #include "base/strings/utf_string_conversions.h" +#include "ui/base/l10n/l10n_util.h" #include "ui/views/controls/button/label_button.h" #include "ui/views/controls/tabbed_pane/tabbed_pane.h" #include "ui/views/examples/examples_window.h" +#include "ui/views/examples/grit/views_examples_resources.h" #include "ui/views/layout/grid_layout.h" -using base::ASCIIToUTF16; +using l10n_util::GetStringUTF16; +using l10n_util::GetStringUTF8; namespace views { namespace examples { -TabbedPaneExample::TabbedPaneExample() : ExampleBase("Tabbed Pane") {} +TabbedPaneExample::TabbedPaneExample() + : ExampleBase(GetStringUTF8(IDS_TABBED_PANE_SELECT_LABEL).c_str()) {} TabbedPaneExample::~TabbedPaneExample() = default; void TabbedPaneExample::CreateExampleView(View* container) { auto tabbed_pane = std::make_unique<TabbedPane>(); tabbed_pane->set_listener(this); - auto add = std::make_unique<LabelButton>(this, ASCIIToUTF16("Add")); - auto add_at = std::make_unique<LabelButton>(this, ASCIIToUTF16("Add At 1")); - auto select_at = - std::make_unique<LabelButton>(this, ASCIIToUTF16("Select At 1")); + auto add = std::make_unique<LabelButton>( + this, GetStringUTF16(IDS_TABBED_PANE_ADD_LABEL)); + auto add_at = std::make_unique<LabelButton>( + this, GetStringUTF16(IDS_TABBED_PANE_ADD_1_LABEL)); + auto select_at = std::make_unique<LabelButton>( + this, GetStringUTF16(IDS_TABBED_PANE_SELECT_1_LABEL)); GridLayout* layout = container->SetLayoutManager(std::make_unique<views::GridLayout>()); @@ -36,21 +42,21 @@ void TabbedPaneExample::CreateExampleView(View* container) { const int tabbed_pane_column = 0; ColumnSet* column_set = layout->AddColumnSet(tabbed_pane_column); column_set->AddColumn(GridLayout::FILL, GridLayout::FILL, 1.0f, - GridLayout::USE_PREF, 0, 0); + GridLayout::ColumnSize::kUsePreferred, 0, 0); layout->StartRow(1 /* expand */, tabbed_pane_column); tabbed_pane_ = layout->AddView(std::move(tabbed_pane)); // Create a few tabs with a button first. - AddButton("Tab 1"); - AddButton("Tab 2"); - AddButton("Tab 3"); + AddButton(GetStringUTF16(IDS_TABBED_PANE_TAB_1_LABEL)); + AddButton(GetStringUTF16(IDS_TABBED_PANE_TAB_2_LABEL)); + AddButton(GetStringUTF16(IDS_TABBED_PANE_TAB_3_LABEL)); // Add control buttons horizontally. const int button_column = 1; column_set = layout->AddColumnSet(button_column); for (size_t i = 0; i < 3; i++) { column_set->AddColumn(GridLayout::FILL, GridLayout::FILL, 1.0f, - GridLayout::USE_PREF, 0, 0); + GridLayout::ColumnSize::kUsePreferred, 0, 0); } layout->StartRow(0 /* no expand */, button_column); @@ -61,9 +67,9 @@ void TabbedPaneExample::CreateExampleView(View* container) { void TabbedPaneExample::ButtonPressed(Button* sender, const ui::Event& event) { if (sender == add_) { - AddButton("Added"); + AddButton(GetStringUTF16(IDS_TABBED_PANE_ADDED_LABEL)); } else if (sender == add_at_) { - const base::string16 label = ASCIIToUTF16("Added at 1"); + const base::string16 label = GetStringUTF16(IDS_TABBED_PANE_ADDED_1_LABEL); tabbed_pane_->AddTabAtIndex(1, label, std::make_unique<LabelButton>(nullptr, label)); } else if (sender == select_at_) { @@ -83,9 +89,8 @@ void TabbedPaneExample::PrintCurrentStatus() { tabbed_pane_->GetTabCount(), tabbed_pane_->GetSelectedTabIndex()); } -void TabbedPaneExample::AddButton(const std::string& label) { - tabbed_pane_->AddTab(ASCIIToUTF16(label), std::make_unique<LabelButton>( - nullptr, ASCIIToUTF16(label))); +void TabbedPaneExample::AddButton(const base::string16& label) { + tabbed_pane_->AddTab(label, std::make_unique<LabelButton>(nullptr, label)); } } // namespace examples diff --git a/chromium/ui/views/examples/tabbed_pane_example.h b/chromium/ui/views/examples/tabbed_pane_example.h index 5806ca9c5c2..ce4fe34d4e9 100644 --- a/chromium/ui/views/examples/tabbed_pane_example.h +++ b/chromium/ui/views/examples/tabbed_pane_example.h @@ -38,7 +38,7 @@ class VIEWS_EXAMPLES_EXPORT TabbedPaneExample : public ExampleBase, // Print the status of the tab in the status area. void PrintCurrentStatus(); - void AddButton(const std::string& label); + void AddButton(const base::string16& label); // The tabbed pane to be tested. TabbedPane* tabbed_pane_; diff --git a/chromium/ui/views/examples/table_example.cc b/chromium/ui/views/examples/table_example.cc index e5913af4951..c272f3b3eca 100644 --- a/chromium/ui/views/examples/table_example.cc +++ b/chromium/ui/views/examples/table_example.cc @@ -67,20 +67,20 @@ void TableExample::CreateExampleView(View* container) { ColumnSet* column_set = layout->AddColumnSet(0); column_set->AddColumn(GridLayout::FILL, GridLayout::FILL, 1, - GridLayout::USE_PREF, 0, 0); + GridLayout::ColumnSize::kUsePreferred, 0, 0); layout->StartRow(1 /* expand */, 0); table_ = table.get(); layout->AddView(TableView::CreateScrollViewWithTable(std::move(table))); column_set = layout->AddColumnSet(1); column_set->AddColumn(GridLayout::FILL, GridLayout::FILL, 0.5f, - GridLayout::USE_PREF, 0, 0); + GridLayout::ColumnSize::kUsePreferred, 0, 0); column_set->AddColumn(GridLayout::FILL, GridLayout::FILL, 0.5f, - GridLayout::USE_PREF, 0, 0); + GridLayout::ColumnSize::kUsePreferred, 0, 0); column_set->AddColumn(GridLayout::FILL, GridLayout::FILL, 0.5f, - GridLayout::USE_PREF, 0, 0); + GridLayout::ColumnSize::kUsePreferred, 0, 0); column_set->AddColumn(GridLayout::FILL, GridLayout::FILL, 0.5f, - GridLayout::USE_PREF, 0, 0); + GridLayout::ColumnSize::kUsePreferred, 0, 0); layout->StartRow(0 /* no expand */, 1); diff --git a/chromium/ui/views/examples/text_example.cc b/chromium/ui/views/examples/text_example.cc index 61a68575fde..7e0e015d872 100644 --- a/chromium/ui/views/examples/text_example.cc +++ b/chromium/ui/views/examples/text_example.cc @@ -168,10 +168,10 @@ void TextExample::CreateExampleView(View* container) { ColumnSet* column_set = layout->AddColumnSet(0); column_set->AddPaddingColumn(0, 8); column_set->AddColumn(GridLayout::LEADING, GridLayout::FILL, 0.1f, - GridLayout::USE_PREF, 0, 0); + GridLayout::ColumnSize::kUsePreferred, 0, 0); for (int i = 0; i < kNumColumns - 1; i++) column_set->AddColumn(GridLayout::FILL, GridLayout::FILL, 0.1f, - GridLayout::USE_PREF, 0, 0); + GridLayout::ColumnSize::kUsePreferred, 0, 0); column_set->AddPaddingColumn(0, 8); h_align_cb_ = AddCombobox(layout, "H-Align", kHorizontalAligments, @@ -196,7 +196,7 @@ void TextExample::CreateExampleView(View* container) { column_set = layout->AddColumnSet(1); column_set->AddPaddingColumn(0, 16); column_set->AddColumn(GridLayout::FILL, GridLayout::FILL, 1, - GridLayout::USE_PREF, 0, 0); + GridLayout::ColumnSize::kUsePreferred, 0, 0); column_set->AddPaddingColumn(0, 16); layout->StartRow(1, 1); text_view_ = layout->AddView(std::move(text_view)); diff --git a/chromium/ui/views/examples/textfield_example.cc b/chromium/ui/views/examples/textfield_example.cc index 4daa1e35f4b..7f1d9b2ddc4 100644 --- a/chromium/ui/views/examples/textfield_example.cc +++ b/chromium/ui/views/examples/textfield_example.cc @@ -10,6 +10,7 @@ #include <utility> #include "base/strings/utf_string_conversions.h" +#include "ui/base/l10n/l10n_util.h" #include "ui/events/event.h" #include "ui/gfx/color_palette.h" #include "ui/gfx/range/range.h" @@ -18,11 +19,12 @@ #include "ui/views/controls/label.h" #include "ui/views/controls/textfield/textfield.h" #include "ui/views/examples/examples_window.h" +#include "ui/views/examples/grit/views_examples_resources.h" #include "ui/views/layout/grid_layout.h" #include "ui/views/view.h" -using base::ASCIIToUTF16; -using base::UTF16ToUTF8; +using l10n_util::GetStringUTF16; +using l10n_util::GetStringUTF8; namespace views { namespace examples { @@ -41,7 +43,8 @@ T* MakeRow(GridLayout* layout, } // namespace -TextfieldExample::TextfieldExample() : ExampleBase("Textfield") {} +TextfieldExample::TextfieldExample() + : ExampleBase(GetStringUTF8(IDS_TEXTFIELD_SELECT_LABEL).c_str()) {} TextfieldExample::~TextfieldExample() = default; @@ -50,14 +53,15 @@ void TextfieldExample::CreateExampleView(View* container) { name->set_controller(this); auto password = std::make_unique<Textfield>(); password->SetTextInputType(ui::TEXT_INPUT_TYPE_PASSWORD); - password->SetPlaceholderText(ASCIIToUTF16("password")); + password->SetPlaceholderText( + GetStringUTF16(IDS_TEXTFIELD_PASSWORD_PLACEHOLDER)); password->set_controller(this); auto disabled = std::make_unique<Textfield>(); disabled->SetEnabled(false); - disabled->SetText(ASCIIToUTF16("disabled")); + disabled->SetText(GetStringUTF16(IDS_TEXTFIELD_DISABLED_PLACEHOLDER)); auto read_only = std::make_unique<Textfield>(); read_only->SetReadOnly(true); - read_only->SetText(ASCIIToUTF16("read only")); + read_only->SetText(GetStringUTF16(IDS_TEXTFFIELD_READ_ONLY_PLACEHOLDER)); auto invalid = std::make_unique<Textfield>(); invalid->SetInvalid(true); auto rtl = std::make_unique<Textfield>(); @@ -68,57 +72,59 @@ void TextfieldExample::CreateExampleView(View* container) { ColumnSet* column_set = layout->AddColumnSet(0); column_set->AddColumn(GridLayout::LEADING, GridLayout::FILL, 0.2f, - GridLayout::USE_PREF, 0, 0); + GridLayout::ColumnSize::kUsePreferred, 0, 0); column_set->AddColumn(GridLayout::FILL, GridLayout::FILL, 0.8f, - GridLayout::USE_PREF, 0, 0); - - name_ = MakeRow(layout, std::make_unique<Label>(ASCIIToUTF16("Name:")), - std::move(name)); - password_ = - MakeRow(layout, std::make_unique<Label>(ASCIIToUTF16("Password:")), - std::move(password)); - disabled_ = - MakeRow(layout, std::make_unique<Label>(ASCIIToUTF16("Disabled:")), - std::move(disabled)); - read_only_ = - MakeRow(layout, std::make_unique<Label>(ASCIIToUTF16("Read Only:")), - std::move(read_only)); - invalid_ = MakeRow(layout, std::make_unique<Label>(ASCIIToUTF16("Invalid:")), - std::move(invalid)); - rtl_ = MakeRow(layout, std::make_unique<Label>(ASCIIToUTF16("RTL:")), - std::move(rtl)); - MakeRow<View, Label>(layout, nullptr, - std::make_unique<Label>(ASCIIToUTF16("Name:"))); + GridLayout::ColumnSize::kUsePreferred, 0, 0); + + name_ = MakeRow( + layout, std::make_unique<Label>(GetStringUTF16(IDS_TEXTFIELD_NAME_LABEL)), + std::move(name)); + password_ = MakeRow( + layout, + std::make_unique<Label>(GetStringUTF16(IDS_TEXTFIELD_PASSWORD_LABEL)), + std::move(password)); + disabled_ = MakeRow( + layout, + std::make_unique<Label>(GetStringUTF16(IDS_TEXTFIELD_DISABLED_LABEL)), + std::move(disabled)); + read_only_ = MakeRow( + layout, + std::make_unique<Label>(GetStringUTF16(IDS_TEXTFIELD_READ_ONLY_LABEL)), + std::move(read_only)); + invalid_ = MakeRow( + layout, + std::make_unique<Label>(GetStringUTF16(IDS_TEXTFIELD_INVALID_LABEL)), + std::move(invalid)); + rtl_ = MakeRow( + layout, std::make_unique<Label>(GetStringUTF16(IDS_TEXTFIELD_RTL_LABEL)), + std::move(rtl)); + MakeRow<View, Label>( + layout, nullptr, + std::make_unique<Label>(GetStringUTF16(IDS_TEXTFIELD_NAME_LABEL))); show_password_ = MakeRow<View, LabelButton>( layout, nullptr, - std::make_unique<LabelButton>(this, ASCIIToUTF16("Show password"))); + std::make_unique<LabelButton>( + this, GetStringUTF16(IDS_TEXTFIELD_SHOW_PASSWORD_LABEL))); set_background_ = MakeRow<View, LabelButton>( layout, nullptr, std::make_unique<LabelButton>( - this, ASCIIToUTF16("Set non-default background"))); + this, GetStringUTF16(IDS_TEXTFIELD_BACKGROUND_LABEL))); clear_all_ = MakeRow<View, LabelButton>( layout, nullptr, - std::make_unique<LabelButton>(this, ASCIIToUTF16("Clear All"))); + std::make_unique<LabelButton>(this, + GetStringUTF16(IDS_TEXTFIELD_CLEAR_LABEL))); append_ = MakeRow<View, LabelButton>( layout, nullptr, - std::make_unique<LabelButton>(this, ASCIIToUTF16("Append"))); + std::make_unique<LabelButton>( + this, GetStringUTF16(IDS_TEXTFIELD_APPEND_LABEL))); set_ = MakeRow<View, LabelButton>( layout, nullptr, - std::make_unique<LabelButton>(this, ASCIIToUTF16("Set"))); + std::make_unique<LabelButton>(this, + GetStringUTF16(IDS_TEXTFIELD_SET_LABEL))); set_style_ = MakeRow<View, LabelButton>( layout, nullptr, - std::make_unique<LabelButton>(this, ASCIIToUTF16("Set Styles"))); -} - -void TextfieldExample::ContentsChanged(Textfield* sender, - const base::string16& new_contents) { - if (sender == name_) { - PrintStatus("Name [%s]", UTF16ToUTF8(new_contents).c_str()); - } else if (sender == password_) { - PrintStatus("Password [%s]", UTF16ToUTF8(new_contents).c_str()); - } else { - NOTREACHED(); - } + std::make_unique<LabelButton>( + this, GetStringUTF16(IDS_TEXTFIELD_SET_STYLE_LABEL))); } bool TextfieldExample::HandleKeyEvent(Textfield* sender, @@ -134,7 +140,8 @@ bool TextfieldExample::HandleMouseEvent(Textfield* sender, void TextfieldExample::ButtonPressed(Button* sender, const ui::Event& event) { if (sender == show_password_) { - PrintStatus("Password [%s]", UTF16ToUTF8(password_->GetText()).c_str()); + PrintStatus("Password [%s]", + base::UTF16ToUTF8(password_->GetText()).c_str()); } else if (sender == set_background_) { password_->SetBackgroundColor(gfx::kGoogleRed300); } else if (sender == clear_all_) { @@ -146,19 +153,19 @@ void TextfieldExample::ButtonPressed(Button* sender, const ui::Event& event) { 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]")); + name_->AppendText(GetStringUTF16(IDS_TEXTFIELD_APPEND_UPDATE_TEXT)); + password_->AppendText(GetStringUTF16(IDS_TEXTFIELD_APPEND_UPDATE_TEXT)); + disabled_->SetText(GetStringUTF16(IDS_TEXTFIELD_APPEND_UPDATE_TEXT)); + read_only_->AppendText(GetStringUTF16(IDS_TEXTFIELD_APPEND_UPDATE_TEXT)); + invalid_->AppendText(GetStringUTF16(IDS_TEXTFIELD_APPEND_UPDATE_TEXT)); + rtl_->AppendText(GetStringUTF16(IDS_TEXTFIELD_APPEND_UPDATE_TEXT)); } 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]")); + name_->SetText(GetStringUTF16(IDS_TEXTFIELD_SET_UPDATE_TEXT)); + password_->SetText(GetStringUTF16(IDS_TEXTFIELD_SET_UPDATE_TEXT)); + disabled_->SetText(GetStringUTF16(IDS_TEXTFIELD_SET_UPDATE_TEXT)); + read_only_->SetText(GetStringUTF16(IDS_TEXTFIELD_SET_UPDATE_TEXT)); + invalid_->SetText(GetStringUTF16(IDS_TEXTFIELD_SET_UPDATE_TEXT)); + rtl_->SetText(GetStringUTF16(IDS_TEXTFIELD_SET_UPDATE_TEXT)); } else if (sender == set_style_) { if (!name_->GetText().empty()) { name_->SetColor(SK_ColorGREEN); diff --git a/chromium/ui/views/examples/textfield_example.h b/chromium/ui/views/examples/textfield_example.h index 9ea0144b72d..6ab66ff47f9 100644 --- a/chromium/ui/views/examples/textfield_example.h +++ b/chromium/ui/views/examples/textfield_example.h @@ -31,8 +31,6 @@ class VIEWS_EXAMPLES_EXPORT TextfieldExample : public ExampleBase, private: // TextfieldController: - void ContentsChanged(Textfield* sender, - const base::string16& new_contents) override; bool HandleKeyEvent(Textfield* sender, const ui::KeyEvent& key_event) override; bool HandleMouseEvent(Textfield* sender, diff --git a/chromium/ui/views/examples/throbber_example.cc b/chromium/ui/views/examples/throbber_example.cc index 6ad3f26e663..4d90b9e804c 100644 --- a/chromium/ui/views/examples/throbber_example.cc +++ b/chromium/ui/views/examples/throbber_example.cc @@ -7,7 +7,9 @@ #include <memory> #include "base/macros.h" +#include "ui/base/l10n/l10n_util.h" #include "ui/views/controls/throbber.h" +#include "ui/views/examples/grit/views_examples_resources.h" #include "ui/views/layout/fill_layout.h" #include "ui/views/view.h" @@ -18,8 +20,8 @@ namespace { class ThrobberView : public View { public: - ThrobberView() : throbber_(new Throbber()) { - AddChildView(throbber_); + ThrobberView() { + throbber_ = AddChildView(std::make_unique<Throbber>()); throbber_->Start(); } @@ -57,13 +59,15 @@ class ThrobberView : public View { } // namespace -ThrobberExample::ThrobberExample() : ExampleBase("Throbber") {} +ThrobberExample::ThrobberExample() + : ExampleBase(l10n_util::GetStringUTF8(IDS_THROBBER_SELECT_LABEL).c_str()) { +} ThrobberExample::~ThrobberExample() = default; void ThrobberExample::CreateExampleView(View* container) { container->SetLayoutManager(std::make_unique<FillLayout>()); - container->AddChildView(new ThrobberView()); + container->AddChildView(std::make_unique<ThrobberView>()); } } // namespace examples diff --git a/chromium/ui/views/examples/toggle_button_example.cc b/chromium/ui/views/examples/toggle_button_example.cc index 88084f387e1..3bc279ffc1e 100644 --- a/chromium/ui/views/examples/toggle_button_example.cc +++ b/chromium/ui/views/examples/toggle_button_example.cc @@ -9,24 +9,27 @@ #include "base/strings/stringprintf.h" #include "base/strings/utf_string_conversions.h" +#include "ui/base/l10n/l10n_util.h" #include "ui/gfx/geometry/insets.h" #include "ui/views/controls/button/toggle_button.h" #include "ui/views/examples/examples_window.h" +#include "ui/views/examples/grit/views_examples_resources.h" #include "ui/views/layout/box_layout.h" namespace views { namespace examples { -ToggleButtonExample::ToggleButtonExample() : ExampleBase("Toggle button") {} +ToggleButtonExample::ToggleButtonExample() + : ExampleBase( + l10n_util::GetStringUTF8(IDS_TOGGLE_BUTTON_SELECT_LABEL).c_str()) {} ToggleButtonExample::~ToggleButtonExample() = default; void ToggleButtonExample::CreateExampleView(View* container) { - button_ = new ToggleButton(this); auto layout = std::make_unique<BoxLayout>(BoxLayout::Orientation::kVertical); layout->set_cross_axis_alignment(BoxLayout::CrossAxisAlignment::kCenter); container->SetLayoutManager(std::move(layout)); - container->AddChildView(button_); + button_ = container->AddChildView(std::make_unique<ToggleButton>(this)); } void ToggleButtonExample::ButtonPressed(Button* sender, diff --git a/chromium/ui/views/examples/tree_view_example.cc b/chromium/ui/views/examples/tree_view_example.cc index 0e84bce0823..dea712ae3e9 100644 --- a/chromium/ui/views/examples/tree_view_example.cc +++ b/chromium/ui/views/examples/tree_view_example.cc @@ -7,15 +7,18 @@ #include <utility> #include "base/strings/utf_string_conversions.h" +#include "ui/base/l10n/l10n_util.h" #include "ui/views/controls/button/label_button.h" #include "ui/views/controls/menu/menu_model_adapter.h" #include "ui/views/controls/menu/menu_runner.h" #include "ui/views/controls/scroll_view.h" #include "ui/views/controls/tree/tree_view.h" #include "ui/views/controls/tree/tree_view_drawing_provider.h" +#include "ui/views/examples/grit/views_examples_resources.h" #include "ui/views/layout/grid_layout.h" -using base::ASCIIToUTF16; +using l10n_util::GetStringUTF16; +using l10n_util::GetStringUTF8; namespace { @@ -27,7 +30,7 @@ class ExampleTreeViewDrawingProvider : public views::TreeViewDrawingProvider { base::string16 GetAuxiliaryTextForNode(views::TreeView* tree_view, ui::TreeModelNode* node) override { if (tree_view->GetSelectedNode() == node) - return base::UTF8ToUTF16("Selected"); + return GetStringUTF16(IDS_TREE_VIEW_SELECTED_LABEL); return views::TreeViewDrawingProvider::GetAuxiliaryTextForNode(tree_view, node); } @@ -44,8 +47,10 @@ namespace views { namespace examples { TreeViewExample::TreeViewExample() - : ExampleBase("Tree View"), - model_(std::make_unique<NodeType>(ASCIIToUTF16("root"), 1)) {} + : ExampleBase(GetStringUTF8(IDS_TREE_VIEW_SELECT_LABEL).c_str()), + model_(std::make_unique<NodeType>( + GetStringUTF16(IDS_TREE_VIEW_ROOT_NODE_LABEL), + 1)) {} TreeViewExample::~TreeViewExample() { // Remove the model from the view. @@ -56,15 +61,29 @@ TreeViewExample::~TreeViewExample() { void TreeViewExample::CreateExampleView(View* container) { // Add some sample data. NodeType* colors_node = model_.GetRoot()->Add( - std::make_unique<NodeType>(ASCIIToUTF16("colors"), 1), 0); - colors_node->Add(std::make_unique<NodeType>(ASCIIToUTF16("red"), 1), 0); - colors_node->Add(std::make_unique<NodeType>(ASCIIToUTF16("green"), 1), 1); - colors_node->Add(std::make_unique<NodeType>(ASCIIToUTF16("blue"), 1), 2); + std::make_unique<NodeType>(GetStringUTF16(IDS_TREE_VIEW_COLOR_NODE_LABEL), + 1), + 0); + colors_node->Add(std::make_unique<NodeType>( + GetStringUTF16(IDS_TREE_VIEW_COLOR_RED_LABEL), 1), + 0); + colors_node->Add(std::make_unique<NodeType>( + GetStringUTF16(IDS_TREE_VIEW_COLOR_GREEN_LABEL), 1), + 1); + colors_node->Add(std::make_unique<NodeType>( + GetStringUTF16(IDS_TREE_VIEW_COLOR_BLUE_LABEL), 1), + 2); NodeType* sheep_node = model_.GetRoot()->Add( - std::make_unique<NodeType>(ASCIIToUTF16("sheep"), 1), 0); - sheep_node->Add(std::make_unique<NodeType>(ASCIIToUTF16("Sheep 1"), 1), 0); - sheep_node->Add(std::make_unique<NodeType>(ASCIIToUTF16("Sheep 2"), 1), 1); + std::make_unique<NodeType>(GetStringUTF16(IDS_TREE_VIEW_SHEEP_NODE_LABEL), + 1), + 0); + sheep_node->Add( + std::make_unique<NodeType>(GetStringUTF16(IDS_TREE_VIEW_SHEEP1_LABEL), 1), + 0); + sheep_node->Add( + std::make_unique<NodeType>(GetStringUTF16(IDS_TREE_VIEW_SHEEP2_LABEL), 1), + 1); auto tree_view = std::make_unique<TreeView>(); tree_view->set_context_menu_controller(this); @@ -73,14 +92,16 @@ void TreeViewExample::CreateExampleView(View* container) { tree_view->SetController(this); tree_view->SetDrawingProvider( std::make_unique<ExampleTreeViewDrawingProvider>()); - auto add = std::make_unique<LabelButton>(this, ASCIIToUTF16("Add")); + auto add = std::make_unique<LabelButton>( + this, GetStringUTF16(IDS_TREE_VIEW_ADD_BUTTON_LABEL)); add->SetFocusForPlatform(); add->set_request_focus_on_press(true); - auto remove = std::make_unique<LabelButton>(this, ASCIIToUTF16("Remove")); + auto remove = std::make_unique<LabelButton>( + this, GetStringUTF16(IDS_TREE_VIEW_REMOVE_BUTTON_LABEL)); remove->SetFocusForPlatform(); remove->set_request_focus_on_press(true); - auto change_title = - std::make_unique<LabelButton>(this, ASCIIToUTF16("Change Title")); + auto change_title = std::make_unique<LabelButton>( + this, GetStringUTF16(IDS_TREE_VIEW_CHANGE_TITLE_LABEL)); change_title->SetFocusForPlatform(); change_title->set_request_focus_on_press(true); @@ -90,7 +111,7 @@ void TreeViewExample::CreateExampleView(View* container) { const int tree_view_column = 0; ColumnSet* column_set = layout->AddColumnSet(tree_view_column); column_set->AddColumn(GridLayout::FILL, GridLayout::FILL, 1.0f, - GridLayout::USE_PREF, 0, 0); + GridLayout::ColumnSize::kUsePreferred, 0, 0); layout->StartRow(1 /* expand */, tree_view_column); tree_view_ = tree_view.get(); layout->AddView(TreeView::CreateScrollViewWithTree(std::move(tree_view))); @@ -100,7 +121,7 @@ void TreeViewExample::CreateExampleView(View* container) { column_set = layout->AddColumnSet(button_column); for (size_t i = 0; i < 3; i++) { column_set->AddColumn(GridLayout::FILL, GridLayout::FILL, 1.0f, - GridLayout::USE_PREF, 0, 0); + GridLayout::ColumnSize::kUsePreferred, 0, 0); } layout->StartRow(0 /* no expand */, button_column); @@ -136,7 +157,8 @@ void TreeViewExample::ButtonPressed(Button* sender, const ui::Event& event) { } else if (sender == change_title_) { DCHECK(selected_node); model_.SetTitle(selected_node, - selected_node->GetTitle() + ASCIIToUTF16("new")); + selected_node->GetTitle() + + GetStringUTF16(IDS_TREE_VIEW_NEW_NODE_LABEL)); } } @@ -160,9 +182,12 @@ void TreeViewExample::ShowContextMenuForViewImpl( const gfx::Point& point, ui::MenuSourceType source_type) { context_menu_model_ = std::make_unique<ui::SimpleMenuModel>(this); - context_menu_model_->AddItem(ID_EDIT, ASCIIToUTF16("Edit")); - context_menu_model_->AddItem(ID_REMOVE, ASCIIToUTF16("Remove")); - context_menu_model_->AddItem(ID_ADD, ASCIIToUTF16("Add")); + context_menu_model_->AddItem(ID_EDIT, + GetStringUTF16(IDS_TREE_VIEW_EDIT_BUTTON_LABEL)); + context_menu_model_->AddItem( + ID_REMOVE, GetStringUTF16(IDS_TREE_VIEW_REMOVE_BUTTON_LABEL)); + context_menu_model_->AddItem(ID_ADD, + GetStringUTF16(IDS_TREE_VIEW_ADD_BUTTON_LABEL)); context_menu_runner_ = std::make_unique<MenuRunner>(context_menu_model_.get(), 0); context_menu_runner_->RunMenuAt(source->GetWidget(), nullptr, diff --git a/chromium/ui/views/examples/vector_example.cc b/chromium/ui/views/examples/vector_example.cc index a3cebf68456..c447df47fcf 100644 --- a/chromium/ui/views/examples/vector_example.cc +++ b/chromium/ui/views/examples/vector_example.cc @@ -15,6 +15,7 @@ #include "base/strings/utf_string_conversions.h" #include "base/threading/thread_restrictions.h" #include "build/build_config.h" +#include "ui/base/l10n/l10n_util.h" #include "ui/gfx/geometry/insets.h" #include "ui/gfx/paint_vector_icon.h" #include "ui/views/border.h" @@ -23,10 +24,14 @@ #include "ui/views/controls/image_view.h" #include "ui/views/controls/textfield/textfield.h" #include "ui/views/controls/textfield/textfield_controller.h" +#include "ui/views/examples/grit/views_examples_resources.h" #include "ui/views/layout/box_layout.h" #include "ui/views/layout/fill_layout.h" #include "ui/views/view.h" +using l10n_util::GetStringUTF16; +using l10n_util::GetStringUTF8; + namespace views { namespace examples { @@ -59,20 +64,22 @@ class VectorIconGallery : public View, auto file_chooser = std::make_unique<Textfield>(); file_chooser->SetPlaceholderText( - base::ASCIIToUTF16("Enter a file to read")); + GetStringUTF16(IDS_VECTOR_FILE_SELECT_LABEL)); auto file_container = std::make_unique<View>(); BoxLayout* file_box = file_container->SetLayoutManager(std::make_unique<BoxLayout>( BoxLayout::Orientation::kHorizontal, gfx::Insets(10), 10)); file_chooser_ = file_container->AddChildView(std::move(file_chooser)); file_go_button_ = file_container->AddChildView( - MdTextButton::Create(this, base::ASCIIToUTF16("Render"))); + MdTextButton::Create(this, GetStringUTF16(IDS_VECTOR_RENDER_LABEL))); file_box->SetFlexForView(file_chooser_, 1); AddChildView(std::move(file_container)); - size_input_->SetPlaceholderText(base::ASCIIToUTF16("Size in dip")); + size_input_->SetPlaceholderText( + GetStringUTF16(IDS_VECTOR_DIP_SIZE_DESC_LABEL)); size_input_->set_controller(this); - color_input_->SetPlaceholderText(base::ASCIIToUTF16("Color (AARRGGBB)")); + color_input_->SetPlaceholderText( + GetStringUTF16(IDS_VECTOR_COLOR_DESC_LABEL)); color_input_->set_controller(this); } @@ -105,10 +112,10 @@ class VectorIconGallery : public View, void ButtonPressed(Button* sender, const ui::Event& event) override { DCHECK_EQ(file_go_button_, sender); base::ScopedAllowBlockingForTesting allow_blocking; -#if defined(OS_POSIX) - base::FilePath path(base::UTF16ToUTF8(file_chooser_->GetText())); -#elif defined(OS_WIN) +#if defined(OS_WIN) base::FilePath path(file_chooser_->GetText()); +#else + base::FilePath path(base::UTF16ToUTF8(file_chooser_->GetText())); #endif base::ReadFileToString(path, &contents_); // Skip over comments. @@ -147,13 +154,14 @@ class VectorIconGallery : public View, } // namespace -VectorExample::VectorExample() : ExampleBase("Vector Icon") {} +VectorExample::VectorExample() + : ExampleBase(GetStringUTF8(IDS_VECTOR_SELECT_LABEL).c_str()) {} VectorExample::~VectorExample() = default; void VectorExample::CreateExampleView(View* container) { container->SetLayoutManager(std::make_unique<FillLayout>()); - container->AddChildView(new VectorIconGallery()); + container->AddChildView(std::make_unique<VectorIconGallery>()); } } // namespace examples diff --git a/chromium/ui/views/examples/views_examples_resources.grd b/chromium/ui/views/examples/views_examples_resources.grd new file mode 100644 index 00000000000..207bbd371cf --- /dev/null +++ b/chromium/ui/views/examples/views_examples_resources.grd @@ -0,0 +1,411 @@ +<?xml version="1.0" encoding="UTF-8"?> +<grit base_dir="." latest_public_release="0" current_release="1" + output_all_resource_defines="false" source_lang_id="en" enc_check="möl"> + <outputs> + <output filename="grit/views_examples_resources.h" type="rc_header"> + <emit emit_type='prepend'></emit> + </output> + <output filename="views_examples_resources.pak" type="data_package" lang="en" /> + </outputs> + <release seq="1" allow_pseudo="false"> + <messages fallback_to_english="true"> + <!-- colored dialog example --> + <message translateable="false" name="IDS_COLORED_DIALOG_CHOOSER_CHECKBOX"> + Use Dark Theme + </message> + <message translateable="false" name="IDS_COLORED_DIALOG_CHOOSER_BUTTON"> + Send Feedback + </message> + <message translateable="false" name="IDS_COLORED_DIALOG_CHOOSER_CONFIRM_LABEL"> + Thank you! We received the following response: <ph name="USER_FEEDBACK">$1<ex>I like it.</ex></ph> + </message> + <message translateable="false" name="IDS_COLORED_DIALOG_TITLE"> + Send Feedback to Chromium + </message> + <message translateable="false" name="IDS_COLORED_DIALOG_TEXTFIELD_PLACEHOLDER"> + Please type your quick feedback here... + </message> + <message translateable="false" name="IDS_COLORED_DIALOG_TEXTFIELD_AX_LABEL"> + Input Feedback + </message> + <message translateable="false" name="IDS_COLORED_DIALOG_SUBMIT_BUTTON"> + Submit + </message> + + <!-- layout base example --> + <message translateable="false" name="IDS_LAYOUT_BASE_ADD_LABEL"> + Add + </message> + + <!-- link example --> + <message translateable="false" name="IDS_LINK_SELECT_LABEL"> + Link + </message> + <message translateable="false" name="IDS_LINK_CLICK_PROMPT_LABEL"> + Click me! + </message> + <message translateable="false" name="IDS_LINK_CLICK_CONFIRMED_LABEL"> + Link clicked + </message> + + <!-- Login example --> + <message translateable="false" name="IDS_LOGIN_SELECT_LABEL"> + Login Bubble Dialog + </message> + <message translateable="false" name="IDS_LOGIN_SHOW_BUTTON_LABEL"> + Show + </message> + <message translateable="false" name="IDS_LOGIN_TITLE_LABEL"> + Login Dialog + </message> + <message translateable="false" name="IDS_LOGIN_USERNAME_LABEL"> + Username + </message> + <message translateable="false" name="IDS_LOGIN_PASSWORD_LABEL"> + Password + </message> + <message translateable="false" name="IDS_LOGIN_OK_BUTTON_LABEL"> + LOGIN + </message> + + <!-- menu example --> + <message translateable="false" name="IDS_MENU_DO_SOMETHING_LABEL"> + Do Something + </message> + <message translateable="false" name="IDS_MENU_ASCII_LABEL"> + ASCII + </message> + <message translateable="false" name="IDS_MENU_UTF8_LABEL"> + UTF-8 + </message> + <message translateable="false" name="IDS_MENU_UTF16_LABEL"> + UTF-16 + </message> + <message translateable="false" name="IDS_MENU_APPLE_LABEL"> + Apple + </message> + <message translateable="false" name="IDS_MENU_ORANGE_LABEL"> + Orange + </message> + <message translateable="false" name="IDS_MENU_KIWI_LABEL"> + Kiwi + </message> + <message translateable="false" name="IDS_MENU_GO_HOME_LABEL"> + Go Home + </message> + <message translateable="false" name="IDS_MENU_DO_SOMETHING_2_LABEL"> + Do Something 2 + </message> + <message translateable="false" name="IDS_MENU_SUBMENU_LABEL"> + Submenu + </message> + <message translateable="false" name="IDS_MENU_SELECT_LABEL"> + Menu + </message> + <message translateable="false" name="IDS_MENU_BUTTON_LABEL"> + Open a menu + </message> + + <!-- message box example --> + <message translateable="false" name="IDS_MESSAGE_SELECT_LABEL"> + Message Box View + </message> + <message translateable="false" name="IDS_MESSAGE_INTRO_LABEL"> + Hello, world! + </message> + <message translateable="false" name="IDS_MESSAGE_CHECK_BOX_LABEL"> + Check Box + </message> + <message translateable="false" name="IDS_MESSAGE_STATUS_LABEL"> + Show Status + </message> + <message translateable="false" name="IDS_MESSAGE_TOGGLE_LABEL"> + Toggle Checkbox + </message> + <message translateable="false" name="IDS_MESSAGE_ON_LABEL"> + on + </message> + <message translateable="false" name="IDS_MESSAGE_OFF_LABEL"> + off + </message> + <message translateable="false" name="IDS_MESSAGE_CHECK_SELECTED_LABEL"> + Check Box Selected + </message> + <message translateable="false" name="IDS_MESSAGE_CHECK_NOT_SELECTED_LABEL"> + Check Box Not Selected + </message> + + <!-- multiline example --> + <message translateable="false" name="IDS_MULTILINE_SELECT_LABEL"> + Multiline RenderText + </message> + <message translateable="false" name="IDS_MULTILINE_LABEL"> + views::Label: + </message> + <message translateable="false" name="IDS_MULTILINE_ELIDE_LABEL"> + elide text? + </message> + <message translateable="false" name="IDS_MULTILINE_RENDER_TEXT_LABEL"> + gfx::RenderText: + </message> + <message translateable="false" name="IDS_MULTILINE_SAMPLE_TEXT_LABEL"> + Sample Text: + </message> + + <!-- native theme example --> + <message translateable="false" name="IDS_THEME_SELECT_LABEL"> + Native Theme Colors + </message> + + <!-- progress example --> + <message translateable="false" name="IDS_PROGRESS_SELECT_LABEL"> + Progress Bar + </message> + <message translateable="false" name="IDS_PROGRESS_LOADER_LABEL"> + Infinite Loader: + </message> + <message translateable="false" name="IDS_PROGRESS_LOADER_SHORT_LABEL"> + Infinite loader (very short): + </message> + + <!-- radio button example --> + <message translateable="false" name="IDS_RADIO_BUTTON_SELECT_LABEL"> + Radio Button + </message> + <message translateable="false" name="IDS_RADIO_BUTTON_SELECT_BUTTON_LABEL"> + Select + </message> + <message translateable="false" name="IDS_RADIO_BUTTON_STATUS_LABEL"> + Show Status + </message> + + + <!-- scroll view example --> + <message translateable="false" name="IDS_SCROLL_VIEW_BUTTON_LABEL"> + Button + </message> + <message translateable="false" name="IDS_SCROLL_VIEW_RADIO_BUTTON_LABEL"> + Radio Button + </message> + <message translateable="false" name="IDS_SCROLL_VIEW_SELECT_LABEL"> + Scroll View + </message> + <message translateable="false" name="IDS_SCROLL_VIEW_WIDE_LABEL"> + Wide + </message> + <message translateable="false" name="IDS_SCROLL_VIEW_TALL_LABEL"> + Tall + </message> + <message translateable="false" name="IDS_SCROLL_VIEW_BIG_SQUARE_LABEL"> + Big Square + </message> + <message translateable="false" name="IDS_SCROLL_VIEW_SMALL_SQUARE_LABEL"> + Small Square + </message> + <message translateable="false" name="IDS_SCROLL_VIEW_SCROLL_TO_LABEL"> + Scroll to + </message> + + <!-- slider example --> + <message translateable="false" name="IDS_SLIDER_SELECT_LABEL"> + Slider + </message> + + <!-- tabbed pane example --> + <message translateable="false" name="IDS_TABBED_PANE_SELECT_LABEL"> + Tabbed Pane + </message> + <message translateable="false" name="IDS_TABBED_PANE_ADD_LABEL"> + Add + </message> + <message translateable="false" name="IDS_TABBED_PANE_ADD_1_LABEL"> + Add At 1 + </message> + <message translateable="false" name="IDS_TABBED_PANE_SELECT_1_LABEL"> + Select At 1 + </message> + <message translateable="false" name="IDS_TABBED_PANE_TAB_1_LABEL"> + Tab 1 + </message> + <message translateable="false" name="IDS_TABBED_PANE_TAB_2_LABEL"> + Tab 2 + </message> + <message translateable="false" name="IDS_TABBED_PANE_TAB_3_LABEL"> + Tab 3 + </message> + <message translateable="false" name="IDS_TABBED_PANE_ADDED_LABEL"> + Added + </message> + <message translateable="false" name="IDS_TABBED_PANE_ADDED_1_LABEL"> + Added at 1 + </message> + + <!-- textfield example --> + <message translateable="false" name="IDS_TEXTFIELD_SELECT_LABEL"> + Textfield + </message> + <message translateable="false" name="IDS_TEXTFIELD_PASSWORD_PLACEHOLDER"> + password + </message> + <message translateable="false" name="IDS_TEXTFIELD_DISABLED_PLACEHOLDER"> + disabled + </message> + <message translateable="false" name="IDS_TEXTFFIELD_READ_ONLY_PLACEHOLDER"> + read only + </message> + <message translateable="false" name="IDS_TEXTFIELD_NAME_LABEL"> + Name: + </message> + <message translateable="false" name="IDS_TEXTFIELD_PASSWORD_LABEL"> + Password: + </message> + <message translateable="false" name="IDS_TEXTFIELD_DISABLED_LABEL"> + Disabled: + </message> + <message translateable="false" name="IDS_TEXTFIELD_READ_ONLY_LABEL"> + Read Only: + </message> + <message translateable="false" name="IDS_TEXTFIELD_INVALID_LABEL"> + Invalid: + </message> + <message translateable="false" name="IDS_TEXTFIELD_RTL_LABEL"> + RTL: + </message> + <message translateable="false" name="IDS_TEXTFIELD_SHOW_PASSWORD_LABEL"> + Show password + </message> + <message translateable="false" name="IDS_TEXTFIELD_BACKGROUND_LABEL"> + Set non-default background + </message> + <message translateable="false" name="IDS_TEXTFIELD_CLEAR_LABEL"> + Clear All + </message> + <message translateable="false" name="IDS_TEXTFIELD_APPEND_LABEL"> + Append + </message> + <message translateable="false" name="IDS_TEXTFIELD_SET_LABEL"> + Set + </message> + <message translateable="false" name="IDS_TEXTFIELD_SET_STYLE_LABEL"> + Set Styles + </message> + <message translateable="false" name="IDS_TEXTFIELD_APPEND_UPDATE_TEXT"> + [append] + </message> + <message translateable="false" name="IDS_TEXTFIELD_SET_UPDATE_TEXT"> + [set] + </message> + + <!-- throbber example --> + <message translateable="false" name="IDS_THROBBER_SELECT_LABEL"> + Throbber + </message> + + <!-- toggle button example --> + <message translateable="false" name="IDS_TOGGLE_BUTTON_SELECT_LABEL"> + Toggle Button + </message> + + <!-- tree view example --> + <message translateable="false" name="IDS_TREE_VIEW_SELECTED_LABEL"> + Selected + </message> + <message translateable="false" name="IDS_TREE_VIEW_SELECT_LABEL"> + Tree View + </message> + <message translateable="false" name="IDS_TREE_VIEW_ROOT_NODE_LABEL"> + root + </message> + <message translateable="false" name="IDS_TREE_VIEW_COLOR_NODE_LABEL"> + colors + </message> + <message translateable="false" name="IDS_TREE_VIEW_COLOR_RED_LABEL"> + red + </message> + <message translateable="false" name="IDS_TREE_VIEW_COLOR_GREEN_LABEL"> + green + </message> + <message translateable="false" name="IDS_TREE_VIEW_COLOR_BLUE_LABEL"> + blue + </message> + <message translateable="false" name="IDS_TREE_VIEW_SHEEP_NODE_LABEL"> + sheep + </message> + <message translateable="false" name="IDS_TREE_VIEW_SHEEP1_LABEL"> + Sheep 1 + </message> + <message translateable="false" name="IDS_TREE_VIEW_SHEEP2_LABEL"> + Sheep 2 + </message> + <message translateable="false" name="IDS_TREE_VIEW_ADD_BUTTON_LABEL"> + Add + </message> + <message translateable="false" name="IDS_TREE_VIEW_REMOVE_BUTTON_LABEL"> + Remove + </message> + <message translateable="false" name="IDS_TREE_VIEW_CHANGE_TITLE_LABEL"> + Change Title + </message> + <message translateable="false" name="IDS_TREE_VIEW_NEW_NODE_LABEL"> + new + </message> + <message translateable="false" name="IDS_TREE_VIEW_EDIT_BUTTON_LABEL"> + Edit + </message> + + <!-- vector example --> + <message translateable="false" name="IDS_VECTOR_FILE_SELECT_LABEL"> + Enter a file to read + </message> + <message translateable="false" name="IDS_VECTOR_RENDER_LABEL"> + Render + </message> + <message translateable="false" name="IDS_VECTOR_DIP_SIZE_DESC_LABEL"> + Size in dip + </message> + <message translateable="false" name="IDS_VECTOR_COLOR_DESC_LABEL"> + Color (AARRGGBB) + </message> + <message translateable="false" name="IDS_VECTOR_SELECT_LABEL"> + Vector Icon + </message> + + <!-- webview example --> + <message translateable="false" name="IDS_WEBVIEW_SELECT_LABEL"> + WebView + </message> + + <!-- widget example --> + <message translateable="false" name="IDS_WIDGET_EXTRA_BUTTON"> + Extra button! + </message> + <message translateable="false" name="IDS_WIDGET_FOOTNOTE_LABEL"> + Footnote label! + </message> + <message translateable="false" name="IDS_WIDGET_DIALOG_CONTENTS_LABEL"> + Dialog contents label! + </message> + <message translateable="false" name="IDS_WIDGET_WINDOW_TITLE"> + Dialog Widget Example + </message> + <message translateable="false" name="IDS_WIDGET_SELECT_LABEL"> + Widget + </message> + <message translateable="false" name="IDS_WIDGET_POPUP_BUTTON_LABEL"> + Popup widget + </message> + <message translateable="false" name="IDS_WIDGET_DIALOG_BUTTON_LABEL"> + Dialog widget + </message> + <message translateable="false" name="IDS_WIDGET_MODAL_BUTTON_LABEL"> + Modal Dialog + </message> + <message translateable="false" name="IDS_WIDGET_CHILD_WIDGET_BUTTON_LABEL"> + Child widget + </message> + <message translateable="false" name="IDS_WIDGET_CLOSE_BUTTON_LABEL"> + Close + </message> + </messages> + </release> +</grit>
\ No newline at end of file diff --git a/chromium/ui/views/examples/webview_example.cc b/chromium/ui/views/examples/webview_example.cc index 8058e35c25a..1571dd2fc9e 100644 --- a/chromium/ui/views/examples/webview_example.cc +++ b/chromium/ui/views/examples/webview_example.cc @@ -8,14 +8,16 @@ #include "content/public/browser/browser_context.h" #include "content/public/browser/web_contents.h" +#include "ui/base/l10n/l10n_util.h" #include "ui/views/controls/webview/webview.h" +#include "ui/views/examples/grit/views_examples_resources.h" #include "ui/views/layout/fill_layout.h" namespace views { namespace examples { WebViewExample::WebViewExample(content::BrowserContext* browser_context) - : ExampleBase("WebView"), + : ExampleBase(l10n_util::GetStringUTF8(IDS_WEBVIEW_SELECT_LABEL).c_str()), webview_(nullptr), browser_context_(browser_context) {} diff --git a/chromium/ui/views/examples/widget_example.cc b/chromium/ui/views/examples/widget_example.cc index a09246ab1fb..7a4106184e2 100644 --- a/chromium/ui/views/examples/widget_example.cc +++ b/chromium/ui/views/examples/widget_example.cc @@ -10,16 +10,19 @@ #include "base/macros.h" #include "base/strings/utf_string_conversions.h" #include "build/build_config.h" +#include "ui/base/l10n/l10n_util.h" #include "ui/gfx/geometry/insets.h" #include "ui/views/background.h" #include "ui/views/controls/button/md_text_button.h" #include "ui/views/controls/label.h" +#include "ui/views/examples/grit/views_examples_resources.h" #include "ui/views/layout/box_layout.h" #include "ui/views/view.h" #include "ui/views/widget/widget.h" #include "ui/views/window/dialog_delegate.h" -using base::ASCIIToUTF16; +using l10n_util::GetStringUTF16; +using l10n_util::GetStringUTF8; namespace views { namespace examples { @@ -48,40 +51,45 @@ WidgetDialogExample::WidgetDialogExample() { SetBackground(CreateSolidBackground(SK_ColorGRAY)); SetLayoutManager(std::make_unique<BoxLayout>( BoxLayout::Orientation::kVertical, gfx::Insets(10), 10)); - SetExtraView(MdTextButton::CreateSecondaryUiButton( - nullptr, ASCIIToUTF16("Extra button!"))); - SetFootnoteView(std::make_unique<Label>(ASCIIToUTF16("Footnote label!"))); - AddChildView(new Label(ASCIIToUTF16("Dialog contents label!"))); + SetExtraView( + MdTextButton::Create(nullptr, GetStringUTF16(IDS_WIDGET_EXTRA_BUTTON))); + SetFootnoteView( + std::make_unique<Label>(GetStringUTF16(IDS_WIDGET_FOOTNOTE_LABEL))); + AddChildView(new Label(GetStringUTF16(IDS_WIDGET_DIALOG_CONTENTS_LABEL))); } WidgetDialogExample::~WidgetDialogExample() = default; base::string16 WidgetDialogExample::GetWindowTitle() const { - return ASCIIToUTF16("Dialog Widget Example"); + return GetStringUTF16(IDS_WIDGET_WINDOW_TITLE); } } // namespace -WidgetExample::WidgetExample() : ExampleBase("Widget") {} +WidgetExample::WidgetExample() + : ExampleBase(GetStringUTF8(IDS_WIDGET_SELECT_LABEL).c_str()) {} WidgetExample::~WidgetExample() = default; void WidgetExample::CreateExampleView(View* container) { container->SetLayoutManager(std::make_unique<BoxLayout>( BoxLayout::Orientation::kHorizontal, gfx::Insets(), 10)); - BuildButton(container, "Popup widget", POPUP); - BuildButton(container, "Dialog widget", DIALOG); - BuildButton(container, "Modal Dialog", MODAL_DIALOG); + BuildButton(container, GetStringUTF16(IDS_WIDGET_POPUP_BUTTON_LABEL), POPUP); + BuildButton(container, GetStringUTF16(IDS_WIDGET_DIALOG_BUTTON_LABEL), + DIALOG); + BuildButton(container, GetStringUTF16(IDS_WIDGET_MODAL_BUTTON_LABEL), + MODAL_DIALOG); #if defined(OS_LINUX) // Windows does not support TYPE_CONTROL top-level widgets. - BuildButton(container, "Child widget", CHILD); + BuildButton(container, GetStringUTF16(IDS_WIDGET_CHILD_WIDGET_BUTTON_LABEL), + CHILD); #endif } void WidgetExample::BuildButton(View* container, - const std::string& label, + const base::string16& label, int tag) { - LabelButton* button = new LabelButton(this, ASCIIToUTF16(label)); + LabelButton* button = new LabelButton(this, label); button->SetFocusForPlatform(); button->set_request_focus_on_press(true); button->set_tag(tag); @@ -103,7 +111,8 @@ void WidgetExample::ShowWidget(View* sender, Widget::InitParams params) { contents->SetLayoutManager( std::make_unique<BoxLayout>(BoxLayout::Orientation::kHorizontal)); contents->SetBackground(CreateSolidBackground(SK_ColorGRAY)); - BuildButton(contents, "Close", CLOSE_WIDGET); + BuildButton(contents, GetStringUTF16(IDS_WIDGET_CLOSE_BUTTON_LABEL), + CLOSE_WIDGET); widget->SetContentsView(contents); } diff --git a/chromium/ui/views/examples/widget_example.h b/chromium/ui/views/examples/widget_example.h index 1d8cce31d74..1094f135c01 100644 --- a/chromium/ui/views/examples/widget_example.h +++ b/chromium/ui/views/examples/widget_example.h @@ -36,7 +36,7 @@ class VIEWS_EXAMPLES_EXPORT WidgetExample : public ExampleBase, }; // Construct a button with the specified |label| and |tag| in |container|. - void BuildButton(View* container, const std::string& label, int tag); + void BuildButton(View* container, const base::string16& label, int tag); // Construct a Widget for |sender|, initialize with |params|, and call Show(). void ShowWidget(View* sender, Widget::InitParams params); diff --git a/chromium/ui/views/focus/external_focus_tracker.cc b/chromium/ui/views/focus/external_focus_tracker.cc index 5eef3af77e3..62143e3d6c5 100644 --- a/chromium/ui/views/focus/external_focus_tracker.cc +++ b/chromium/ui/views/focus/external_focus_tracker.cc @@ -6,7 +6,7 @@ #include <memory> -#include "base/logging.h" +#include "base/check.h" #include "ui/views/view.h" #include "ui/views/view_tracker.h" diff --git a/chromium/ui/views/focus/focus_manager.cc b/chromium/ui/views/focus/focus_manager.cc index 462a3e67c80..0b634e3f151 100644 --- a/chromium/ui/views/focus/focus_manager.cc +++ b/chromium/ui/views/focus/focus_manager.cc @@ -9,14 +9,15 @@ #include <vector> #include "base/auto_reset.h" +#include "base/check_op.h" #include "base/i18n/rtl.h" -#include "base/logging.h" #include "build/build_config.h" #include "ui/base/accelerators/accelerator.h" #include "ui/base/ime/input_method.h" #include "ui/base/ime/text_input_client.h" #include "ui/events/event.h" #include "ui/events/keycodes/keyboard_codes.h" +#include "ui/views/bubble/bubble_dialog_delegate_view.h" #include "ui/views/focus/focus_manager_delegate.h" #include "ui/views/focus/focus_search.h" #include "ui/views/focus/widget_focus_manager.h" @@ -312,7 +313,7 @@ View* FocusManager::GetNextFocusableView(View* original_starting_view, // the starting views widget or |widget_|. Widget* widget = starting_view ? starting_view->GetWidget() : original_starting_view->GetWidget(); - if (widget->widget_delegate()->ShouldAdvanceFocusToTopLevelWidget()) + if (widget->widget_delegate()->focus_traverses_out()) widget = widget_; return GetNextFocusableView(nullptr, widget, reverse, true); } @@ -522,7 +523,22 @@ void FocusManager::UnregisterAccelerators(ui::AcceleratorTarget* target) { bool FocusManager::ProcessAccelerator(const ui::Accelerator& accelerator) { if (accelerator_manager_.Process(accelerator)) return true; - return delegate_ && delegate_->ProcessAccelerator(accelerator); + if (delegate_ && delegate_->ProcessAccelerator(accelerator)) + return true; + +#if defined(OS_MACOSX) + // On MacOS accelerators are processed when a bubble is opened without + // manual redirection to bubble anchor widget. Including redirect on MacOS + // breaks processing accelerators by the bubble itself. + return false; +#else + return RedirectAcceleratorToBubbleAnchorWidget(accelerator); +#endif +} + +bool FocusManager::IsAcceleratorRegistered( + const ui::Accelerator& accelerator) const { + return accelerator_manager_.IsRegistered(accelerator); } bool FocusManager::HasPriorityHandler( @@ -591,4 +607,25 @@ void FocusManager::OnViewIsDeleting(View* view) { SetFocusedView(nullptr); } +bool FocusManager::RedirectAcceleratorToBubbleAnchorWidget( + const ui::Accelerator& accelerator) { + Widget* anchor_widget = GetBubbleAnchorWidget(); + if (!anchor_widget) + return false; + + FocusManager* focus_manager = anchor_widget->GetFocusManager(); + if (!focus_manager->IsAcceleratorRegistered(accelerator)) + return false; + + // The parent view must be focused for it to process events. + focus_manager->SetFocusedView(anchor_widget->GetRootView()); + return focus_manager->ProcessAccelerator(accelerator); +} + +Widget* FocusManager::GetBubbleAnchorWidget() { + BubbleDialogDelegateView* widget_delegate = + widget_->widget_delegate()->AsBubbleDialogDelegate(); + return widget_delegate ? widget_delegate->anchor_widget() : nullptr; +} + } // namespace views diff --git a/chromium/ui/views/focus/focus_manager.h b/chromium/ui/views/focus/focus_manager.h index 909a4fee3fe..8c7888aeff3 100644 --- a/chromium/ui/views/focus/focus_manager.h +++ b/chromium/ui/views/focus/focus_manager.h @@ -256,6 +256,9 @@ class VIEWS_EXPORT FocusManager : public ViewObserver { void AddFocusChangeListener(FocusChangeListener* listener); void RemoveFocusChangeListener(FocusChangeListener* listener); + // Whether the given |accelerator| is registered. + bool IsAcceleratorRegistered(const ui::Accelerator& accelerator) const; + // Whether the given |accelerator| has a priority handler associated with it. bool HasPriorityHandler(const ui::Accelerator& accelerator) const; @@ -293,11 +296,10 @@ class VIEWS_EXPORT FocusManager : public ViewObserver { } // Returns the next focusable view. Traversal starts at |starting_view|. If - // |starting_view| is NULL |starting_widget| is consuled to determine which - // Widget to start from. See - // WidgetDelegate::ShouldAdvanceFocusToTopLevelWidget() for details. If both - // |starting_view| and |starting_widget| are NULL, traversal starts at - // |widget_|. + // |starting_view| is null, |starting_widget| is consulted to determine which + // Widget to start from. See WidgetDelegate::Params::focus_traverses_out for + // details. If both |starting_view| and |starting_widget| are null, traversal + // starts at |widget_|. View* GetNextFocusableView(View* starting_view, Widget* starting_widget, bool reverse, @@ -329,6 +331,14 @@ class VIEWS_EXPORT FocusManager : public ViewObserver { // ViewObserver: void OnViewIsDeleting(View* view) override; + // Try to redirect the accelerator to bubble's anchor widget to process it if + // the bubble didn't. + bool RedirectAcceleratorToBubbleAnchorWidget( + const ui::Accelerator& accelerator); + + // Returns bubble's anchor widget. + Widget* GetBubbleAnchorWidget(); + // Whether arrow key traversal is enabled globally. static bool arrow_key_traversal_enabled_; diff --git a/chromium/ui/views/focus/focus_manager_unittest.cc b/chromium/ui/views/focus/focus_manager_unittest.cc index b96e317503e..b4438b0aa46 100644 --- a/chromium/ui/views/focus/focus_manager_unittest.cc +++ b/chromium/ui/views/focus/focus_manager_unittest.cc @@ -54,6 +54,9 @@ class SimpleTestView : public View { SetID(view_id); } + SimpleTestView(const SimpleTestView&) = delete; + SimpleTestView& operator=(const SimpleTestView&) = delete; + void OnFocus() override { event_list_->push_back({ ON_FOCUS, @@ -72,8 +75,6 @@ class SimpleTestView : public View { private: std::vector<FocusTestEvent>* event_list_; - - DISALLOW_COPY_AND_ASSIGN(SimpleTestView); }; // Tests that the appropriate Focus related methods are called when a View @@ -379,6 +380,11 @@ class SelfUnregisteringAcceleratorTarget : public ui::TestAcceleratorTarget { FocusManager* focus_manager) : accelerator_(accelerator), focus_manager_(focus_manager) {} + SelfUnregisteringAcceleratorTarget( + const SelfUnregisteringAcceleratorTarget&) = delete; + SelfUnregisteringAcceleratorTarget& operator=( + const SelfUnregisteringAcceleratorTarget&) = delete; + // ui::TestAcceleratorTarget: bool AcceleratorPressed(const ui::Accelerator& accelerator) override { focus_manager_->UnregisterAccelerator(accelerator, this); @@ -388,8 +394,6 @@ class SelfUnregisteringAcceleratorTarget : public ui::TestAcceleratorTarget { private: ui::Accelerator accelerator_; FocusManager* focus_manager_; - - DISALLOW_COPY_AND_ASSIGN(SelfUnregisteringAcceleratorTarget); }; TEST_F(FocusManagerTest, CallsSelfDeletingAcceleratorTarget) { @@ -438,20 +442,22 @@ class FocusManagerDtorTest : public FocusManagerTest { : FocusManager(widget, nullptr /* delegate */), dtor_tracker_(dtor_tracker) {} + FocusManagerDtorTracked(const FocusManagerDtorTracked&) = delete; + FocusManagerDtorTracked& operator=(const FocusManagerDtorTracked&) = delete; + ~FocusManagerDtorTracked() override { dtor_tracker_->push_back("FocusManagerDtorTracked"); } DtorTrackVector* dtor_tracker_; - - private: - DISALLOW_COPY_AND_ASSIGN(FocusManagerDtorTracked); }; class TestFocusManagerFactory : public FocusManagerFactory { public: explicit TestFocusManagerFactory(DtorTrackVector* dtor_tracker) : dtor_tracker_(dtor_tracker) {} + TestFocusManagerFactory(const TestFocusManagerFactory&) = delete; + TestFocusManagerFactory& operator=(const TestFocusManagerFactory&) = delete; ~TestFocusManagerFactory() override = default; std::unique_ptr<FocusManager> CreateFocusManager(Widget* widget) override { @@ -460,8 +466,6 @@ class FocusManagerDtorTest : public FocusManagerTest { private: DtorTrackVector* dtor_tracker_; - - DISALLOW_COPY_AND_ASSIGN(TestFocusManagerFactory); }; class WindowDtorTracked : public Widget { @@ -506,6 +510,11 @@ class FocusInAboutToRequestFocusFromTabTraversalView : public View { public: FocusInAboutToRequestFocusFromTabTraversalView() = default; + FocusInAboutToRequestFocusFromTabTraversalView( + const FocusInAboutToRequestFocusFromTabTraversalView&) = delete; + FocusInAboutToRequestFocusFromTabTraversalView& operator=( + const FocusInAboutToRequestFocusFromTabTraversalView&) = delete; + void set_view_to_focus(View* view) { view_to_focus_ = view; } void AboutToRequestFocusFromTabTraversal(bool reverse) override { @@ -514,8 +523,6 @@ class FocusInAboutToRequestFocusFromTabTraversalView : public View { private: views::View* view_to_focus_ = nullptr; - - DISALLOW_COPY_AND_ASSIGN(FocusInAboutToRequestFocusFromTabTraversalView); }; } // namespace @@ -643,6 +650,10 @@ class FocusManagerArrowKeyTraversalTest public testing::WithParamInterface<bool> { public: FocusManagerArrowKeyTraversalTest() = default; + FocusManagerArrowKeyTraversalTest(const FocusManagerArrowKeyTraversalTest&) = + delete; + FocusManagerArrowKeyTraversalTest& operator=( + const FocusManagerArrowKeyTraversalTest&) = delete; ~FocusManagerArrowKeyTraversalTest() override = default; // FocusManagerTest overrides: @@ -671,8 +682,6 @@ class FocusManagerArrowKeyTraversalTest base::test::ScopedRestoreICUDefaultLocale restore_locale_; bool previous_arrow_key_traversal_enabled_ = false; - - DISALLOW_COPY_AND_ASSIGN(FocusManagerArrowKeyTraversalTest); }; // Instantiate the Boolean which is used to toggle RTL in @@ -851,26 +860,18 @@ namespace { // ShouldAdvanceFocusToTopLevelWidget(). class AdvanceFocusWidgetDelegate : public WidgetDelegate { public: - explicit AdvanceFocusWidgetDelegate(Widget* widget) - : widget_(widget), should_advance_focus_to_parent_(false) {} + explicit AdvanceFocusWidgetDelegate(Widget* widget) : widget_(widget) {} + AdvanceFocusWidgetDelegate(const AdvanceFocusWidgetDelegate&) = delete; + AdvanceFocusWidgetDelegate& operator=(const AdvanceFocusWidgetDelegate&) = + delete; ~AdvanceFocusWidgetDelegate() override = default; - void set_should_advance_focus_to_parent(bool value) { - should_advance_focus_to_parent_ = value; - } - // WidgetDelegate: - bool ShouldAdvanceFocusToTopLevelWidget() const override { - return should_advance_focus_to_parent_; - } Widget* GetWidget() override { return widget_; } const Widget* GetWidget() const override { return widget_; } private: Widget* widget_; - bool should_advance_focus_to_parent_; - - DISALLOW_COPY_AND_ASSIGN(AdvanceFocusWidgetDelegate); }; class TestBubbleDialogDelegateView : public BubbleDialogDelegateView { @@ -879,8 +880,23 @@ class TestBubbleDialogDelegateView : public BubbleDialogDelegateView { : BubbleDialogDelegateView(anchor, BubbleBorder::NONE) { DialogDelegate::SetButtons(ui::DIALOG_BUTTON_NONE); } + TestBubbleDialogDelegateView(const TestBubbleDialogDelegateView&) = delete; + TestBubbleDialogDelegateView& operator=(const TestBubbleDialogDelegateView&) = + delete; ~TestBubbleDialogDelegateView() override = default; + static TestBubbleDialogDelegateView* CreateAndShowBubble(View* anchor) { + TestBubbleDialogDelegateView* bubble = + new TestBubbleDialogDelegateView(anchor); + Widget* bubble_widget = BubbleDialogDelegateView::CreateBubble(bubble); + bubble_widget->SetFocusTraversableParent( + bubble->anchor_widget()->GetFocusTraversable()); + bubble_widget->SetFocusTraversableParentView(anchor); + bubble->set_close_on_deactivate(false); + bubble_widget->Show(); + return bubble; + } + // If this is called, the bubble will be forced to use a NativeWidgetAura. // If not set, it might get a DesktopNativeWidgetAura depending on the // platform and other factors. @@ -899,8 +915,6 @@ class TestBubbleDialogDelegateView : public BubbleDialogDelegateView { private: bool use_native_widget_aura_ = false; - - DISALLOW_COPY_AND_ASSIGN(TestBubbleDialogDelegateView); }; } // namespace @@ -950,7 +964,7 @@ TEST_F(FocusManagerTest, AdvanceFocusStaysInWidget) { // Allow focus to go to the parent, and focus backwards which should now move // up |widget_view| (in the parent). - delegate->set_should_advance_focus_to_parent(true); + delegate->SetFocusTraversesOut(true); GetFocusManager()->AdvanceFocus(true); EXPECT_EQ(widget_view, GetFocusManager()->GetFocusedView()); } @@ -984,21 +998,15 @@ TEST_F(FocusManagerTest, NavigateIntoAnchoredDialog) { parent3->AddChildView(new View()); BubbleDialogDelegateView* bubble_delegate = - new TestBubbleDialogDelegateView(parent3); - test::WidgetTest::WidgetAutoclosePtr bubble_widget( - BubbleDialogDelegateView::CreateBubble(bubble_delegate)); - bubble_widget->SetFocusTraversableParent( - bubble_delegate->anchor_widget()->GetFocusTraversable()); + TestBubbleDialogDelegateView::CreateAndShowBubble(parent3); + Widget* bubble_widget = bubble_delegate->GetWidget(); - bubble_widget->SetFocusTraversableParentView(parent3); 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(); @@ -1048,20 +1056,15 @@ TEST_F(FocusManagerTest, AnchoredDialogOnContainerView) { GetWidget()->GetRootView()->AddChildView(parent4); BubbleDialogDelegateView* bubble_delegate = - new TestBubbleDialogDelegateView(parent_group); - test::WidgetTest::WidgetAutoclosePtr bubble_widget( - BubbleDialogDelegateView::CreateBubble(bubble_delegate)); - bubble_widget->SetFocusTraversableParent( - bubble_delegate->anchor_widget()->GetFocusTraversable()); - bubble_widget->SetFocusTraversableParentView(parent_group); + TestBubbleDialogDelegateView::CreateAndShowBubble(parent3); + Widget* bubble_widget = bubble_delegate->GetWidget(); + 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(); @@ -1097,14 +1100,8 @@ TEST_F(FocusManagerTest, AnchoredDialogInPane) { View* anchor = pane->AddChildView(std::make_unique<View>()); anchor->SetFocusBehavior(View::FocusBehavior::ALWAYS); - BubbleDialogDelegateView* bubble = new TestBubbleDialogDelegateView(anchor); - test::WidgetTest::WidgetAutoclosePtr bubble_widget( - BubbleDialogDelegateView::CreateBubble(bubble)); - bubble_widget->SetFocusTraversableParent( - bubble->anchor_widget()->GetFocusTraversable()); - bubble_widget->SetFocusTraversableParentView(anchor); - bubble->set_close_on_deactivate(false); - bubble_widget->Show(); + BubbleDialogDelegateView* bubble = + TestBubbleDialogDelegateView::CreateAndShowBubble(anchor); // We need a focusable view inside our bubble to check that focus traverses // in. @@ -1127,6 +1124,9 @@ TEST_F(FocusManagerTest, AnchoredDialogInPane) { class DesktopWidgetFocusManagerTest : public FocusManagerTest { public: DesktopWidgetFocusManagerTest() = default; + DesktopWidgetFocusManagerTest(const DesktopWidgetFocusManagerTest&) = delete; + DesktopWidgetFocusManagerTest& operator=( + const DesktopWidgetFocusManagerTest&) = delete; ~DesktopWidgetFocusManagerTest() override = default; // FocusManagerTest: @@ -1134,9 +1134,6 @@ class DesktopWidgetFocusManagerTest : public FocusManagerTest { set_native_widget_type(NativeWidgetType::kDesktop); FocusManagerTest::SetUp(); } - - private: - DISALLOW_COPY_AND_ASSIGN(DesktopWidgetFocusManagerTest); }; TEST_F(DesktopWidgetFocusManagerTest, AnchoredDialogInDesktopNativeWidgetAura) { @@ -1158,18 +1155,13 @@ TEST_F(DesktopWidgetFocusManagerTest, AnchoredDialogInDesktopNativeWidgetAura) { widget.GetRootView()->AddChildView(parent2); TestBubbleDialogDelegateView* bubble_delegate = - new TestBubbleDialogDelegateView(parent2); + TestBubbleDialogDelegateView::CreateAndShowBubble(parent2); + Widget* bubble_widget = bubble_delegate->GetWidget(); bubble_delegate->UseNativeWidgetAura(); - test::WidgetTest::WidgetAutoclosePtr bubble_widget( - BubbleDialogDelegateView::CreateBubble(bubble_delegate)); - bubble_widget->SetFocusTraversableParent( - bubble_delegate->anchor_widget()->GetFocusTraversable()); - bubble_widget->SetFocusTraversableParentView(parent2); + View* child = new View(); child->SetFocusBehavior(View::FocusBehavior::ALWAYS); bubble_widget->GetRootView()->AddChildView(child); - bubble_delegate->set_close_on_deactivate(false); - bubble_widget->Show(); widget.Activate(); parent1->RequestFocus(); @@ -1234,4 +1226,83 @@ TEST_F(FocusManagerTest, HandlesFocusCycles) { EXPECT_FALSE(left->HasFocus()); } +#if defined(USE_AURA) +class RedirectToParentFocusManagerTest : public FocusManagerTest { + public: + RedirectToParentFocusManagerTest() = default; + RedirectToParentFocusManagerTest(const RedirectToParentFocusManagerTest&) = + delete; + RedirectToParentFocusManagerTest& operator=( + const RedirectToParentFocusManagerTest&) = delete; + ~RedirectToParentFocusManagerTest() override = default; + + // FocusManagerTest: + void SetUp() override { + FocusManagerTest::SetUp(); + + View* anchor = + GetWidget()->GetRootView()->AddChildView(std::make_unique<View>()); + anchor->SetFocusBehavior(View::FocusBehavior::ALWAYS); + + BubbleDialogDelegateView* bubble_delegate = + TestBubbleDialogDelegateView::CreateAndShowBubble(anchor); + Widget* bubble_widget = bubble_delegate->GetWidget(); + + parent_focus_manager_ = anchor->GetFocusManager(); + bubble_focus_manager_ = bubble_widget->GetFocusManager(); + } + + void TearDown() override { + FocusManagerFactory::Install(nullptr); + FocusManagerTest::TearDown(); + } + + protected: + FocusManager* parent_focus_manager_; + FocusManager* bubble_focus_manager_; +}; + +// Test that when an accelerator is sent to a bubble that isn't registered, +// the bubble's parent handles it instead. +TEST_F(RedirectToParentFocusManagerTest, ParentHandlesAcceleratorFromBubble) { + ui::Accelerator return_accelerator(ui::VKEY_RETURN, ui::EF_NONE); + ui::TestAcceleratorTarget parent_return_target(true); + + EXPECT_EQ(0, parent_return_target.accelerator_count()); + parent_focus_manager_->RegisterAccelerator( + return_accelerator, ui::AcceleratorManager::kNormalPriority, + &parent_return_target); + + EXPECT_TRUE( + !bubble_focus_manager_->IsAcceleratorRegistered(return_accelerator)); + // Accelerator was proccesed by the parent. + EXPECT_TRUE(bubble_focus_manager_->ProcessAccelerator(return_accelerator)); + EXPECT_EQ(parent_return_target.accelerator_count(), 1); +} + +// Test that when an accelerator is sent to a bubble that is registered on both +// it and its parent, the bubble handles it. +TEST_F(RedirectToParentFocusManagerTest, BubbleHandlesRegisteredAccelerators) { + ui::Accelerator return_accelerator(ui::VKEY_RETURN, ui::EF_NONE); + ui::TestAcceleratorTarget parent_return_target(true); + ui::TestAcceleratorTarget bubble_return_target(true); + + EXPECT_EQ(0, bubble_return_target.accelerator_count()); + EXPECT_EQ(0, parent_return_target.accelerator_count()); + + bubble_focus_manager_->RegisterAccelerator( + return_accelerator, ui::AcceleratorManager::kNormalPriority, + &bubble_return_target); + parent_focus_manager_->RegisterAccelerator( + return_accelerator, ui::AcceleratorManager::kNormalPriority, + &parent_return_target); + + // Accelerator was proccesed by the bubble and not by the parent. + EXPECT_TRUE(bubble_focus_manager_->ProcessAccelerator(return_accelerator)); + EXPECT_EQ(1, bubble_return_target.accelerator_count()); + EXPECT_EQ(0, parent_return_target.accelerator_count()); +} + +#endif + } // namespace views diff --git a/chromium/ui/views/layout/animating_layout_manager.cc b/chromium/ui/views/layout/animating_layout_manager.cc index a2e72769780..d436f28da5e 100644 --- a/chromium/ui/views/layout/animating_layout_manager.cc +++ b/chromium/ui/views/layout/animating_layout_manager.cc @@ -207,10 +207,10 @@ void AnimatingLayoutManager::AnimationDelegate::AnimationEnded( AnimatingLayoutManager::AnimatingLayoutManager() = default; AnimatingLayoutManager::~AnimatingLayoutManager() = default; -AnimatingLayoutManager& AnimatingLayoutManager::SetShouldAnimateBounds( - bool should_animate_bounds) { - if (should_animate_bounds_ != should_animate_bounds) { - should_animate_bounds_ = should_animate_bounds; +AnimatingLayoutManager& AnimatingLayoutManager::SetBoundsAnimationMode( + BoundsAnimationMode bounds_animation_mode) { + if (bounds_animation_mode_ != bounds_animation_mode) { + bounds_animation_mode_ = bounds_animation_mode; ResetLayout(); } return *this; @@ -329,9 +329,21 @@ gfx::Size AnimatingLayoutManager::GetPreferredSize(const View* host) const { if (!target_layout_manager()) return gfx::Size(); - return should_animate_bounds_ - ? current_layout_.host_size - : target_layout_manager()->GetPreferredSize(host); + switch (bounds_animation_mode_) { + case BoundsAnimationMode::kUseHostBounds: + return target_layout_manager()->GetPreferredSize(host); + case BoundsAnimationMode::kAnimateMainAxis: { + // Animating only main axis, so cross axis is preferred size. + gfx::Size result = current_layout_.host_size; + SetCrossAxis( + &result, orientation(), + GetCrossAxis(orientation(), + target_layout_manager()->GetPreferredSize(host))); + return result; + } + case BoundsAnimationMode::kAnimateBothAxes: + return current_layout_.host_size; + } } gfx::Size AnimatingLayoutManager::GetMinimumSize(const View* host) const { @@ -340,8 +352,20 @@ gfx::Size AnimatingLayoutManager::GetMinimumSize(const View* host) const { // TODO(dfried): consider cases where the minimum size might not be just the // minimum size of the embedded layout. gfx::Size minimum_size = target_layout_manager()->GetMinimumSize(host); - if (should_animate_bounds_) - minimum_size.SetToMin(current_layout_.host_size); + switch (bounds_animation_mode_) { + case BoundsAnimationMode::kUseHostBounds: + // No modification required. + break; + case BoundsAnimationMode::kAnimateMainAxis: + SetMainAxis( + &minimum_size, orientation(), + std::min(GetMainAxis(orientation(), minimum_size), + GetMainAxis(orientation(), current_layout_.host_size))); + break; + case BoundsAnimationMode::kAnimateBothAxes: + minimum_size.SetToMin(current_layout_.host_size); + break; + } return minimum_size; } @@ -351,9 +375,12 @@ int AnimatingLayoutManager::GetPreferredHeightForWidth(const View* host, return 0; // TODO(dfried): revisit this computation. - return should_animate_bounds_ - ? current_layout_.host_size.height() - : target_layout_manager()->GetPreferredHeightForWidth(host, width); + if (bounds_animation_mode_ == BoundsAnimationMode::kAnimateBothAxes || + (bounds_animation_mode_ == BoundsAnimationMode::kAnimateMainAxis && + orientation() == LayoutOrientation::kVertical)) { + return current_layout_.host_size.height(); + } + return target_layout_manager()->GetPreferredHeightForWidth(host, width); } std::vector<View*> AnimatingLayoutManager::GetChildViewsInPaintOrder( @@ -445,31 +472,48 @@ void AnimatingLayoutManager::LayoutImpl() { // than an invalidation. This should reset the layout (but see the note in // RecalculateTarget() below). const gfx::Size host_size = host_view()->size(); - if (should_animate_bounds_) { - // Reset the layout immediately if the current or target layout exceeds the - // host size or the available space. + + if (bounds_animation_mode_ == BoundsAnimationMode::kUseHostBounds) { + if (!cached_layout_size() || host_size != *cached_layout_size()) { + // Host size changed, so reset the layout. + ResetLayoutToTargetSize(); + } + + } else { const SizeBounds available_size = GetAvailableHostSize(); - const base::Optional<int> bounds_main = - GetMainAxis(orientation(), available_size); - const int host_main = GetMainAxis(orientation(), host_size); - const int current_main = - GetMainAxis(orientation(), current_layout_.host_size); - if (current_main > host_main || - (bounds_main && current_main > *bounds_main)) { + + if (bounds_animation_mode_ == BoundsAnimationMode::kAnimateMainAxis && + (!cached_layout_size() || + GetCrossAxis(orientation(), host_size) != + GetCrossAxis(orientation(), *cached_layout_size()))) { + // If we're fixed to the cross-axis size of the host and that size + // changes, we need to reset the layout. last_available_host_size_ = available_size; ResetLayoutToSize(host_size); - } else if (available_size != last_available_host_size_) { - // May need to re-trigger animation if our bounds were relaxed; let us - // expand into the new available space. - RecalculateTarget(); + } else { + // Either both axes are animating or only the main axis is animating or + // the cross axis hasn't changed (because otherwise the previous condition + // would have executed instead). + const base::Optional<int> bounds_main = + GetMainAxis(orientation(), available_size); + const int host_main = GetMainAxis(orientation(), host_size); + const int current_main = + GetMainAxis(orientation(), current_layout_.host_size); + if (current_main > host_main || + (bounds_main && current_main > *bounds_main)) { + // Reset the layout immediately if the current or target layout exceeds + // the host size or the available space. + last_available_host_size_ = available_size; + ResetLayoutToSize(host_size); + } else if (available_size != last_available_host_size_) { + // May need to re-trigger animation if our bounds were relaxed; let us + // expand into the new available space. + RecalculateTarget(); + } } // Verify that the last available size has been updated. DCHECK_EQ(available_size, last_available_host_size_); - - } else if (!cached_layout_size() || host_size != *cached_layout_size()) { - // Host size changed, so reset the layout. - ResetLayoutToTargetSize(); } ApplyLayout(current_layout_); @@ -529,7 +573,8 @@ bool AnimatingLayoutManager::RecalculateTarget() { // space as adjacent child views appear/disappear. This will be useful in // animating tab titles, which currently slide over when the favicon // disappears. - if (!should_animate_bounds_ && *cached_layout_size() != target_size) { + if (bounds_animation_mode_ == BoundsAnimationMode::kUseHostBounds && + *cached_layout_size() != target_size) { ResetLayoutToSize(target_size); return true; } @@ -982,25 +1027,39 @@ ChildLayout AnimatingLayoutManager::CalculateSlideFade( // Returns the space in which to calculate the target layout. gfx::Size AnimatingLayoutManager::GetAvailableTargetLayoutSize() { - if (!should_animate_bounds_) + if (bounds_animation_mode_ == BoundsAnimationMode::kUseHostBounds) return host_view()->size(); const SizeBounds bounds = GetAvailableHostSize(); last_available_host_size_ = bounds; const gfx::Size preferred_size = target_layout_manager()->GetPreferredSize(host_view()); - if (!bounds.width() || *bounds.width() > preferred_size.width()) { - return gfx::Size(preferred_size.width(), - bounds.height() - ? std::min(preferred_size.height(), *bounds.height()) - : preferred_size.height()); + + int width = preferred_size.width(); + + if (orientation() == LayoutOrientation::kVertical && + bounds_animation_mode_ == BoundsAnimationMode::kAnimateMainAxis) { + width = host_view()->width(); + } else if (bounds.width()) { + width = std::min(width, *bounds.width()); + } + + int height; + + if (orientation() == LayoutOrientation::kHorizontal && + bounds_animation_mode_ == BoundsAnimationMode::kAnimateMainAxis) { + height = host_view()->height(); + } else { + height = width < preferred_size.width() + ? target_layout_manager()->GetPreferredHeightForWidth( + host_view(), width) + : preferred_size.height(); + if (bounds.height()) { + height = std::min(height, *bounds.height()); + } } - const int height = target_layout_manager()->GetPreferredHeightForWidth( - host_view(), *bounds.width()); - return gfx::Size(*bounds.width(), bounds.height() - ? std::min(height, *bounds.height()) - : height); + return gfx::Size(width, height); } // static diff --git a/chromium/ui/views/layout/animating_layout_manager.h b/chromium/ui/views/layout/animating_layout_manager.h index 207c1edef77..70561ebe5c7 100644 --- a/chromium/ui/views/layout/animating_layout_manager.h +++ b/chromium/ui/views/layout/animating_layout_manager.h @@ -44,7 +44,8 @@ namespace views { // // auto* animating_layout = button_container->SetLayoutManager( // std::make_unique<AnimatingLayoutManager>()); -// animating_layout->SetShouldAnimateBounds(true); +// animating_layout->SetBoundsAnimationMode( +// AnimatingLayoutManager::BoundsAnimationMode::kAnimateMainAxis); // auto* flex_layout = animating_layout->SetTargetLayoutManager( // std::make_unique<FlexLayout>()); // flex_layout->SetOrientation(LayoutOrientation::kHorizontal) @@ -71,6 +72,26 @@ class VIEWS_EXPORT AnimatingLayoutManager : public LayoutManagerBase { bool is_animating) = 0; }; + // Describes if and how the bounds of the host view can be animated as part of + // layout animations, if the preferred size of the layout changes. + enum BoundsAnimationMode { + // Default behavior: the host view will always take the space given to it by + // its parent view and child views will animate within those bounds. Useful + // for cases where the layout is in a fixed-size container or dialog, but + // we want child views to be able to animate. + kUseHostBounds, + // The host view will request more or less space within the available space + // offered by its parent view, allowing its main axis size to animate, but + // will use exactly the cross-axis space provided, as it would with + // kUseHostBounds. Useful if the host view is in a toolbar or a dialog with + // fixed width but variable height or vice-versa. + kAnimateMainAxis, + // The host view will request more space or less space in both axes within + // the available space offered by its parent view. Useful if the host view + // is in e.g. a dialog that can vary in size. + kAnimateBothAxes + }; + // Describes how a view which is appearing or disappearing during an animation // behaves. Child views which are removed from the parent view always simply // disappear; use one of the Fade methods below to cause a view to fade out. @@ -96,8 +117,11 @@ class VIEWS_EXPORT AnimatingLayoutManager : public LayoutManagerBase { AnimatingLayoutManager(); ~AnimatingLayoutManager() override; - bool should_animate_bounds() const { return should_animate_bounds_; } - AnimatingLayoutManager& SetShouldAnimateBounds(bool should_animate_bounds); + BoundsAnimationMode bounds_animation_mode() const { + return bounds_animation_mode_; + } + AnimatingLayoutManager& SetBoundsAnimationMode( + BoundsAnimationMode bounds_animation_mode); base::TimeDelta animation_duration() const { return animation_duration_; } AnimatingLayoutManager& SetAnimationDuration( @@ -253,11 +277,10 @@ class VIEWS_EXPORT AnimatingLayoutManager : public LayoutManagerBase { const View* view, const SizeBounds& size_bounds); - // Whether or not to animate the bounds of the host view when the preferred - // size of the layout changes. If false, the size will have to be set - // explicitly by the host view's owner. Bounds animation is done by changing - // the preferred size and invalidating the layout. - bool should_animate_bounds_ = false; + // How to animate bounds of the host view when the preferred size of the + // layout changes. + BoundsAnimationMode bounds_animation_mode_ = + BoundsAnimationMode::kUseHostBounds; // How long each animation takes. Depending on how far along an animation is, // a new target layout will either cause the animation to restart or redirect. diff --git a/chromium/ui/views/layout/animating_layout_manager_unittest.cc b/chromium/ui/views/layout/animating_layout_manager_unittest.cc index 896ce78b5f5..8fc53d36838 100644 --- a/chromium/ui/views/layout/animating_layout_manager_unittest.cc +++ b/chromium/ui/views/layout/animating_layout_manager_unittest.cc @@ -15,6 +15,7 @@ #include "ui/gfx/animation/animation_test_api.h" #include "ui/views/layout/fill_layout.h" #include "ui/views/layout/flex_layout.h" +#include "ui/views/layout/normalized_geometry.h" #include "ui/views/test/views_test_base.h" #include "ui/views/view.h" #include "ui/views/view_class_properties.h" @@ -251,7 +252,8 @@ const FlexSpecification AnimatingLayoutManagerTest::kFlex = TEST_F(AnimatingLayoutManagerTest, SetLayoutManager_NoAnimation) { auto test_layout = std::make_unique<TestLayoutManager>(); test_layout->SetLayout(layout1()); - layout()->SetShouldAnimateBounds(true); + layout()->SetBoundsAnimationMode( + AnimatingLayoutManager::BoundsAnimationMode::kAnimateBothAxes); layout()->SetTargetLayoutManager(std::move(test_layout)); SizeAndLayout(); @@ -261,7 +263,8 @@ TEST_F(AnimatingLayoutManagerTest, SetLayoutManager_NoAnimation) { } TEST_F(AnimatingLayoutManagerTest, ResetLayout_NoAnimation) { - layout()->SetShouldAnimateBounds(true); + layout()->SetBoundsAnimationMode( + AnimatingLayoutManager::BoundsAnimationMode::kAnimateBothAxes); auto* const test_layout = layout()->SetTargetLayoutManager(std::make_unique<TestLayoutManager>()); test_layout->SetLayout(layout1()); @@ -274,7 +277,8 @@ TEST_F(AnimatingLayoutManagerTest, ResetLayout_NoAnimation) { } TEST_F(AnimatingLayoutManagerTest, HostInvalidate_TriggersAnimation) { - layout()->SetShouldAnimateBounds(true); + layout()->SetBoundsAnimationMode( + AnimatingLayoutManager::BoundsAnimationMode::kAnimateBothAxes); auto* const test_layout = layout()->SetTargetLayoutManager(std::make_unique<TestLayoutManager>()); test_layout->SetLayout(layout1()); @@ -293,7 +297,8 @@ TEST_F(AnimatingLayoutManagerTest, HostInvalidate_TriggersAnimation) { TEST_F(AnimatingLayoutManagerTest, HostInvalidate_AnimateBounds_AnimationProgresses) { - layout()->SetShouldAnimateBounds(true); + layout()->SetBoundsAnimationMode( + AnimatingLayoutManager::BoundsAnimationMode::kAnimateBothAxes); auto* const test_layout = layout()->SetTargetLayoutManager(std::make_unique<TestLayoutManager>()); test_layout->SetLayout(layout1()); @@ -330,7 +335,8 @@ TEST_F(AnimatingLayoutManagerTest, } TEST_F(AnimatingLayoutManagerTest, HostInvalidate_NoAnimateBounds_NoAnimation) { - layout()->SetShouldAnimateBounds(false); + layout()->SetBoundsAnimationMode( + AnimatingLayoutManager::BoundsAnimationMode::kUseHostBounds); auto* const test_layout = layout()->SetTargetLayoutManager(std::make_unique<TestLayoutManager>()); test_layout->SetLayout(layout1()); @@ -351,7 +357,8 @@ TEST_F(AnimatingLayoutManagerTest, HostInvalidate_NoAnimateBounds_NoAnimation) { } TEST_F(AnimatingLayoutManagerTest, HostResize_NoAnimateBounds_NoAnimation) { - layout()->SetShouldAnimateBounds(false); + layout()->SetBoundsAnimationMode( + AnimatingLayoutManager::BoundsAnimationMode::kUseHostBounds); auto* const test_layout = layout()->SetTargetLayoutManager(std::make_unique<TestLayoutManager>()); test_layout->SetLayout(layout1()); @@ -375,7 +382,8 @@ TEST_F(AnimatingLayoutManagerTest, HostResize_NoAnimateBounds_NoAnimation) { TEST_F(AnimatingLayoutManagerTest, HostInvalidate_NoAnimateBounds_NewLayoutTriggersAnimation) { - layout()->SetShouldAnimateBounds(false); + layout()->SetBoundsAnimationMode( + AnimatingLayoutManager::BoundsAnimationMode::kUseHostBounds); auto* const test_layout = layout()->SetTargetLayoutManager(std::make_unique<TestLayoutManager>()); test_layout->SetLayout(layout1()); @@ -400,7 +408,8 @@ TEST_F(AnimatingLayoutManagerTest, TEST_F(AnimatingLayoutManagerTest, HostInvalidate_NoAnimateBounds_AnimationProgresses) { - layout()->SetShouldAnimateBounds(false); + layout()->SetBoundsAnimationMode( + AnimatingLayoutManager::BoundsAnimationMode::kUseHostBounds); auto* const test_layout = layout()->SetTargetLayoutManager(std::make_unique<TestLayoutManager>()); test_layout->SetLayout(layout1()); @@ -449,7 +458,8 @@ TEST_F(AnimatingLayoutManagerTest, FadeInOutMode_MiddleView_ScaleFromZero) { {{child(0), true, {5, 5, 10, 10}}, {child(1), true, {20, 5, 10, 10}}, {child(2), true, {35, 5, 10, 10}}}}; - layout()->SetShouldAnimateBounds(true); + layout()->SetBoundsAnimationMode( + AnimatingLayoutManager::BoundsAnimationMode::kAnimateBothAxes); layout()->SetDefaultFadeMode( AnimatingLayoutManager::FadeInOutMode::kScaleFromZero); layout()->SetOrientation(LayoutOrientation::kHorizontal); @@ -528,7 +538,8 @@ TEST_F(AnimatingLayoutManagerTest, FadeInOutMode_MiddleView_ScaleFromMinimum) { {{child(0), true, {5, 5, 10, 10}}, {child(1), true, {20, 5, 10, 10}}, {child(2), true, {35, 5, 10, 10}}}}; - layout()->SetShouldAnimateBounds(true); + layout()->SetBoundsAnimationMode( + AnimatingLayoutManager::BoundsAnimationMode::kAnimateBothAxes); layout()->SetDefaultFadeMode( AnimatingLayoutManager::FadeInOutMode::kScaleFromMinimum); layout()->SetOrientation(LayoutOrientation::kHorizontal); @@ -591,7 +602,8 @@ TEST_F(AnimatingLayoutManagerTest, FadeInOutMode_LeadingView_ScaleFromMinimum) { {{child(0), true, {5, 5, 10, 10}}, {child(1), true, {20, 5, 10, 10}}, {child(2), true, {35, 5, 10, 10}}}}; - layout()->SetShouldAnimateBounds(true); + layout()->SetBoundsAnimationMode( + AnimatingLayoutManager::BoundsAnimationMode::kAnimateBothAxes); layout()->SetDefaultFadeMode( AnimatingLayoutManager::FadeInOutMode::kScaleFromMinimum); layout()->SetOrientation(LayoutOrientation::kHorizontal); @@ -654,7 +666,8 @@ TEST_F(AnimatingLayoutManagerTest, {child(1), true, {20, 5, 10, 10}}, {child(2), false}}}; - layout()->SetShouldAnimateBounds(true); + layout()->SetBoundsAnimationMode( + AnimatingLayoutManager::BoundsAnimationMode::kAnimateBothAxes); layout()->SetDefaultFadeMode( AnimatingLayoutManager::FadeInOutMode::kScaleFromMinimum); layout()->SetOrientation(LayoutOrientation::kHorizontal); @@ -718,7 +731,8 @@ TEST_F(AnimatingLayoutManagerTest, {{child(0), true, {5, 5, 10, 10}}, {child(1), true, {20, 5, 10, 10}}, {child(2), true, {35, 5, 10, 10}}}}; - layout()->SetShouldAnimateBounds(true); + layout()->SetBoundsAnimationMode( + AnimatingLayoutManager::BoundsAnimationMode::kAnimateBothAxes); layout()->SetDefaultFadeMode( AnimatingLayoutManager::FadeInOutMode::kScaleFromMinimum); layout()->SetOrientation(LayoutOrientation::kHorizontal); @@ -782,7 +796,8 @@ TEST_F(AnimatingLayoutManagerTest, {{child(0), true, {5, 5, 10, 10}}, {child(1), true, {5, 20, 10, 10}}, {child(2), true, {5, 35, 10, 10}}}}; - layout()->SetShouldAnimateBounds(true); + layout()->SetBoundsAnimationMode( + AnimatingLayoutManager::BoundsAnimationMode::kAnimateBothAxes); layout()->SetDefaultFadeMode( AnimatingLayoutManager::FadeInOutMode::kScaleFromMinimum); layout()->SetOrientation(LayoutOrientation::kVertical); @@ -839,7 +854,8 @@ TEST_F(AnimatingLayoutManagerTest, TEST_F(AnimatingLayoutManagerTest, FadeInOutMode_Hide_HidesViewDuringAnimation) { - layout()->SetShouldAnimateBounds(false); + layout()->SetBoundsAnimationMode( + AnimatingLayoutManager::BoundsAnimationMode::kUseHostBounds); layout()->SetDefaultFadeMode(AnimatingLayoutManager::FadeInOutMode::kHide); layout()->SetOrientation(LayoutOrientation::kVertical); FlexLayout* const flex_layout = @@ -881,7 +897,8 @@ TEST_F(AnimatingLayoutManagerTest, TEST_F(AnimatingLayoutManagerTest, FadeInOutMode_Hide_HidesViewDuringAnimation_OneFrame) { - layout()->SetShouldAnimateBounds(false); + layout()->SetBoundsAnimationMode( + AnimatingLayoutManager::BoundsAnimationMode::kUseHostBounds); layout()->SetDefaultFadeMode(AnimatingLayoutManager::FadeInOutMode::kHide); layout()->SetOrientation(LayoutOrientation::kVertical); FlexLayout* const flex_layout = @@ -916,7 +933,8 @@ TEST_F(AnimatingLayoutManagerTest, TEST_F(AnimatingLayoutManagerTest, FadeInOutMode_Hide_AnimationResetDuringHide) { - layout()->SetShouldAnimateBounds(false); + layout()->SetBoundsAnimationMode( + AnimatingLayoutManager::BoundsAnimationMode::kUseHostBounds); layout()->SetDefaultFadeMode(AnimatingLayoutManager::FadeInOutMode::kHide); layout()->SetOrientation(LayoutOrientation::kVertical); FlexLayout* const flex_layout = @@ -957,7 +975,8 @@ TEST_F(AnimatingLayoutManagerTest, FadeInOutMode_SlideFromLeading_LastView) { {child(1), true, {20, 5, 10, 10}}, {child(2), true, {35, 5, 10, 10}}}}; - layout()->SetShouldAnimateBounds(true); + layout()->SetBoundsAnimationMode( + AnimatingLayoutManager::BoundsAnimationMode::kAnimateBothAxes); layout()->SetDefaultFadeMode( AnimatingLayoutManager::FadeInOutMode::kSlideFromLeadingEdge); layout()->SetOrientation(LayoutOrientation::kHorizontal); @@ -1004,7 +1023,8 @@ TEST_F(AnimatingLayoutManagerTest, FadeInOutMode_SlideFromLeading_Vertical) { {child(1), true, {5, 20, 10, 10}}, {child(2), true, {5, 35, 10, 10}}}}; - layout()->SetShouldAnimateBounds(true); + layout()->SetBoundsAnimationMode( + AnimatingLayoutManager::BoundsAnimationMode::kAnimateBothAxes); layout()->SetDefaultFadeMode( AnimatingLayoutManager::FadeInOutMode::kSlideFromLeadingEdge); layout()->SetOrientation(LayoutOrientation::kVertical); @@ -1052,7 +1072,8 @@ TEST_F(AnimatingLayoutManagerTest, FadeInOutMode_SlideFromLeading_MiddleView) { {child(1), false}, {child(2), true, {20, 5, 10, 10}}}}; - layout()->SetShouldAnimateBounds(true); + layout()->SetBoundsAnimationMode( + AnimatingLayoutManager::BoundsAnimationMode::kAnimateBothAxes); layout()->SetDefaultFadeMode( AnimatingLayoutManager::FadeInOutMode::kSlideFromLeadingEdge); layout()->SetOrientation(LayoutOrientation::kHorizontal); @@ -1102,7 +1123,8 @@ TEST_F(AnimatingLayoutManagerTest, {child(1), true, {5, 5, 5, 10}}, {child(2), true, {20, 5, 10, 10}}}}; - layout()->SetShouldAnimateBounds(true); + layout()->SetBoundsAnimationMode( + AnimatingLayoutManager::BoundsAnimationMode::kAnimateBothAxes); layout()->SetDefaultFadeMode( AnimatingLayoutManager::FadeInOutMode::kSlideFromLeadingEdge); layout()->SetOrientation(LayoutOrientation::kHorizontal); @@ -1151,7 +1173,8 @@ TEST_F(AnimatingLayoutManagerTest, FadeInOutMode_SlideFromTrailing_MiddleView) { {child(1), false}, {child(2), true, {20, 5, 10, 10}}}}; - layout()->SetShouldAnimateBounds(true); + layout()->SetBoundsAnimationMode( + AnimatingLayoutManager::BoundsAnimationMode::kAnimateBothAxes); layout()->SetDefaultFadeMode( AnimatingLayoutManager::FadeInOutMode::kSlideFromTrailingEdge); layout()->SetOrientation(LayoutOrientation::kHorizontal); @@ -1191,7 +1214,8 @@ TEST_F(AnimatingLayoutManagerTest, FadeInOutMode_SlideFromTrailing_MiddleView) { TEST_F(AnimatingLayoutManagerTest, FlexLayout_FadeOutOnVisibilitySet) { constexpr gfx::Insets kChildMargins(5); - layout()->SetShouldAnimateBounds(false); + layout()->SetBoundsAnimationMode( + AnimatingLayoutManager::BoundsAnimationMode::kUseHostBounds); layout()->SetOrientation(LayoutOrientation::kHorizontal); layout()->SetDefaultFadeMode( AnimatingLayoutManager::FadeInOutMode::kScaleFromZero); @@ -1247,7 +1271,8 @@ TEST_F(AnimatingLayoutManagerTest, FlexLayout_FadeOutOnVisibilitySet) { TEST_F(AnimatingLayoutManagerTest, FlexLayout_FadeInOnVisibilitySet) { constexpr gfx::Insets kChildMargins(5); - layout()->SetShouldAnimateBounds(false); + layout()->SetBoundsAnimationMode( + AnimatingLayoutManager::BoundsAnimationMode::kUseHostBounds); layout()->SetOrientation(LayoutOrientation::kHorizontal); layout()->SetDefaultFadeMode( AnimatingLayoutManager::FadeInOutMode::kScaleFromZero); @@ -1306,7 +1331,8 @@ TEST_F(AnimatingLayoutManagerTest, FlexLayout_FadeInOnVisibilitySet) { TEST_F(AnimatingLayoutManagerTest, FlexLayout_AnimateOutOnDescendentVisbilitySet) { constexpr gfx::Insets kChildMargins(5); - layout()->SetShouldAnimateBounds(false); + layout()->SetBoundsAnimationMode( + AnimatingLayoutManager::BoundsAnimationMode::kUseHostBounds); layout()->SetOrientation(LayoutOrientation::kHorizontal); layout()->SetDefaultFadeMode( AnimatingLayoutManager::FadeInOutMode::kScaleFromZero); @@ -1368,7 +1394,8 @@ TEST_F(AnimatingLayoutManagerTest, TEST_F(AnimatingLayoutManagerTest, FlexLayout_AnimateInOnDescendentVisbilitySet) { constexpr gfx::Insets kChildMargins(5); - layout()->SetShouldAnimateBounds(false); + layout()->SetBoundsAnimationMode( + AnimatingLayoutManager::BoundsAnimationMode::kUseHostBounds); layout()->SetOrientation(LayoutOrientation::kHorizontal); layout()->SetDefaultFadeMode( AnimatingLayoutManager::FadeInOutMode::kScaleFromZero); @@ -1430,7 +1457,8 @@ TEST_F(AnimatingLayoutManagerTest, // Regression test for crbug.com/1037625: crash in SetViewVisibility() (1/2) TEST_F(AnimatingLayoutManagerTest, FlexLayout_RemoveFadingViewDoesNotCrash) { constexpr gfx::Insets kChildMargins(5); - layout()->SetShouldAnimateBounds(false); + layout()->SetBoundsAnimationMode( + AnimatingLayoutManager::BoundsAnimationMode::kUseHostBounds); layout()->SetOrientation(LayoutOrientation::kHorizontal); auto* const flex_layout = layout()->SetTargetLayoutManager(std::make_unique<FlexLayout>()); @@ -1473,7 +1501,8 @@ TEST_F(AnimatingLayoutManagerTest, FlexLayout_RemoveFadingViewDoesNotCrash) { // Regression test for crbug.com/1037625: crash in SetViewVisibility() (2/2) TEST_F(AnimatingLayoutManagerTest, FlexLayout_RemoveShowingViewDoesNotCrash) { constexpr gfx::Insets kChildMargins(5); - layout()->SetShouldAnimateBounds(false); + layout()->SetBoundsAnimationMode( + AnimatingLayoutManager::BoundsAnimationMode::kUseHostBounds); layout()->SetOrientation(LayoutOrientation::kHorizontal); child(1)->SetVisible(false); auto* const flex_layout = @@ -1509,7 +1538,8 @@ TEST_F(AnimatingLayoutManagerTest, FlexLayout_RemoveShowingViewDoesNotCrash) { // Regression test for crbug.com/1037947 (1/2) TEST_F(AnimatingLayoutManagerTest, FlexLayout_DoubleSlide) { - layout()->SetShouldAnimateBounds(true); + layout()->SetBoundsAnimationMode( + AnimatingLayoutManager::BoundsAnimationMode::kAnimateBothAxes); layout()->SetOrientation(LayoutOrientation::kHorizontal); layout()->SetDefaultFadeMode( AnimatingLayoutManager::FadeInOutMode::kSlideFromTrailingEdge); @@ -1596,7 +1626,8 @@ TEST_F(AnimatingLayoutManagerTest, FlexLayout_DoubleSlide) { // [A] [B] // TEST_F(AnimatingLayoutManagerTest, FlexLayout_RedirectAfterExchangePlaces) { - layout()->SetShouldAnimateBounds(true); + layout()->SetBoundsAnimationMode( + AnimatingLayoutManager::BoundsAnimationMode::kAnimateBothAxes); layout()->SetOrientation(LayoutOrientation::kHorizontal); layout()->SetDefaultFadeMode( AnimatingLayoutManager::FadeInOutMode::kSlideFromLeadingEdge); @@ -1647,7 +1678,8 @@ TEST_F(AnimatingLayoutManagerTest, FlexLayout_RedirectAfterExchangePlaces) { TEST_F(AnimatingLayoutManagerTest, FlexLayout_PostDelayedActionAfterFadeIn_AnimateNewViewIn) { child(0)->SetVisible(false); - layout()->SetShouldAnimateBounds(true); + layout()->SetBoundsAnimationMode( + AnimatingLayoutManager::BoundsAnimationMode::kAnimateBothAxes); layout()->SetOrientation(LayoutOrientation::kHorizontal); auto* const flex_layout = layout()->SetTargetLayoutManager(std::make_unique<FlexLayout>()); @@ -1686,7 +1718,8 @@ TEST_F(AnimatingLayoutManagerTest, TEST_F(AnimatingLayoutManagerTest, FlexLayout_PostDelayedActionAfterFadeIn_SwapTwoViews) { child(0)->SetVisible(false); - layout()->SetShouldAnimateBounds(false); + layout()->SetBoundsAnimationMode( + AnimatingLayoutManager::BoundsAnimationMode::kUseHostBounds); layout()->SetOrientation(LayoutOrientation::kHorizontal); auto* const flex_layout = layout()->SetTargetLayoutManager(std::make_unique<FlexLayout>()); @@ -1726,7 +1759,8 @@ TEST_F(AnimatingLayoutManagerTest, // change and FadeIn() was called. TEST_F(AnimatingLayoutManagerTest, FlexLayout_PostDelayedActionAfterFadeIn_FadeInHiddenView) { - layout()->SetShouldAnimateBounds(false); + layout()->SetBoundsAnimationMode( + AnimatingLayoutManager::BoundsAnimationMode::kUseHostBounds); layout()->SetOrientation(LayoutOrientation::kHorizontal); auto* const flex_layout = layout()->SetTargetLayoutManager(std::make_unique<FlexLayout>()); @@ -1779,7 +1813,8 @@ TEST_F(AnimatingLayoutManagerTest, RemoveDuringAnimationDoesntCrash) { const ProposedLayout final_layout{ {20, 20}, {{child(0), true, {5, 5, 10, 10}}, {child(1), false}, {child(2), false}}}; - layout()->SetShouldAnimateBounds(true); + layout()->SetBoundsAnimationMode( + AnimatingLayoutManager::BoundsAnimationMode::kAnimateBothAxes); layout()->SetDefaultFadeMode( AnimatingLayoutManager::FadeInOutMode::kSlideFromLeadingEdge); layout()->SetOrientation(LayoutOrientation::kHorizontal); @@ -1809,7 +1844,8 @@ TEST_F(AnimatingLayoutManagerTest, RemoveDuringAnimationDoesntCrash) { TEST_F(AnimatingLayoutManagerTest, FlexLayout_FadeInOnAdded) { constexpr gfx::Insets kChildMargins(5); - layout()->SetShouldAnimateBounds(false); + layout()->SetBoundsAnimationMode( + AnimatingLayoutManager::BoundsAnimationMode::kUseHostBounds); layout()->SetOrientation(LayoutOrientation::kHorizontal); layout()->SetDefaultFadeMode( AnimatingLayoutManager::FadeInOutMode::kScaleFromZero); @@ -1869,7 +1905,8 @@ TEST_F(AnimatingLayoutManagerTest, FlexLayout_FadeInOnAdded) { TEST_F(AnimatingLayoutManagerTest, FlexLayout_FadeIn) { constexpr gfx::Insets kChildMargins(5); - layout()->SetShouldAnimateBounds(false); + layout()->SetBoundsAnimationMode( + AnimatingLayoutManager::BoundsAnimationMode::kUseHostBounds); layout()->SetOrientation(LayoutOrientation::kHorizontal); layout()->SetDefaultFadeMode( AnimatingLayoutManager::FadeInOutMode::kScaleFromZero); @@ -1926,7 +1963,8 @@ TEST_F(AnimatingLayoutManagerTest, FlexLayout_FadeIn) { TEST_F(AnimatingLayoutManagerTest, FlexLayout_FadeOut) { constexpr gfx::Insets kChildMargins(5); - layout()->SetShouldAnimateBounds(false); + layout()->SetBoundsAnimationMode( + AnimatingLayoutManager::BoundsAnimationMode::kUseHostBounds); layout()->SetOrientation(LayoutOrientation::kHorizontal); layout()->SetDefaultFadeMode( AnimatingLayoutManager::FadeInOutMode::kScaleFromZero); @@ -1982,7 +2020,8 @@ TEST_F(AnimatingLayoutManagerTest, FlexLayout_FadeOut) { TEST_F(AnimatingLayoutManagerTest, FlexLayout_FadeOut_NoCrashOnRemove) { constexpr gfx::Insets kChildMargins(5); - layout()->SetShouldAnimateBounds(false); + layout()->SetBoundsAnimationMode( + AnimatingLayoutManager::BoundsAnimationMode::kUseHostBounds); layout()->SetOrientation(LayoutOrientation::kHorizontal); layout()->SetDefaultFadeMode( AnimatingLayoutManager::FadeInOutMode::kScaleFromZero); @@ -2040,7 +2079,8 @@ TEST_F(AnimatingLayoutManagerTest, FlexLayout_FadeOut_NoCrashOnRemove) { TEST_F(AnimatingLayoutManagerTest, FlexLayout_FadeOut_IgnoreChildView) { constexpr gfx::Insets kChildMargins(5); - layout()->SetShouldAnimateBounds(false); + layout()->SetBoundsAnimationMode( + AnimatingLayoutManager::BoundsAnimationMode::kUseHostBounds); layout()->SetOrientation(LayoutOrientation::kHorizontal); layout()->SetDefaultFadeMode( AnimatingLayoutManager::FadeInOutMode::kScaleFromZero); @@ -2094,7 +2134,8 @@ TEST_F(AnimatingLayoutManagerTest, FlexLayout_FadeOut_IgnoreChildView) { // which is hidden, and that such a layout change triggers animation. TEST_F(AnimatingLayoutManagerTest, FlexLayout_SlideAfterViewHidden) { constexpr gfx::Insets kChildMargins(5); - layout()->SetShouldAnimateBounds(false); + layout()->SetBoundsAnimationMode( + AnimatingLayoutManager::BoundsAnimationMode::kUseHostBounds); auto* const flex_layout = layout()->SetTargetLayoutManager(std::make_unique<FlexLayout>()); flex_layout->SetOrientation(LayoutOrientation::kHorizontal); @@ -2146,7 +2187,8 @@ TEST_F(AnimatingLayoutManagerTest, FlexLayout_SlideAfterViewHidden) { // which is removed, and that such a layout change triggers animation. TEST_F(AnimatingLayoutManagerTest, FlexLayout_SlideAfterViewRemoved) { constexpr gfx::Insets kChildMargins(5); - layout()->SetShouldAnimateBounds(false); + layout()->SetBoundsAnimationMode( + AnimatingLayoutManager::BoundsAnimationMode::kUseHostBounds); auto* const flex_layout = layout()->SetTargetLayoutManager(std::make_unique<FlexLayout>()); flex_layout->SetOrientation(LayoutOrientation::kHorizontal); @@ -2198,7 +2240,8 @@ TEST_F(AnimatingLayoutManagerTest, FlexLayout_SlideAfterViewRemoved) { // the animation redirects. TEST_F(AnimatingLayoutManagerTest, FlexLayout_RedirectAnimation) { constexpr gfx::Insets kChildMargins(5); - layout()->SetShouldAnimateBounds(false); + layout()->SetBoundsAnimationMode( + AnimatingLayoutManager::BoundsAnimationMode::kUseHostBounds); auto* const flex_layout = layout()->SetTargetLayoutManager(std::make_unique<FlexLayout>()); flex_layout->SetOrientation(LayoutOrientation::kHorizontal); @@ -2257,7 +2300,8 @@ TEST_F(AnimatingLayoutManagerTest, FlexLayout_RedirectAnimation) { // of the animation, the animation resets. TEST_F(AnimatingLayoutManagerTest, FlexLayout_ResetAnimation) { constexpr gfx::Insets kChildMargins(5); - layout()->SetShouldAnimateBounds(false); + layout()->SetBoundsAnimationMode( + AnimatingLayoutManager::BoundsAnimationMode::kUseHostBounds); auto* const flex_layout = layout()->SetTargetLayoutManager(std::make_unique<FlexLayout>()); flex_layout->SetOrientation(LayoutOrientation::kHorizontal); @@ -2317,7 +2361,8 @@ TEST_F(AnimatingLayoutManagerTest, FlexLayout_ResetAnimation) { } TEST_F(AnimatingLayoutManagerTest, TestEvents) { - layout()->SetShouldAnimateBounds(true); + layout()->SetBoundsAnimationMode( + AnimatingLayoutManager::BoundsAnimationMode::kAnimateBothAxes); auto* const test_layout = layout()->SetTargetLayoutManager(std::make_unique<TestLayoutManager>()); test_layout->SetLayout(layout1()); @@ -2348,7 +2393,8 @@ TEST_F(AnimatingLayoutManagerTest, TestEvents) { } TEST_F(AnimatingLayoutManagerTest, PostOrQueueAction) { - layout()->SetShouldAnimateBounds(true); + layout()->SetBoundsAnimationMode( + AnimatingLayoutManager::BoundsAnimationMode::kAnimateBothAxes); auto* const test_layout = layout()->SetTargetLayoutManager(std::make_unique<TestLayoutManager>()); test_layout->SetLayout(layout1()); @@ -2400,7 +2446,8 @@ TEST_F(AnimatingLayoutManagerTest, PostOrQueueAction) { } TEST_F(AnimatingLayoutManagerTest, PostOrQueueAction_ContinueAnimation) { - layout()->SetShouldAnimateBounds(true); + layout()->SetBoundsAnimationMode( + AnimatingLayoutManager::BoundsAnimationMode::kAnimateBothAxes); auto* const test_layout = layout()->SetTargetLayoutManager(std::make_unique<TestLayoutManager>()); test_layout->SetLayout(layout1()); @@ -2462,7 +2509,8 @@ TEST_F(AnimatingLayoutManagerTest, PostOrQueueAction_ContinueAnimation) { } TEST_F(AnimatingLayoutManagerTest, PostOrQueueAction_NeverFinishes) { - layout()->SetShouldAnimateBounds(true); + layout()->SetBoundsAnimationMode( + AnimatingLayoutManager::BoundsAnimationMode::kAnimateBothAxes); auto* const test_layout = layout()->SetTargetLayoutManager(std::make_unique<TestLayoutManager>()); test_layout->SetLayout(layout1()); @@ -2505,7 +2553,8 @@ TEST_F(AnimatingLayoutManagerTest, PostOrQueueAction_NeverFinishes) { } TEST_F(AnimatingLayoutManagerTest, PostOrQueueAction_MayPostImmediately) { - layout()->SetShouldAnimateBounds(true); + layout()->SetBoundsAnimationMode( + AnimatingLayoutManager::BoundsAnimationMode::kAnimateBothAxes); auto* const test_layout = layout()->SetTargetLayoutManager(std::make_unique<TestLayoutManager>()); test_layout->SetLayout(layout1()); @@ -2563,7 +2612,8 @@ TEST_F(AnimatingLayoutManagerTest, ZOrder_UnchangedWhenNotAnimating) { } TEST_F(AnimatingLayoutManagerTest, ZOrder_UnchangedWhenNotFading) { - layout()->SetShouldAnimateBounds(true); + layout()->SetBoundsAnimationMode( + AnimatingLayoutManager::BoundsAnimationMode::kAnimateBothAxes); auto* const test_layout = layout()->SetTargetLayoutManager(std::make_unique<TestLayoutManager>()); test_layout->SetLayout(layout1()); @@ -2603,7 +2653,8 @@ TEST_F(AnimatingLayoutManagerTest, ZOrder_FadingOutViewMovedToBack) { const std::vector<View*> expected_order{child(1), child(0), child(2)}; - layout()->SetShouldAnimateBounds(true); + layout()->SetBoundsAnimationMode( + AnimatingLayoutManager::BoundsAnimationMode::kAnimateBothAxes); layout()->SetDefaultFadeMode( AnimatingLayoutManager::FadeInOutMode::kScaleFromZero); auto* const test_layout = @@ -2642,7 +2693,8 @@ TEST_F(AnimatingLayoutManagerTest, ZOrder_FadingInViewMovedToBack) { const std::vector<View*> expected_order{child(1), child(0), child(2)}; - layout()->SetShouldAnimateBounds(true); + layout()->SetBoundsAnimationMode( + AnimatingLayoutManager::BoundsAnimationMode::kAnimateBothAxes); layout()->SetDefaultFadeMode( AnimatingLayoutManager::FadeInOutMode::kScaleFromZero); auto* const test_layout = @@ -2669,7 +2721,8 @@ TEST_F(AnimatingLayoutManagerTest, ZOrder_FadingInViewMovedToBack) { } TEST_F(AnimatingLayoutManagerTest, ConstrainedSpace_StopsAnimation) { - layout()->SetShouldAnimateBounds(true); + layout()->SetBoundsAnimationMode( + AnimatingLayoutManager::BoundsAnimationMode::kAnimateBothAxes); auto* const test_layout = layout()->SetTargetLayoutManager(std::make_unique<TestLayoutManager>()); test_layout->SetLayout(layout1()); @@ -2689,7 +2742,8 @@ TEST_F(AnimatingLayoutManagerTest, ConstrainedSpace_StopsAnimation) { } TEST_F(AnimatingLayoutManagerTest, ConstrainedSpace_TriggersDelayedAction) { - layout()->SetShouldAnimateBounds(true); + layout()->SetBoundsAnimationMode( + AnimatingLayoutManager::BoundsAnimationMode::kAnimateBothAxes); auto* const test_layout = layout()->SetTargetLayoutManager(std::make_unique<TestLayoutManager>()); test_layout->SetLayout(layout1()); @@ -2717,7 +2771,8 @@ TEST_F(AnimatingLayoutManagerTest, ConstrainedSpace_TriggersDelayedAction) { } TEST_F(AnimatingLayoutManagerTest, ConstrainedSpace_SubsequentAnimation) { - layout()->SetShouldAnimateBounds(true); + layout()->SetBoundsAnimationMode( + AnimatingLayoutManager::BoundsAnimationMode::kAnimateBothAxes); auto* const test_layout = layout()->SetTargetLayoutManager(std::make_unique<TestLayoutManager>()); test_layout->SetLayout(layout1()); @@ -2753,12 +2808,18 @@ constexpr base::TimeDelta kMinimumAnimationTime = // invalidated. class ImmediateLayoutManager : public LayoutManagerBase { public: - explicit ImmediateLayoutManager(bool use_preferred_size, - SizeBounds size_bounds = SizeBounds()) - : use_preferred_size_(use_preferred_size), - size_bounds_(std::move(size_bounds)) { - DCHECK(use_preferred_size_ || size_bounds == SizeBounds()); - } + ImmediateLayoutManager() + : ImmediateLayoutManager( + AnimatingLayoutManager::BoundsAnimationMode::kAnimateBothAxes, + LayoutOrientation::kHorizontal) {} + + ImmediateLayoutManager( + AnimatingLayoutManager::BoundsAnimationMode bounds_animation_mode, + LayoutOrientation orientation, + SizeBounds size_bounds = SizeBounds()) + : bounds_animation_mode_(bounds_animation_mode), + orientation_(orientation), + size_bounds_(std::move(size_bounds)) {} // LayoutManager: @@ -2777,11 +2838,23 @@ class ImmediateLayoutManager : public LayoutManagerBase { child_layout.child_view = child; child_layout.visible = child->GetVisible(); child_layout.available_size = size_bounds_; - if (use_preferred_size_) { - child_layout.bounds = gfx::Rect( - ConstrainSizeToBounds(child->GetPreferredSize(), size_bounds_)); - } else { - child_layout.bounds = child->bounds(); + switch (bounds_animation_mode_) { + case AnimatingLayoutManager::BoundsAnimationMode::kAnimateBothAxes: + child_layout.bounds = gfx::Rect( + ConstrainSizeToBounds(child->GetPreferredSize(), size_bounds_)); + break; + case AnimatingLayoutManager::BoundsAnimationMode::kAnimateMainAxis: { + // Start with the preferred size constrained to the bounds, then force + // the cross axis. + gfx::Size size = + ConstrainSizeToBounds(child->GetPreferredSize(), size_bounds_); + SetCrossAxis(&size, orientation_, + GetCrossAxis(orientation_, child->bounds().size())); + child_layout.bounds = gfx::Rect(size); + } break; + case AnimatingLayoutManager::BoundsAnimationMode::kUseHostBounds: + child_layout.bounds = child->bounds(); + break; } layout.host_size.SetToMax(child_layout.bounds.size()); layout.child_layouts.push_back(child_layout); @@ -2796,7 +2869,8 @@ class ImmediateLayoutManager : public LayoutManagerBase { } private: - const bool use_preferred_size_; + const AnimatingLayoutManager::BoundsAnimationMode bounds_animation_mode_; + const LayoutOrientation orientation_; SizeBounds size_bounds_; }; @@ -2865,7 +2939,7 @@ class AnimatingLayoutManagerAvailableSizeTest void InitRootView() { root_layout_ = root_view()->SetLayoutManager(std::make_unique<ImmediateLayoutManager>( - layout()->should_animate_bounds())); + layout()->bounds_animation_mode(), layout()->orientation())); } ImmediateLayoutManager* root_layout() { return root_layout_; } @@ -2876,7 +2950,8 @@ class AnimatingLayoutManagerAvailableSizeTest TEST_F(AnimatingLayoutManagerAvailableSizeTest, AvailableSize_LimitsExpansion) { constexpr gfx::Insets kChildMargins(5); - layout()->SetShouldAnimateBounds(true); + layout()->SetBoundsAnimationMode( + AnimatingLayoutManager::BoundsAnimationMode::kAnimateBothAxes); auto* const flex_layout = layout()->SetTargetLayoutManager(std::make_unique<FlexLayout>()); flex_layout->SetOrientation(LayoutOrientation::kHorizontal); @@ -2924,7 +2999,8 @@ TEST_F(AnimatingLayoutManagerAvailableSizeTest, AvailableSize_LimitsExpansion) { TEST_F(AnimatingLayoutManagerAvailableSizeTest, AvailableSize_RestartsAnimation) { constexpr gfx::Insets kChildMargins(5); - layout()->SetShouldAnimateBounds(true); + layout()->SetBoundsAnimationMode( + AnimatingLayoutManager::BoundsAnimationMode::kAnimateBothAxes); auto* const flex_layout = layout()->SetTargetLayoutManager(std::make_unique<FlexLayout>()); flex_layout->SetOrientation(LayoutOrientation::kHorizontal); @@ -2966,7 +3042,8 @@ TEST_F(AnimatingLayoutManagerAvailableSizeTest, TEST_F(AnimatingLayoutManagerAvailableSizeTest, AvailableSize_RestartsAnimation_Vertical) { constexpr gfx::Insets kChildMargins(5); - layout()->SetShouldAnimateBounds(true); + layout()->SetBoundsAnimationMode( + AnimatingLayoutManager::BoundsAnimationMode::kAnimateBothAxes); auto* const flex_layout = layout()->SetTargetLayoutManager(std::make_unique<FlexLayout>()); flex_layout->SetOrientation(LayoutOrientation::kVertical); @@ -3019,7 +3096,8 @@ TEST_F(AnimatingLayoutManagerAvailableSizeTest, TEST_F(AnimatingLayoutManagerAvailableSizeTest, AvailableSize_RedirectsAnimation) { constexpr gfx::Insets kChildMargins(5); - layout()->SetShouldAnimateBounds(true); + layout()->SetBoundsAnimationMode( + AnimatingLayoutManager::BoundsAnimationMode::kAnimateBothAxes); auto* const flex_layout = layout()->SetTargetLayoutManager(std::make_unique<FlexLayout>()); flex_layout->SetOrientation(LayoutOrientation::kHorizontal); @@ -3064,7 +3142,8 @@ TEST_F(AnimatingLayoutManagerAvailableSizeTest, TEST_F(AnimatingLayoutManagerAvailableSizeTest, AvailableSize_StopsAnimation) { constexpr gfx::Insets kChildMargins(5); - layout()->SetShouldAnimateBounds(true); + layout()->SetBoundsAnimationMode( + AnimatingLayoutManager::BoundsAnimationMode::kAnimateBothAxes); auto* const flex_layout = layout()->SetTargetLayoutManager(std::make_unique<FlexLayout>()); flex_layout->SetOrientation(LayoutOrientation::kHorizontal); @@ -3103,7 +3182,8 @@ TEST_F(AnimatingLayoutManagerAvailableSizeTest, AvailableSize_StopsAnimation) { TEST_F(AnimatingLayoutManagerAvailableSizeTest, AvailableSize_ImmediateResize) { constexpr gfx::Insets kChildMargins(5); - layout()->SetShouldAnimateBounds(true); + layout()->SetBoundsAnimationMode( + AnimatingLayoutManager::BoundsAnimationMode::kAnimateBothAxes); auto* const flex_layout = layout()->SetTargetLayoutManager(std::make_unique<FlexLayout>()); flex_layout->SetOrientation(LayoutOrientation::kHorizontal); @@ -3131,7 +3211,8 @@ TEST_F(AnimatingLayoutManagerAvailableSizeTest, AvailableSize_ImmediateResize) { TEST_F(AnimatingLayoutManagerAvailableSizeTest, AvailableSize_StepDownStepUp) { constexpr gfx::Insets kChildMargins(5); - layout()->SetShouldAnimateBounds(true); + layout()->SetBoundsAnimationMode( + AnimatingLayoutManager::BoundsAnimationMode::kAnimateBothAxes); auto* const flex_layout = layout()->SetTargetLayoutManager(std::make_unique<FlexLayout>()); flex_layout->SetOrientation(LayoutOrientation::kHorizontal); @@ -3173,7 +3254,8 @@ TEST_F(AnimatingLayoutManagerAvailableSizeTest, AvailableSize_StepDownStepUp) { TEST_F(AnimatingLayoutManagerAvailableSizeTest, AvailableSize_ConstraintRemovedStartsAnimation) { constexpr gfx::Insets kChildMargins(5); - layout()->SetShouldAnimateBounds(true); + layout()->SetBoundsAnimationMode( + AnimatingLayoutManager::BoundsAnimationMode::kAnimateBothAxes); auto* const flex_layout = layout()->SetTargetLayoutManager(std::make_unique<FlexLayout>()); flex_layout->SetOrientation(LayoutOrientation::kHorizontal); @@ -3213,7 +3295,8 @@ TEST_F(AnimatingLayoutManagerAvailableSizeTest, TEST_F(AnimatingLayoutManagerAvailableSizeTest, AvailableSize_LimitsExpansion_WithFlex) { constexpr gfx::Insets kChildMargins(5); - layout()->SetShouldAnimateBounds(true); + layout()->SetBoundsAnimationMode( + AnimatingLayoutManager::BoundsAnimationMode::kAnimateBothAxes); auto* const flex_layout = layout()->SetTargetLayoutManager(std::make_unique<FlexLayout>()); flex_layout->SetOrientation(LayoutOrientation::kHorizontal); @@ -3263,7 +3346,8 @@ TEST_F(AnimatingLayoutManagerAvailableSizeTest, TEST_F(AnimatingLayoutManagerAvailableSizeTest, AvailableSize_RestartsAnimation_WithFlex) { constexpr gfx::Insets kChildMargins(5); - layout()->SetShouldAnimateBounds(true); + layout()->SetBoundsAnimationMode( + AnimatingLayoutManager::BoundsAnimationMode::kAnimateBothAxes); auto* const flex_layout = layout()->SetTargetLayoutManager(std::make_unique<FlexLayout>()); flex_layout->SetOrientation(LayoutOrientation::kHorizontal); @@ -3306,7 +3390,8 @@ TEST_F(AnimatingLayoutManagerAvailableSizeTest, TEST_F(AnimatingLayoutManagerAvailableSizeTest, AvailableSize_RedirectsAnimation_WithFlex) { constexpr gfx::Insets kChildMargins(5); - layout()->SetShouldAnimateBounds(true); + layout()->SetBoundsAnimationMode( + AnimatingLayoutManager::BoundsAnimationMode::kAnimateBothAxes); auto* const flex_layout = layout()->SetTargetLayoutManager(std::make_unique<FlexLayout>()); flex_layout->SetOrientation(LayoutOrientation::kHorizontal); @@ -3354,7 +3439,8 @@ TEST_F(AnimatingLayoutManagerAvailableSizeTest, TEST_F(AnimatingLayoutManagerAvailableSizeTest, AvailableSize_StopsAnimation_WithFlex) { constexpr gfx::Insets kChildMargins(5); - layout()->SetShouldAnimateBounds(true); + layout()->SetBoundsAnimationMode( + AnimatingLayoutManager::BoundsAnimationMode::kAnimateBothAxes); auto* const flex_layout = layout()->SetTargetLayoutManager(std::make_unique<FlexLayout>()); flex_layout->SetOrientation(LayoutOrientation::kHorizontal); @@ -3395,7 +3481,8 @@ TEST_F(AnimatingLayoutManagerAvailableSizeTest, TEST_F(AnimatingLayoutManagerAvailableSizeTest, AvailableSize_ImmediateResize_WithFlex) { constexpr gfx::Insets kChildMargins(5); - layout()->SetShouldAnimateBounds(true); + layout()->SetBoundsAnimationMode( + AnimatingLayoutManager::BoundsAnimationMode::kAnimateBothAxes); auto* const flex_layout = layout()->SetTargetLayoutManager(std::make_unique<FlexLayout>()); flex_layout->SetOrientation(LayoutOrientation::kHorizontal); @@ -3425,7 +3512,8 @@ TEST_F(AnimatingLayoutManagerAvailableSizeTest, TEST_F(AnimatingLayoutManagerAvailableSizeTest, AvailableSize_StepDownStepUp_WithFlex) { constexpr gfx::Insets kChildMargins(5); - layout()->SetShouldAnimateBounds(true); + layout()->SetBoundsAnimationMode( + AnimatingLayoutManager::BoundsAnimationMode::kAnimateBothAxes); auto* const flex_layout = layout()->SetTargetLayoutManager(std::make_unique<FlexLayout>()); flex_layout->SetOrientation(LayoutOrientation::kHorizontal); @@ -3467,7 +3555,8 @@ TEST_F(AnimatingLayoutManagerAvailableSizeTest, TEST_F(AnimatingLayoutManagerAvailableSizeTest, AvailableSize_ConstraintRemovedStartsAnimation_WithFlex) { constexpr gfx::Insets kChildMargins(5); - layout()->SetShouldAnimateBounds(true); + layout()->SetBoundsAnimationMode( + AnimatingLayoutManager::BoundsAnimationMode::kAnimateBothAxes); auto* const flex_layout = layout()->SetTargetLayoutManager(std::make_unique<FlexLayout>()); flex_layout->SetOrientation(LayoutOrientation::kHorizontal); @@ -3506,6 +3595,330 @@ TEST_F(AnimatingLayoutManagerAvailableSizeTest, EXPECT_EQ(expected_events, logger.events()); } +TEST_F(AnimatingLayoutManagerAvailableSizeTest, + AnimateMainAxis_Horizontal_MainAxisAnimates) { + layout()->SetBoundsAnimationMode( + AnimatingLayoutManager::BoundsAnimationMode::kAnimateMainAxis); + auto* const flex_layout = + layout()->SetTargetLayoutManager(std::make_unique<FlexLayout>()); + flex_layout->SetOrientation(LayoutOrientation::kHorizontal) + .SetCrossAxisAlignment(LayoutAlignment::kStart) + .SetDefault(kFlexBehaviorKey, + FlexSpecification(LayoutOrientation::kHorizontal, + MinimumFlexSizeRule::kPreferred, + MaximumFlexSizeRule::kPreferred, false, + MinimumFlexSizeRule::kScaleToZero)); + view()->SetBoundsRect(gfx::Rect(0, 0, 5, 5)); + InitRootView(); + layout()->ResetLayout(); + root_view()->Layout(); + + EXPECT_FALSE(layout()->is_animating()); + EXPECT_EQ(gfx::Size(30, 5), view()->size()); + EXPECT_TRUE(child(0)->GetVisible()); + EXPECT_EQ(gfx::Rect(0, 0, 10, 5), child(0)->bounds()); + EXPECT_TRUE(child(1)->GetVisible()); + EXPECT_EQ(gfx::Rect(10, 0, 10, 5), child(1)->bounds()); + EXPECT_TRUE(child(2)->GetVisible()); + EXPECT_EQ(gfx::Rect(20, 0, 10, 5), child(2)->bounds()); + + child(1)->SetVisible(false); + EXPECT_TRUE(layout()->is_animating()); + + // Advance the animation halfway. + animation_api()->IncrementTime(base::TimeDelta::FromMilliseconds(500)); + EXPECT_TRUE(layout()->is_animating()); + EXPECT_EQ(gfx::Size(25, 5), view()->size()); + + // Finish the animation. + animation_api()->IncrementTime(base::TimeDelta::FromMilliseconds(500)); + EXPECT_FALSE(layout()->is_animating()); + EXPECT_EQ(gfx::Size(20, 5), view()->size()); + EXPECT_TRUE(child(0)->GetVisible()); + EXPECT_EQ(gfx::Rect(0, 0, 10, 5), child(0)->bounds()); + EXPECT_FALSE(child(1)->GetVisible()); + EXPECT_TRUE(child(2)->GetVisible()); + EXPECT_EQ(gfx::Rect(10, 0, 10, 5), child(2)->bounds()); +} + +TEST_F(AnimatingLayoutManagerAvailableSizeTest, + AnimateMainAxis_Vertical_MainAxisAnimates) { + layout() + ->SetBoundsAnimationMode( + AnimatingLayoutManager::BoundsAnimationMode::kAnimateMainAxis) + .SetOrientation(LayoutOrientation::kVertical); + auto* const flex_layout = + layout()->SetTargetLayoutManager(std::make_unique<FlexLayout>()); + flex_layout->SetOrientation(LayoutOrientation::kVertical) + .SetCrossAxisAlignment(LayoutAlignment::kStart) + .SetDefault(kFlexBehaviorKey, + FlexSpecification(LayoutOrientation::kVertical, + MinimumFlexSizeRule::kPreferred, + MaximumFlexSizeRule::kPreferred, false, + MinimumFlexSizeRule::kScaleToZero)); + view()->SetBoundsRect(gfx::Rect(0, 0, 5, 5)); + InitRootView(); + layout()->ResetLayout(); + root_view()->Layout(); + + EXPECT_FALSE(layout()->is_animating()); + EXPECT_EQ(gfx::Size(5, 30), view()->size()); + EXPECT_TRUE(child(0)->GetVisible()); + EXPECT_EQ(gfx::Rect(0, 0, 5, 10), child(0)->bounds()); + EXPECT_TRUE(child(1)->GetVisible()); + EXPECT_EQ(gfx::Rect(0, 10, 5, 10), child(1)->bounds()); + EXPECT_TRUE(child(2)->GetVisible()); + EXPECT_EQ(gfx::Rect(0, 20, 5, 10), child(2)->bounds()); + + child(1)->SetVisible(false); + EXPECT_TRUE(layout()->is_animating()); + + // Advance the animation halfway. + animation_api()->IncrementTime(base::TimeDelta::FromMilliseconds(500)); + EXPECT_TRUE(layout()->is_animating()); + EXPECT_EQ(gfx::Size(5, 25), view()->size()); + + // Finish the animation. + animation_api()->IncrementTime(base::TimeDelta::FromMilliseconds(500)); + EXPECT_FALSE(layout()->is_animating()); + EXPECT_EQ(gfx::Size(5, 20), view()->size()); + EXPECT_TRUE(child(0)->GetVisible()); + EXPECT_EQ(gfx::Rect(0, 0, 5, 10), child(0)->bounds()); + EXPECT_FALSE(child(1)->GetVisible()); + EXPECT_TRUE(child(2)->GetVisible()); + EXPECT_EQ(gfx::Rect(0, 10, 5, 10), child(2)->bounds()); +} + +TEST_F(AnimatingLayoutManagerAvailableSizeTest, + AnimateMainAxis_Horizontal_CrossAxisSizeChangeResetsLayout) { + layout()->SetBoundsAnimationMode( + AnimatingLayoutManager::BoundsAnimationMode::kAnimateMainAxis); + auto* const flex_layout = + layout()->SetTargetLayoutManager(std::make_unique<FlexLayout>()); + flex_layout->SetOrientation(LayoutOrientation::kHorizontal) + .SetCrossAxisAlignment(LayoutAlignment::kStart) + .SetDefault(kFlexBehaviorKey, + FlexSpecification(LayoutOrientation::kHorizontal, + MinimumFlexSizeRule::kPreferred, + MaximumFlexSizeRule::kPreferred, false, + MinimumFlexSizeRule::kScaleToZero)); + view()->SetBoundsRect(gfx::Rect(0, 0, 5, 5)); + InitRootView(); + layout()->ResetLayout(); + root_view()->Layout(); + + EXPECT_FALSE(layout()->is_animating()); + EXPECT_EQ(gfx::Size(30, 5), view()->size()); + EXPECT_TRUE(child(0)->GetVisible()); + EXPECT_EQ(gfx::Rect(0, 0, 10, 5), child(0)->bounds()); + EXPECT_TRUE(child(1)->GetVisible()); + EXPECT_EQ(gfx::Rect(10, 0, 10, 5), child(1)->bounds()); + EXPECT_TRUE(child(2)->GetVisible()); + EXPECT_EQ(gfx::Rect(20, 0, 10, 5), child(2)->bounds()); + + child(1)->SetVisible(false); + EXPECT_TRUE(layout()->is_animating()); + + // Advance the animation halfway. + animation_api()->IncrementTime(base::TimeDelta::FromMilliseconds(500)); + EXPECT_TRUE(layout()->is_animating()); + EXPECT_EQ(gfx::Size(25, 5), view()->size()); + + // Change the cross-axis size. + view()->SetSize(gfx::Size(25, 7)); + view()->InvalidateLayout(); + EXPECT_FALSE(layout()->is_animating()); + EXPECT_EQ(gfx::Size(20, 7), view()->size()); + EXPECT_TRUE(child(0)->GetVisible()); + EXPECT_EQ(gfx::Rect(0, 0, 10, 7), child(0)->bounds()); + EXPECT_FALSE(child(1)->GetVisible()); + EXPECT_TRUE(child(2)->GetVisible()); + EXPECT_EQ(gfx::Rect(10, 0, 10, 7), child(2)->bounds()); +} + +TEST_F(AnimatingLayoutManagerAvailableSizeTest, + AnimateMainAxis_Vertical_CrossAxisSizeChangeResetsLayout) { + layout() + ->SetBoundsAnimationMode( + AnimatingLayoutManager::BoundsAnimationMode::kAnimateMainAxis) + .SetOrientation(LayoutOrientation::kVertical); + auto* const flex_layout = + layout()->SetTargetLayoutManager(std::make_unique<FlexLayout>()); + flex_layout->SetOrientation(LayoutOrientation::kVertical) + .SetCrossAxisAlignment(LayoutAlignment::kStart) + .SetDefault(kFlexBehaviorKey, + FlexSpecification(LayoutOrientation::kVertical, + MinimumFlexSizeRule::kPreferred, + MaximumFlexSizeRule::kPreferred, false, + MinimumFlexSizeRule::kScaleToZero)); + view()->SetBoundsRect(gfx::Rect(0, 0, 5, 5)); + InitRootView(); + layout()->ResetLayout(); + root_view()->Layout(); + + EXPECT_FALSE(layout()->is_animating()); + EXPECT_EQ(gfx::Size(5, 30), view()->size()); + EXPECT_TRUE(child(0)->GetVisible()); + EXPECT_EQ(gfx::Rect(0, 0, 5, 10), child(0)->bounds()); + EXPECT_TRUE(child(1)->GetVisible()); + EXPECT_EQ(gfx::Rect(0, 10, 5, 10), child(1)->bounds()); + EXPECT_TRUE(child(2)->GetVisible()); + EXPECT_EQ(gfx::Rect(0, 20, 5, 10), child(2)->bounds()); + + child(1)->SetVisible(false); + EXPECT_TRUE(layout()->is_animating()); + + // Advance the animation halfway. + animation_api()->IncrementTime(base::TimeDelta::FromMilliseconds(500)); + EXPECT_TRUE(layout()->is_animating()); + EXPECT_EQ(gfx::Size(5, 25), view()->size()); + + // Change the cross-axis size. + view()->SetSize(gfx::Size(7, 25)); + view()->InvalidateLayout(); + EXPECT_FALSE(layout()->is_animating()); + EXPECT_EQ(gfx::Size(7, 20), view()->size()); + EXPECT_TRUE(child(0)->GetVisible()); + EXPECT_EQ(gfx::Rect(0, 0, 7, 10), child(0)->bounds()); + EXPECT_FALSE(child(1)->GetVisible()); + EXPECT_TRUE(child(2)->GetVisible()); + EXPECT_EQ(gfx::Rect(0, 10, 7, 10), child(2)->bounds()); +} + +TEST_F(AnimatingLayoutManagerAvailableSizeTest, + AnimateMainAxis_Horizontal_CrossAxisAlignmentWorks) { + layout()->SetBoundsAnimationMode( + AnimatingLayoutManager::BoundsAnimationMode::kAnimateMainAxis); + auto* const flex_layout = + layout()->SetTargetLayoutManager(std::make_unique<FlexLayout>()); + flex_layout->SetOrientation(LayoutOrientation::kHorizontal) + .SetCrossAxisAlignment(LayoutAlignment::kStart); + // Pick an arbitrary (wrong) main-axis size. + view()->SetBoundsRect(gfx::Rect(0, 0, 20, 20)); + InitRootView(); + layout()->ResetLayout(); + root_view()->Layout(); + + EXPECT_FALSE(layout()->is_animating()); + EXPECT_EQ(gfx::Size(30, 20), view()->size()); + EXPECT_TRUE(child(0)->GetVisible()); + EXPECT_EQ(gfx::Rect(0, 0, 10, 10), child(0)->bounds()); + EXPECT_TRUE(child(1)->GetVisible()); + EXPECT_EQ(gfx::Rect(10, 0, 10, 10), child(1)->bounds()); + EXPECT_TRUE(child(2)->GetVisible()); + EXPECT_EQ(gfx::Rect(20, 0, 10, 10), child(2)->bounds()); + + flex_layout->SetCrossAxisAlignment(LayoutAlignment::kCenter); + EXPECT_TRUE(layout()->is_animating()); + + // Finish the animation. + animation_api()->IncrementTime(base::TimeDelta::FromMilliseconds(1000)); + EXPECT_FALSE(layout()->is_animating()); + EXPECT_EQ(gfx::Size(30, 20), view()->size()); + EXPECT_TRUE(child(0)->GetVisible()); + EXPECT_EQ(gfx::Rect(0, 5, 10, 10), child(0)->bounds()); + EXPECT_TRUE(child(1)->GetVisible()); + EXPECT_EQ(gfx::Rect(10, 5, 10, 10), child(1)->bounds()); + EXPECT_TRUE(child(2)->GetVisible()); + EXPECT_EQ(gfx::Rect(20, 5, 10, 10), child(2)->bounds()); + + flex_layout->SetCrossAxisAlignment(LayoutAlignment::kEnd); + EXPECT_TRUE(layout()->is_animating()); + + // Finish the animation. + animation_api()->IncrementTime(base::TimeDelta::FromMilliseconds(1000)); + EXPECT_FALSE(layout()->is_animating()); + EXPECT_EQ(gfx::Size(30, 20), view()->size()); + EXPECT_TRUE(child(0)->GetVisible()); + EXPECT_EQ(gfx::Rect(0, 10, 10, 10), child(0)->bounds()); + EXPECT_TRUE(child(1)->GetVisible()); + EXPECT_EQ(gfx::Rect(10, 10, 10, 10), child(1)->bounds()); + EXPECT_TRUE(child(2)->GetVisible()); + EXPECT_EQ(gfx::Rect(20, 10, 10, 10), child(2)->bounds()); + + flex_layout->SetCrossAxisAlignment(LayoutAlignment::kStretch); + EXPECT_TRUE(layout()->is_animating()); + + // Finish the animation. + animation_api()->IncrementTime(base::TimeDelta::FromMilliseconds(1000)); + EXPECT_FALSE(layout()->is_animating()); + EXPECT_EQ(gfx::Size(30, 20), view()->size()); + EXPECT_TRUE(child(0)->GetVisible()); + EXPECT_EQ(gfx::Rect(0, 0, 10, 20), child(0)->bounds()); + EXPECT_TRUE(child(1)->GetVisible()); + EXPECT_EQ(gfx::Rect(10, 0, 10, 20), child(1)->bounds()); + EXPECT_TRUE(child(2)->GetVisible()); + EXPECT_EQ(gfx::Rect(20, 0, 10, 20), child(2)->bounds()); +} + +TEST_F(AnimatingLayoutManagerAvailableSizeTest, + AnimateMainAxis_Vertical_CrossAxisAlignmentWorks) { + layout() + ->SetBoundsAnimationMode( + AnimatingLayoutManager::BoundsAnimationMode::kAnimateMainAxis) + .SetOrientation(LayoutOrientation::kVertical); + auto* const flex_layout = + layout()->SetTargetLayoutManager(std::make_unique<FlexLayout>()); + flex_layout->SetOrientation(LayoutOrientation::kVertical) + .SetCrossAxisAlignment(LayoutAlignment::kStart); + // Pick an arbitrary (wrong) main-axis size. + view()->SetBoundsRect(gfx::Rect(0, 0, 20, 20)); + InitRootView(); + layout()->ResetLayout(); + root_view()->Layout(); + + EXPECT_FALSE(layout()->is_animating()); + EXPECT_EQ(gfx::Size(20, 30), view()->size()); + EXPECT_TRUE(child(0)->GetVisible()); + EXPECT_EQ(gfx::Rect(0, 0, 10, 10), child(0)->bounds()); + EXPECT_TRUE(child(1)->GetVisible()); + EXPECT_EQ(gfx::Rect(0, 10, 10, 10), child(1)->bounds()); + EXPECT_TRUE(child(2)->GetVisible()); + EXPECT_EQ(gfx::Rect(0, 20, 10, 10), child(2)->bounds()); + + flex_layout->SetCrossAxisAlignment(LayoutAlignment::kCenter); + EXPECT_TRUE(layout()->is_animating()); + + // Finish the animation. + animation_api()->IncrementTime(base::TimeDelta::FromMilliseconds(1000)); + EXPECT_FALSE(layout()->is_animating()); + EXPECT_EQ(gfx::Size(20, 30), view()->size()); + EXPECT_TRUE(child(0)->GetVisible()); + EXPECT_EQ(gfx::Rect(5, 0, 10, 10), child(0)->bounds()); + EXPECT_TRUE(child(1)->GetVisible()); + EXPECT_EQ(gfx::Rect(5, 10, 10, 10), child(1)->bounds()); + EXPECT_TRUE(child(2)->GetVisible()); + EXPECT_EQ(gfx::Rect(5, 20, 10, 10), child(2)->bounds()); + + flex_layout->SetCrossAxisAlignment(LayoutAlignment::kEnd); + EXPECT_TRUE(layout()->is_animating()); + + // Finish the animation. + animation_api()->IncrementTime(base::TimeDelta::FromMilliseconds(1000)); + EXPECT_FALSE(layout()->is_animating()); + EXPECT_EQ(gfx::Size(20, 30), view()->size()); + EXPECT_TRUE(child(0)->GetVisible()); + EXPECT_EQ(gfx::Rect(10, 0, 10, 10), child(0)->bounds()); + EXPECT_TRUE(child(1)->GetVisible()); + EXPECT_EQ(gfx::Rect(10, 10, 10, 10), child(1)->bounds()); + EXPECT_TRUE(child(2)->GetVisible()); + EXPECT_EQ(gfx::Rect(10, 20, 10, 10), child(2)->bounds()); + + flex_layout->SetCrossAxisAlignment(LayoutAlignment::kStretch); + EXPECT_TRUE(layout()->is_animating()); + + // Finish the animation. + animation_api()->IncrementTime(base::TimeDelta::FromMilliseconds(1000)); + EXPECT_FALSE(layout()->is_animating()); + EXPECT_EQ(gfx::Size(20, 30), view()->size()); + EXPECT_TRUE(child(0)->GetVisible()); + EXPECT_EQ(gfx::Rect(0, 0, 20, 10), child(0)->bounds()); + EXPECT_TRUE(child(1)->GetVisible()); + EXPECT_EQ(gfx::Rect(0, 10, 20, 10), child(1)->bounds()); + EXPECT_TRUE(child(2)->GetVisible()); + EXPECT_EQ(gfx::Rect(0, 20, 20, 10), child(2)->bounds()); +} + // Flex Rule Tests ------------------------------------------------------------- class AnimatingLayoutManagerFlexRuleTest : public AnimatingLayoutManagerTest { @@ -3699,7 +4112,8 @@ class AnimatingLayoutManagerInFlexLayoutTest protected: void SetUp() override { AnimatingLayoutManagerRootViewTest::SetUp(); - layout()->SetShouldAnimateBounds(true); + layout()->SetBoundsAnimationMode( + AnimatingLayoutManager::BoundsAnimationMode::kAnimateBothAxes); root_layout_ = root_view()->SetLayoutManager(std::make_unique<FlexLayout>()); root_layout_->SetOrientation(LayoutOrientation::kHorizontal) @@ -4137,7 +4551,8 @@ class AnimatingLayoutManagerRealtimeTest protected: void InitRootView(SizeBounds bounds = SizeBounds()) { root_view()->SetLayoutManager(std::make_unique<ImmediateLayoutManager>( - layout()->should_animate_bounds(), std::move(bounds))); + layout()->bounds_animation_mode(), layout()->orientation(), + std::move(bounds))); layout()->EnableAnimationForTesting(); } @@ -4149,7 +4564,8 @@ class AnimatingLayoutManagerRealtimeTest TEST_F(AnimatingLayoutManagerRealtimeTest, TestAnimateSlide) { constexpr gfx::Insets kChildMargins(5); - layout()->SetShouldAnimateBounds(true); + layout()->SetBoundsAnimationMode( + AnimatingLayoutManager::BoundsAnimationMode::kAnimateBothAxes); layout()->SetAnimationDuration(kMinimumAnimationTime); auto* const flex_layout = layout()->SetTargetLayoutManager(std::make_unique<FlexLayout>()); @@ -4187,7 +4603,8 @@ TEST_F(AnimatingLayoutManagerRealtimeTest, TestAnimateSlide) { TEST_F(AnimatingLayoutManagerRealtimeTest, TestAnimateStretch) { constexpr gfx::Insets kChildMargins(5); - layout()->SetShouldAnimateBounds(false); + layout()->SetBoundsAnimationMode( + AnimatingLayoutManager::BoundsAnimationMode::kUseHostBounds); layout()->SetAnimationDuration(kMinimumAnimationTime); auto* const flex_layout = layout()->SetTargetLayoutManager(std::make_unique<FlexLayout>()); @@ -4227,7 +4644,8 @@ TEST_F(AnimatingLayoutManagerRealtimeTest, TestAnimateStretch) { TEST_F(AnimatingLayoutManagerRealtimeTest, TestConstrainedSpaceStopsAnimation) { constexpr gfx::Insets kChildMargins(5); constexpr SizeBounds kSizeBounds(45, base::nullopt); - layout()->SetShouldAnimateBounds(true); + layout()->SetBoundsAnimationMode( + AnimatingLayoutManager::BoundsAnimationMode::kAnimateBothAxes); layout()->SetAnimationDuration(kMinimumAnimationTime); auto* const flex_layout = layout()->SetTargetLayoutManager(std::make_unique<FlexLayout>()); @@ -4271,7 +4689,8 @@ TEST_F(AnimatingLayoutManagerRealtimeTest, TestConstrainedSpaceStopsAnimation) { TEST_F(AnimatingLayoutManagerRealtimeTest, TestConstrainedSpaceDoesNotRestart) { constexpr gfx::Insets kChildMargins(5); constexpr SizeBounds kSizeBounds(45, base::nullopt); - layout()->SetShouldAnimateBounds(true); + layout()->SetBoundsAnimationMode( + AnimatingLayoutManager::BoundsAnimationMode::kAnimateBothAxes); layout()->SetAnimationDuration(kMinimumAnimationTime); auto* const flex_layout = layout()->SetTargetLayoutManager(std::make_unique<FlexLayout>()); @@ -4319,7 +4738,8 @@ TEST_F(AnimatingLayoutManagerRealtimeTest, TestConstrainedSpaceRestartedAnimationSucceeds) { constexpr gfx::Insets kChildMargins(5); constexpr SizeBounds kSizeBounds(45, base::nullopt); - layout()->SetShouldAnimateBounds(true); + layout()->SetBoundsAnimationMode( + AnimatingLayoutManager::BoundsAnimationMode::kAnimateBothAxes); layout()->SetAnimationDuration(kMinimumAnimationTime); auto* const flex_layout = layout()->SetTargetLayoutManager(std::make_unique<FlexLayout>()); @@ -4392,7 +4812,7 @@ class AnimatingLayoutManagerSequenceTest : public ViewsTestBase { parent_view_ptr_ = std::make_unique<View>(); parent_view_ptr_->SetLayoutManager( - std::make_unique<ImmediateLayoutManager>(true)); + std::make_unique<ImmediateLayoutManager>()); parent_view_ = parent_view_ptr_.get(); layout_view_ptr_ = std::make_unique<View>(); @@ -4416,7 +4836,8 @@ class AnimatingLayoutManagerSequenceTest : public ViewsTestBase { flex_layout->SetCollapseMargins(true); flex_layout->SetCrossAxisAlignment(LayoutAlignment::kStart); flex_layout->SetDefault(kMarginsKey, gfx::Insets(5)); - layout_manager_->SetShouldAnimateBounds(true); + layout_manager_->SetBoundsAnimationMode( + AnimatingLayoutManager::BoundsAnimationMode::kAnimateBothAxes); } void AddViewToParent() { diff --git a/chromium/ui/views/layout/box_layout.cc b/chromium/ui/views/layout/box_layout.cc index 255ca1c2f97..6dec3c98612 100644 --- a/chromium/ui/views/layout/box_layout.cc +++ b/chromium/ui/views/layout/box_layout.cc @@ -16,14 +16,14 @@ namespace { // Returns the maximum of the given insets along the given |axis|. // NOTE: |axis| is different from |orientation_|; it specifies the actual // desired axis. -enum Axis { HORIZONTAL_AXIS, VERTICAL_AXIS }; +enum class Axis { kHorizontal, kVertical }; gfx::Insets MaxAxisInsets(Axis axis, const gfx::Insets& leading1, const gfx::Insets& leading2, const gfx::Insets& trailing1, const gfx::Insets& trailing2) { - if (axis == HORIZONTAL_AXIS) { + if (axis == Axis::kHorizontal) { return gfx::Insets(0, std::max(leading1.left(), leading2.left()), 0, std::max(trailing1.right(), trailing2.right())); } @@ -201,11 +201,11 @@ void BoxLayout::Layout(View* host) { gfx::Rect min_child_area(child_area); gfx::Insets child_margins; if (collapse_margins_spacing_) { - child_margins = - MaxAxisInsets(orientation_ == Orientation::kVertical ? HORIZONTAL_AXIS - : VERTICAL_AXIS, - child.margins(), inside_border_insets_, child.margins(), - inside_border_insets_); + child_margins = MaxAxisInsets(orientation_ == Orientation::kVertical + ? Axis::kHorizontal + : Axis::kVertical, + child.margins(), inside_border_insets_, + child.margins(), inside_border_insets_); } else { child_margins = child.margins(); } @@ -307,7 +307,7 @@ gfx::Size BoxLayout::GetPreferredSize(const View* host) const { gfx::Size child_size = child.view()->GetPreferredSize(); gfx::Insets child_margins; if (collapse_margins_spacing_) { - child_margins = MaxAxisInsets(HORIZONTAL_AXIS, child.margins(), + child_margins = MaxAxisInsets(Axis::kHorizontal, child.margins(), inside_border_insets_, child.margins(), inside_border_insets_); } else { @@ -474,14 +474,14 @@ gfx::Insets BoxLayout::MainAxisOuterMargin() const { const ViewWrapper first(this, FirstVisibleView()); const ViewWrapper last(this, LastVisibleView()); return MaxAxisInsets(orientation_ == Orientation::kHorizontal - ? HORIZONTAL_AXIS - : VERTICAL_AXIS, + ? Axis::kHorizontal + : Axis::kVertical, inside_border_insets_, first.margins(), inside_border_insets_, last.margins()); } return MaxAxisInsets(orientation_ == Orientation::kHorizontal - ? HORIZONTAL_AXIS - : VERTICAL_AXIS, + ? Axis::kHorizontal + : Axis::kVertical, inside_border_insets_, gfx::Insets(), inside_border_insets_, gfx::Insets()); } @@ -569,9 +569,9 @@ gfx::Size BoxLayout::GetPreferredSizeForChildWidth(const View* host, size.height()); gfx::Insets child_margins; if (collapse_margins_spacing_) - child_margins = - MaxAxisInsets(VERTICAL_AXIS, child.margins(), inside_border_insets_, - child.margins(), inside_border_insets_); + child_margins = MaxAxisInsets(Axis::kVertical, child.margins(), + inside_border_insets_, child.margins(), + inside_border_insets_); else child_margins = child.margins(); diff --git a/chromium/ui/views/layout/flex_layout.cc b/chromium/ui/views/layout/flex_layout.cc index dee1daf861f..18131f8c984 100644 --- a/chromium/ui/views/layout/flex_layout.cc +++ b/chromium/ui/views/layout/flex_layout.cc @@ -13,7 +13,8 @@ #include "base/bind.h" #include "base/callback.h" -#include "base/logging.h" +#include "base/check_op.h" +#include "base/notreached.h" #include "base/numerics/safe_conversions.h" #include "base/strings/stringprintf.h" #include "ui/events/event_target.h" diff --git a/chromium/ui/views/layout/grid_layout.cc b/chromium/ui/views/layout/grid_layout.cc index aa028983659..58004fb9ee0 100644 --- a/chromium/ui/views/layout/grid_layout.cc +++ b/chromium/ui/views/layout/grid_layout.cc @@ -158,7 +158,7 @@ class Column : public LayoutElement { Column(GridLayout::Alignment h_align, GridLayout::Alignment v_align, float resize_percent, - GridLayout::SizeType size_type, + GridLayout::ColumnSize size_type, int fixed_width, int min_width, bool is_padding) @@ -194,7 +194,7 @@ class Column : public LayoutElement { const GridLayout::Alignment h_align_; const GridLayout::Alignment v_align_; - const GridLayout::SizeType size_type_; + const GridLayout::ColumnSize size_type_; int same_size_column_; const int fixed_width_; const int min_width_; @@ -216,7 +216,7 @@ class Column : public LayoutElement { }; void Column::ResetSize() { - if (size_type_ == GridLayout::FIXED) { + if (size_type_ == GridLayout::ColumnSize::kFixed) { SetSize(fixed_width_); } else { SetSize(min_width_); @@ -249,7 +249,7 @@ void Column::UnifyLinkedColumnSizes(int size_limit) { } void Column::AdjustSize(int size) { - if (size_type_ == GridLayout::USE_PREF) + if (size_type_ == GridLayout::ColumnSize::kUsePreferred) LayoutElement::AdjustSize(size); } @@ -382,13 +382,13 @@ ColumnSet::~ColumnSet() = default; void ColumnSet::AddPaddingColumn(float resize_percent, int width) { AddColumn(GridLayout::FILL, GridLayout::FILL, resize_percent, - GridLayout::FIXED, width, width, true); + GridLayout::ColumnSize::kFixed, width, width, true); } void ColumnSet::AddColumn(GridLayout::Alignment h_align, GridLayout::Alignment v_align, float resize_percent, - GridLayout::SizeType size_type, + GridLayout::ColumnSize size_type, int fixed_width, int min_width) { AddColumn(h_align, v_align, resize_percent, size_type, fixed_width, min_width, @@ -411,7 +411,7 @@ void ColumnSet::LinkColumnSizes(const std::vector<int>& columns) { void ColumnSet::AddColumn(GridLayout::Alignment h_align, GridLayout::Alignment v_align, float resize_percent, - GridLayout::SizeType size_type, + GridLayout::ColumnSize size_type, int fixed_width, int min_width, bool is_padding) { @@ -533,7 +533,8 @@ void ColumnSet::DistributeRemainingWidth(ViewState* view_state) { if (columns_[i]->IsResizable()) { total_resize += columns_[i]->ResizePercent(); resizable_columns++; - } else if (columns_[i]->size_type_ == GridLayout::USE_PREF) { + } else if (columns_[i]->size_type_ == + GridLayout::ColumnSize::kUsePreferred) { pref_size_columns++; } } @@ -559,7 +560,7 @@ void ColumnSet::DistributeRemainingWidth(ViewState* view_state) { // that use the preferred size. int to_distribute = width / pref_size_columns; for (int i = start_col; i < max_col; ++i) { - if (columns_[i]->size_type_ == GridLayout::USE_PREF) { + if (columns_[i]->size_type_ == GridLayout::ColumnSize::kUsePreferred) { width -= to_distribute; if (width < to_distribute) to_distribute += width; @@ -740,7 +741,8 @@ void ColumnSet::ResizeUsingMin(int total_delta) { bool ColumnSet::CanUseMinimum(const ViewState& view_state) const { const auto resizable = [](const auto& col) { - return col->ResizePercent() > 0 && col->size_type_ != GridLayout::FIXED; + return col->ResizePercent() > 0 && + col->size_type_ != GridLayout::ColumnSize::kFixed; }; return std::all_of( columns_.cbegin() + view_state.start_col, diff --git a/chromium/ui/views/layout/grid_layout.h b/chromium/ui/views/layout/grid_layout.h index 118431d13d5..5e64d647a62 100644 --- a/chromium/ui/views/layout/grid_layout.h +++ b/chromium/ui/views/layout/grid_layout.h @@ -26,12 +26,14 @@ // FILL, // Views starting in this column are vertically // // resized. // 1.0, // This column has a resize weight of 1. -// USE_PREF, // Use the preferred size of the view. -// 0, // Ignored for USE_PREF. +// ColumnSize::kUsePreferred, // Use the preferred size of +// the +// // view. +// 0, // Ignored for kUsePref. // 0); // A minimum width of 0. // columns->AddPaddingColumn(kFixedSize, // The padding column is not resizable. // 10); // And has a width of 10 pixels. -// columns->AddColumn(FILL, FILL, kFixedSize, USE_PREF, 0, 0); +// columns->AddColumn(FILL, FILL, kFixedSize, ColumnSize::kUsePreferred, 0, 0); // Now add the views: // // First start a row. // layout->StartRow(kFixedSize, // This row isn't vertically resizable. @@ -104,12 +106,12 @@ class VIEWS_EXPORT GridLayout : public LayoutManager { }; // An enumeration of the possible ways the size of a column may be obtained. - enum SizeType { + enum class ColumnSize { // The column size is fixed. - FIXED, + kFixed, // The preferred size of the view is used to determine the column size. - USE_PREF + kUsePreferred }; GridLayout(); @@ -129,7 +131,7 @@ class VIEWS_EXPORT GridLayout : public LayoutManager { // Returns the column set for the specified id, or NULL if one doesn't exist. ColumnSet* GetColumnSet(int id); - // Adds a padding row. Padding rows typically don't have any views, and + // Adds a padding row. Padding rows typically don't have any views, // but are used to provide vertical white space between views. // Size specifies the height of the row. void AddPaddingRow(float vertical_resize, int size); @@ -330,7 +332,7 @@ class VIEWS_EXPORT ColumnSet { void AddColumn(GridLayout::Alignment h_align, GridLayout::Alignment v_align, float resize_percent, - GridLayout::SizeType size_type, + GridLayout::ColumnSize size_type, int fixed_width, int min_width); @@ -358,7 +360,7 @@ class VIEWS_EXPORT ColumnSet { void AddColumn(GridLayout::Alignment h_align, GridLayout::Alignment v_align, float resize_percent, - GridLayout::SizeType size_type, + GridLayout::ColumnSize size_type, int fixed_width, int min_width, bool is_padding); diff --git a/chromium/ui/views/layout/grid_layout_unittest.cc b/chromium/ui/views/layout/grid_layout_unittest.cc index d93dc7ec67d..76a94e7d169 100644 --- a/chromium/ui/views/layout/grid_layout_unittest.cc +++ b/chromium/ui/views/layout/grid_layout_unittest.cc @@ -126,7 +126,8 @@ class GridLayoutAlignmentTest : public testing::Test { auto v1 = std::make_unique<View>(); v1->SetPreferredSize(gfx::Size(10, 20)); ColumnSet* c1 = layout_->AddColumnSet(0); - c1->AddColumn(alignment, alignment, 1, GridLayout::USE_PREF, 0, 0); + c1->AddColumn(alignment, alignment, 1, + GridLayout::ColumnSize::kUsePreferred, 0, 0); layout_->StartRow(1, 0); auto* v1_ptr = layout_->AddView(std::move(v1)); gfx::Size pref = layout_->GetPreferredSize(host_.get()); @@ -173,9 +174,9 @@ TEST_F(GridLayoutTest, TwoColumns) { auto v2 = CreateSizedView(gfx::Size(20, 20)); ColumnSet* c1 = layout()->AddColumnSet(0); c1->AddColumn(GridLayout::LEADING, GridLayout::LEADING, 0, - GridLayout::USE_PREF, 0, 0); + GridLayout::ColumnSize::kUsePreferred, 0, 0); c1->AddColumn(GridLayout::LEADING, GridLayout::LEADING, 0, - GridLayout::USE_PREF, 0, 0); + GridLayout::ColumnSize::kUsePreferred, 0, 0); layout()->StartRow(0, 0); auto* v1_ptr = layout()->AddView(std::move(v1)); auto* v2_ptr = layout()->AddView(std::move(v2)); @@ -194,12 +195,12 @@ TEST_F(GridLayoutTest, LinkedSizes) { ColumnSet* c1 = layout()->AddColumnSet(0); // Fill widths. - c1->AddColumn(GridLayout::FILL, GridLayout::LEADING, 0, GridLayout::USE_PREF, - 0, 0); - c1->AddColumn(GridLayout::FILL, GridLayout::LEADING, 0, GridLayout::USE_PREF, - 0, 0); - c1->AddColumn(GridLayout::FILL, GridLayout::LEADING, 0, GridLayout::USE_PREF, - 0, 0); + c1->AddColumn(GridLayout::FILL, GridLayout::LEADING, 0, + GridLayout::ColumnSize::kUsePreferred, 0, 0); + c1->AddColumn(GridLayout::FILL, GridLayout::LEADING, 0, + GridLayout::ColumnSize::kUsePreferred, 0, 0); + c1->AddColumn(GridLayout::FILL, GridLayout::LEADING, 0, + GridLayout::ColumnSize::kUsePreferred, 0, 0); layout()->StartRow(0, 0); auto* v1 = layout()->AddView(CreateSizedView(gfx::Size(10, 20))); @@ -257,9 +258,9 @@ TEST_F(GridLayoutTest, LinkedSizes) { TEST_F(GridLayoutTest, ColSpan1) { ColumnSet* c1 = layout()->AddColumnSet(0); c1->AddColumn(GridLayout::LEADING, GridLayout::LEADING, 0, - GridLayout::USE_PREF, 0, 0); + GridLayout::ColumnSize::kUsePreferred, 0, 0); c1->AddColumn(GridLayout::LEADING, GridLayout::LEADING, 1, - GridLayout::USE_PREF, 0, 0); + GridLayout::ColumnSize::kUsePreferred, 0, 0); layout()->StartRow(0, 0); auto* v1 = layout()->AddView(CreateSizedView(gfx::Size(100, 20)), 2, 1); layout()->StartRow(0, 0); @@ -277,9 +278,9 @@ TEST_F(GridLayoutTest, ColSpan1) { TEST_F(GridLayoutTest, ColSpan2) { ColumnSet* c1 = layout()->AddColumnSet(0); c1->AddColumn(GridLayout::LEADING, GridLayout::LEADING, 1, - GridLayout::USE_PREF, 0, 0); + GridLayout::ColumnSize::kUsePreferred, 0, 0); c1->AddColumn(GridLayout::LEADING, GridLayout::LEADING, 0, - GridLayout::USE_PREF, 0, 0); + GridLayout::ColumnSize::kUsePreferred, 0, 0); layout()->StartRow(0, 0); auto* v1 = layout()->AddView(CreateSizedView(gfx::Size(100, 20)), 2, 1); layout()->StartRow(0, 0); @@ -298,9 +299,9 @@ TEST_F(GridLayoutTest, ColSpan2) { TEST_F(GridLayoutTest, ColSpan3) { ColumnSet* c1 = layout()->AddColumnSet(0); c1->AddColumn(GridLayout::LEADING, GridLayout::LEADING, 0, - GridLayout::USE_PREF, 0, 0); + GridLayout::ColumnSize::kUsePreferred, 0, 0); c1->AddColumn(GridLayout::LEADING, GridLayout::LEADING, 0, - GridLayout::USE_PREF, 0, 0); + GridLayout::ColumnSize::kUsePreferred, 0, 0); layout()->StartRow(0, 0); auto* v1 = layout()->AddView(CreateSizedView(gfx::Size(100, 20)), 2, 1); layout()->StartRow(0, 0); @@ -321,9 +322,9 @@ TEST_F(GridLayoutTest, ColSpan4) { ColumnSet* set = layout()->AddColumnSet(0); set->AddColumn(GridLayout::LEADING, GridLayout::LEADING, 0, - GridLayout::USE_PREF, 0, 0); + GridLayout::ColumnSize::kUsePreferred, 0, 0); set->AddColumn(GridLayout::LEADING, GridLayout::LEADING, 0, - GridLayout::USE_PREF, 0, 0); + GridLayout::ColumnSize::kUsePreferred, 0, 0); layout()->StartRow(0, 0); auto* v1 = layout()->AddView(CreateSizedView(gfx::Size(10, 10))); @@ -346,12 +347,12 @@ TEST_F(GridLayoutTest, ColSpan4) { TEST_F(GridLayoutTest, ColSpanStartSecondColumn) { ColumnSet* set = layout()->AddColumnSet(0); - set->AddColumn(GridLayout::FILL, GridLayout::FILL, 0, GridLayout::USE_PREF, 0, - 0); - set->AddColumn(GridLayout::FILL, GridLayout::FILL, 0, GridLayout::USE_PREF, 0, - 0); - set->AddColumn(GridLayout::FILL, GridLayout::FILL, 0, GridLayout::FIXED, 10, - 0); + set->AddColumn(GridLayout::FILL, GridLayout::FILL, 0, + GridLayout::ColumnSize::kUsePreferred, 0, 0); + set->AddColumn(GridLayout::FILL, GridLayout::FILL, 0, + GridLayout::ColumnSize::kUsePreferred, 0, 0); + set->AddColumn(GridLayout::FILL, GridLayout::FILL, 0, + GridLayout::ColumnSize::kFixed, 10, 0); layout()->StartRow(0, 0); auto* v1 = layout()->AddView(CreateSizedView(gfx::Size(10, 10))); @@ -369,9 +370,9 @@ TEST_F(GridLayoutTest, ColSpanStartSecondColumn) { TEST_F(GridLayoutTest, SameSizeColumns) { ColumnSet* c1 = layout()->AddColumnSet(0); c1->AddColumn(GridLayout::LEADING, GridLayout::LEADING, 0, - GridLayout::USE_PREF, 0, 0); + GridLayout::ColumnSize::kUsePreferred, 0, 0); c1->AddColumn(GridLayout::LEADING, GridLayout::LEADING, 0, - GridLayout::USE_PREF, 0, 0); + GridLayout::ColumnSize::kUsePreferred, 0, 0); c1->LinkColumnSizes({0, 1}); layout()->StartRow(0, 0); auto* v1 = layout()->AddView(CreateSizedView(gfx::Size(50, 20))); @@ -388,10 +389,10 @@ TEST_F(GridLayoutTest, SameSizeColumns) { TEST_F(GridLayoutTest, HorizontalResizeTest1) { ColumnSet* c1 = layout()->AddColumnSet(0); - c1->AddColumn(GridLayout::FILL, GridLayout::LEADING, 1, GridLayout::USE_PREF, - 0, 0); + c1->AddColumn(GridLayout::FILL, GridLayout::LEADING, 1, + GridLayout::ColumnSize::kUsePreferred, 0, 0); c1->AddColumn(GridLayout::LEADING, GridLayout::LEADING, 0, - GridLayout::USE_PREF, 0, 0); + GridLayout::ColumnSize::kUsePreferred, 0, 0); layout()->StartRow(0, 0); auto* v1 = layout()->AddView(CreateSizedView(gfx::Size(50, 20))); auto* v2 = layout()->AddView(CreateSizedView(gfx::Size(10, 10))); @@ -404,10 +405,10 @@ TEST_F(GridLayoutTest, HorizontalResizeTest1) { TEST_F(GridLayoutTest, HorizontalResizeTest2) { ColumnSet* c1 = layout()->AddColumnSet(0); - c1->AddColumn(GridLayout::FILL, GridLayout::LEADING, 1, GridLayout::USE_PREF, - 0, 0); + c1->AddColumn(GridLayout::FILL, GridLayout::LEADING, 1, + GridLayout::ColumnSize::kUsePreferred, 0, 0); c1->AddColumn(GridLayout::TRAILING, GridLayout::LEADING, 1, - GridLayout::USE_PREF, 0, 0); + GridLayout::ColumnSize::kUsePreferred, 0, 0); layout()->StartRow(0, 0); auto* v1 = layout()->AddView(CreateSizedView(gfx::Size(50, 20))); auto* v2 = layout()->AddView(CreateSizedView(gfx::Size(10, 10))); @@ -422,12 +423,12 @@ TEST_F(GridLayoutTest, HorizontalResizeTest2) { // resizable column. TEST_F(GridLayoutTest, HorizontalResizeTest3) { ColumnSet* c1 = layout()->AddColumnSet(0); - c1->AddColumn(GridLayout::FILL, GridLayout::LEADING, 1, GridLayout::USE_PREF, - 0, 0); - c1->AddColumn(GridLayout::FILL, GridLayout::LEADING, 1, GridLayout::USE_PREF, - 0, 0); + c1->AddColumn(GridLayout::FILL, GridLayout::LEADING, 1, + GridLayout::ColumnSize::kUsePreferred, 0, 0); + c1->AddColumn(GridLayout::FILL, GridLayout::LEADING, 1, + GridLayout::ColumnSize::kUsePreferred, 0, 0); c1->AddColumn(GridLayout::TRAILING, GridLayout::LEADING, 0, - GridLayout::USE_PREF, 0, 0); + GridLayout::ColumnSize::kUsePreferred, 0, 0); layout()->StartRow(0, 0); auto* v1 = layout()->AddView(CreateSizedView(gfx::Size(10, 10))); auto* v2 = layout()->AddView(CreateSizedView(gfx::Size(10, 10))); @@ -442,8 +443,8 @@ TEST_F(GridLayoutTest, HorizontalResizeTest3) { TEST_F(GridLayoutTest, TestVerticalResize1) { ColumnSet* c1 = layout()->AddColumnSet(0); - c1->AddColumn(GridLayout::FILL, GridLayout::FILL, 1, GridLayout::USE_PREF, 0, - 0); + c1->AddColumn(GridLayout::FILL, GridLayout::FILL, 1, + GridLayout::ColumnSize::kUsePreferred, 0, 0); layout()->StartRow(1, 0); auto* v1 = layout()->AddView(CreateSizedView(gfx::Size(50, 20))); layout()->StartRow(0, 0); @@ -462,7 +463,7 @@ TEST_F(GridLayoutTest, Border) { host()->SetBorder(CreateEmptyBorder(1, 2, 3, 4)); ColumnSet* c1 = layout()->AddColumnSet(0); c1->AddColumn(GridLayout::LEADING, GridLayout::LEADING, 0, - GridLayout::USE_PREF, 0, 0); + GridLayout::ColumnSize::kUsePreferred, 0, 0); layout()->StartRow(0, 0); auto* v1 = layout()->AddView(CreateSizedView(gfx::Size(10, 20))); @@ -486,8 +487,8 @@ TEST_F(GridLayoutTest, FixedSize) { constexpr int kPrefHeight = 20; for (size_t i = 0; i < kColumnCount; ++i) { - set->AddColumn(GridLayout::CENTER, GridLayout::CENTER, 0, GridLayout::FIXED, - kTitleWidth, kTitleWidth); + set->AddColumn(GridLayout::CENTER, GridLayout::CENTER, 0, + GridLayout::ColumnSize::kFixed, kTitleWidth, kTitleWidth); } for (size_t row = 0; row < kRowCount; ++row) { @@ -515,8 +516,8 @@ TEST_F(GridLayoutTest, FixedSize) { TEST_F(GridLayoutTest, RowSpanWithPaddingRow) { ColumnSet* set = layout()->AddColumnSet(0); - set->AddColumn(GridLayout::CENTER, GridLayout::CENTER, 0, GridLayout::FIXED, - 10, 10); + set->AddColumn(GridLayout::CENTER, GridLayout::CENTER, 0, + GridLayout::ColumnSize::kFixed, 10, 10); layout()->StartRow(0, 0); layout()->AddView(CreateSizedView(gfx::Size(10, 10)), 1, 2); @@ -527,9 +528,9 @@ TEST_F(GridLayoutTest, RowSpan) { ColumnSet* set = layout()->AddColumnSet(0); set->AddColumn(GridLayout::LEADING, GridLayout::LEADING, 0, - GridLayout::USE_PREF, 0, 0); + GridLayout::ColumnSize::kUsePreferred, 0, 0); set->AddColumn(GridLayout::LEADING, GridLayout::LEADING, 0, - GridLayout::USE_PREF, 0, 0); + GridLayout::ColumnSize::kUsePreferred, 0, 0); layout()->StartRow(0, 0); layout()->AddView(CreateSizedView(gfx::Size(20, 10))); @@ -549,9 +550,9 @@ TEST_F(GridLayoutTest, RowSpan2) { ColumnSet* set = layout()->AddColumnSet(0); set->AddColumn(GridLayout::LEADING, GridLayout::LEADING, 0, - GridLayout::USE_PREF, 0, 0); + GridLayout::ColumnSize::kUsePreferred, 0, 0); set->AddColumn(GridLayout::LEADING, GridLayout::LEADING, 0, - GridLayout::USE_PREF, 0, 0); + GridLayout::ColumnSize::kUsePreferred, 0, 0); layout()->StartRow(0, 0); layout()->AddView(CreateSizedView(gfx::Size(20, 20))); @@ -574,9 +575,9 @@ TEST_F(GridLayoutTest, FixedViewWidth) { ColumnSet* set = layout()->AddColumnSet(0); set->AddColumn(GridLayout::LEADING, GridLayout::LEADING, 0, - GridLayout::USE_PREF, 0, 0); + GridLayout::ColumnSize::kUsePreferred, 0, 0); set->AddColumn(GridLayout::LEADING, GridLayout::LEADING, 0, - GridLayout::USE_PREF, 0, 0); + GridLayout::ColumnSize::kUsePreferred, 0, 0); layout()->StartRow(0, 0); auto* view = @@ -596,9 +597,9 @@ TEST_F(GridLayoutTest, FixedViewHeight) { ColumnSet* set = layout()->AddColumnSet(0); set->AddColumn(GridLayout::LEADING, GridLayout::LEADING, 0, - GridLayout::USE_PREF, 0, 0); + GridLayout::ColumnSize::kUsePreferred, 0, 0); set->AddColumn(GridLayout::LEADING, GridLayout::LEADING, 0, - GridLayout::USE_PREF, 0, 0); + GridLayout::ColumnSize::kUsePreferred, 0, 0); layout()->StartRow(0, 0); auto* view = @@ -619,10 +620,10 @@ TEST_F(GridLayoutTest, FixedViewHeight) { TEST_F(GridLayoutTest, ColumnSpanResizing) { ColumnSet* set = layout()->AddColumnSet(0); - set->AddColumn(GridLayout::FILL, GridLayout::CENTER, 2, GridLayout::USE_PREF, - 0, 0); - set->AddColumn(GridLayout::FILL, GridLayout::CENTER, 4, GridLayout::USE_PREF, - 0, 0); + set->AddColumn(GridLayout::FILL, GridLayout::CENTER, 2, + GridLayout::ColumnSize::kUsePreferred, 0, 0); + set->AddColumn(GridLayout::FILL, GridLayout::CENTER, 4, + GridLayout::ColumnSize::kUsePreferred, 0, 0); layout()->StartRow(0, 0); // span_view spans two columns and is twice as big the views added below. @@ -653,16 +654,16 @@ TEST_F(GridLayoutTest, ColumnSpanResizing) { // preferred sizes. TEST_F(GridLayoutTest, ColumnResizingOnGetPreferredSize) { ColumnSet* set = layout()->AddColumnSet(0); - set->AddColumn(GridLayout::FILL, GridLayout::CENTER, 1, GridLayout::USE_PREF, - 0, 0); + set->AddColumn(GridLayout::FILL, GridLayout::CENTER, 1, + GridLayout::ColumnSize::kUsePreferred, 0, 0); set = layout()->AddColumnSet(1); - set->AddColumn(GridLayout::FILL, GridLayout::CENTER, 1, GridLayout::USE_PREF, - 0, 0); + set->AddColumn(GridLayout::FILL, GridLayout::CENTER, 1, + GridLayout::ColumnSize::kUsePreferred, 0, 0); set = layout()->AddColumnSet(2); - set->AddColumn(GridLayout::FILL, GridLayout::CENTER, 1, GridLayout::USE_PREF, - 0, 0); + set->AddColumn(GridLayout::FILL, GridLayout::CENTER, 1, + GridLayout::ColumnSize::kUsePreferred, 0, 0); // Make a row containing a flexible view that trades width for height. layout()->StartRow(0, 0); @@ -689,8 +690,8 @@ TEST_F(GridLayoutTest, ColumnResizingOnGetPreferredSize) { TEST_F(GridLayoutTest, MinimumPreferredSize) { ColumnSet* set = layout()->AddColumnSet(0); - set->AddColumn(GridLayout::FILL, GridLayout::FILL, 0, GridLayout::USE_PREF, 0, - 0); + set->AddColumn(GridLayout::FILL, GridLayout::FILL, 0, + GridLayout::ColumnSize::kUsePreferred, 0, 0); layout()->StartRow(0, 0); layout()->AddView(CreateSizedView(gfx::Size(10, 20))); @@ -707,8 +708,8 @@ TEST_F(GridLayoutTest, MinimumPreferredSize) { // structures it uses to calculate Layout, so will give bogus results. TEST_F(GridLayoutTest, LayoutOnAddDeath) { ColumnSet* set = layout()->AddColumnSet(0); - set->AddColumn(GridLayout::FILL, GridLayout::FILL, 0, GridLayout::USE_PREF, 0, - 0); + set->AddColumn(GridLayout::FILL, GridLayout::FILL, 0, + GridLayout::ColumnSize::kUsePreferred, 0, 0); layout()->StartRow(0, 0); auto view = std::make_unique<LayoutOnAddView>(); EXPECT_DCHECK_DEATH(layout()->AddView(std::move(view))); @@ -725,8 +726,8 @@ TEST_F(GridLayoutTest, ColumnMinForcesPreferredWidth) { // Column's min width is greater than views preferred/min width. This should // force the preferred width to the min width of the column. ColumnSet* set = layout()->AddColumnSet(0); - set->AddColumn(GridLayout::FILL, GridLayout::FILL, 5, GridLayout::USE_PREF, 0, - 100); + set->AddColumn(GridLayout::FILL, GridLayout::FILL, 5, + GridLayout::ColumnSize::kUsePreferred, 0, 100); layout()->StartRow(0, 0); layout()->AddView(CreateSizedView(gfx::Size(20, 10))); @@ -739,10 +740,10 @@ TEST_F(GridLayoutTest, HonorsColumnMin) { // Verifies that a column with a min width is never shrunk smaller than the // minw width. ColumnSet* set = layout()->AddColumnSet(0); - set->AddColumn(GridLayout::FILL, GridLayout::FILL, 5, GridLayout::USE_PREF, 0, - 100); - set->AddColumn(GridLayout::FILL, GridLayout::FILL, 5, GridLayout::USE_PREF, 0, - 0); + set->AddColumn(GridLayout::FILL, GridLayout::FILL, 5, + GridLayout::ColumnSize::kUsePreferred, 0, 100); + set->AddColumn(GridLayout::FILL, GridLayout::FILL, 5, + GridLayout::ColumnSize::kUsePreferred, 0, 0); layout()->StartRow(0, 0); View* view1 = layout()->AddView( CreateViewWithMinAndPref(gfx::Size(10, 10), gfx::Size(125, 10))); @@ -772,10 +773,10 @@ TEST_F(GridLayoutTest, TwoViewsOneSizeSmallerThanMinimum) { // Two columns, equally resizable with two views. Only the first view is // resizable. ColumnSet* set = layout()->AddColumnSet(0); - set->AddColumn(GridLayout::FILL, GridLayout::FILL, 5, GridLayout::USE_PREF, 0, - 0); - set->AddColumn(GridLayout::FILL, GridLayout::FILL, 5, GridLayout::USE_PREF, 0, - 0); + set->AddColumn(GridLayout::FILL, GridLayout::FILL, 5, + GridLayout::ColumnSize::kUsePreferred, 0, 0); + set->AddColumn(GridLayout::FILL, GridLayout::FILL, 5, + GridLayout::ColumnSize::kUsePreferred, 0, 0); layout()->StartRow(0, 0); View* view1 = layout()->AddView( CreateViewWithMinAndPref(gfx::Size(20, 10), gfx::Size(100, 10))); @@ -793,10 +794,10 @@ TEST_F(GridLayoutTest, TwoViewsBothSmallerThanMinimumDifferentResizeWeights) { // Two columns, equally resizable with two views. Only the first view is // resizable. ColumnSet* set = layout()->AddColumnSet(0); - set->AddColumn(GridLayout::FILL, GridLayout::FILL, 8, GridLayout::USE_PREF, 0, - 0); - set->AddColumn(GridLayout::FILL, GridLayout::FILL, 2, GridLayout::USE_PREF, 0, - 0); + set->AddColumn(GridLayout::FILL, GridLayout::FILL, 8, + GridLayout::ColumnSize::kUsePreferred, 0, 0); + set->AddColumn(GridLayout::FILL, GridLayout::FILL, 2, + GridLayout::ColumnSize::kUsePreferred, 0, 0); layout()->StartRow(0, 0); View* view1 = layout()->AddView( CreateViewWithMinAndPref(gfx::Size(91, 10), gfx::Size(100, 10))); @@ -842,10 +843,10 @@ TEST_F(GridLayoutTest, TwoViewsBothSmallerThanMinimumDifferentResizeWeights) { TEST_F(GridLayoutTest, TwoViewsOneColumnUsePrefOtherFixed) { layout()->set_honors_min_width(true); ColumnSet* set = layout()->AddColumnSet(0); - set->AddColumn(GridLayout::FILL, GridLayout::FILL, 8, GridLayout::USE_PREF, 0, - 0); - set->AddColumn(GridLayout::FILL, GridLayout::FILL, 2, GridLayout::FIXED, 100, - 0); + set->AddColumn(GridLayout::FILL, GridLayout::FILL, 8, + GridLayout::ColumnSize::kUsePreferred, 0, 0); + set->AddColumn(GridLayout::FILL, GridLayout::FILL, 2, + GridLayout::ColumnSize::kFixed, 100, 0); layout()->StartRow(0, 0); View* view1 = layout()->AddView( CreateViewWithMinAndPref(gfx::Size(10, 10), gfx::Size(100, 10))); @@ -867,10 +868,10 @@ TEST_F(GridLayoutTest, TwoViewsOneColumnUsePrefOtherFixed) { TEST_F(GridLayoutTest, TwoViewsBothColumnsResizableOneViewFixedWidthMin) { layout()->set_honors_min_width(true); ColumnSet* set = layout()->AddColumnSet(0); - set->AddColumn(GridLayout::FILL, GridLayout::FILL, 1, GridLayout::USE_PREF, 0, - 0); - set->AddColumn(GridLayout::FILL, GridLayout::FILL, 1, GridLayout::USE_PREF, 0, - 0); + set->AddColumn(GridLayout::FILL, GridLayout::FILL, 1, + GridLayout::ColumnSize::kUsePreferred, 0, 0); + set->AddColumn(GridLayout::FILL, GridLayout::FILL, 1, + GridLayout::ColumnSize::kUsePreferred, 0, 0); layout()->StartRow(0, 0); View* view1 = layout()->AddView( CreateViewWithMinAndPref(gfx::Size(10, 10), gfx::Size(100, 10))); @@ -910,8 +911,8 @@ class SettablePreferredHeightView : public View { TEST_F(GridLayoutTest, HeightForWidthCalledWhenNotGivenPreferredWidth) { ColumnSet* set = layout()->AddColumnSet(0); - set->AddColumn(GridLayout::LEADING, GridLayout::FILL, 1, GridLayout::USE_PREF, - 0, 0); + set->AddColumn(GridLayout::LEADING, GridLayout::FILL, 1, + GridLayout::ColumnSize::kUsePreferred, 0, 0); layout()->StartRow(0, 0); const int pref_height = 100; auto view = std::make_unique<SettablePreferredHeightView>(pref_height); diff --git a/chromium/ui/views/layout/layout_manager_base.cc b/chromium/ui/views/layout/layout_manager_base.cc index 13622be1d09..5bc27081215 100644 --- a/chromium/ui/views/layout/layout_manager_base.cc +++ b/chromium/ui/views/layout/layout_manager_base.cc @@ -7,7 +7,7 @@ #include <utility> #include "base/auto_reset.h" -#include "base/logging.h" +#include "base/check_op.h" #include "ui/views/view.h" namespace views { diff --git a/chromium/ui/views/layout/layout_manager_base.h b/chromium/ui/views/layout/layout_manager_base.h index cec6a2e6366..8812cb5a17b 100644 --- a/chromium/ui/views/layout/layout_manager_base.h +++ b/chromium/ui/views/layout/layout_manager_base.h @@ -11,6 +11,7 @@ #include <utility> #include <vector> +#include "base/macros.h" #include "base/optional.h" #include "ui/gfx/geometry/insets.h" #include "ui/gfx/geometry/rect.h" diff --git a/chromium/ui/views/layout/proposed_layout.cc b/chromium/ui/views/layout/proposed_layout.cc index 6a2373cfcc5..6f8f502647e 100644 --- a/chromium/ui/views/layout/proposed_layout.cc +++ b/chromium/ui/views/layout/proposed_layout.cc @@ -5,6 +5,7 @@ #include "ui/views/layout/proposed_layout.h" #include <map> +#include <sstream> #include <string> #include "ui/gfx/animation/tween.h" diff --git a/chromium/ui/views/metadata/metadata_cache.cc b/chromium/ui/views/metadata/metadata_cache.cc index 5a2f10ab864..d37abba27de 100644 --- a/chromium/ui/views/metadata/metadata_cache.cc +++ b/chromium/ui/views/metadata/metadata_cache.cc @@ -6,7 +6,7 @@ #include <algorithm> -#include "base/logging.h" +#include "base/check_op.h" #include "base/no_destructor.h" #include "ui/views/metadata/metadata_types.h" diff --git a/chromium/ui/views/metadata/metadata_types.cc b/chromium/ui/views/metadata/metadata_types.cc index 0dc3a0185fe..ef38d3a8ae3 100644 --- a/chromium/ui/views/metadata/metadata_types.cc +++ b/chromium/ui/views/metadata/metadata_types.cc @@ -6,6 +6,8 @@ #include <utility> +#include "base/check_op.h" +#include "base/notreached.h" #include "base/strings/string_util.h" #include "ui/views/metadata/type_conversion.h" diff --git a/chromium/ui/views/metadata/type_conversion.cc b/chromium/ui/views/metadata/type_conversion.cc index 1ca8954dd20..d953161eff3 100644 --- a/chromium/ui/views/metadata/type_conversion.cc +++ b/chromium/ui/views/metadata/type_conversion.cc @@ -312,6 +312,8 @@ DEFINE_ENUM_CONVERTERS(ui::TextInputType, base::ASCIIToUTF16("TEXT_INPUT_TYPE_CONTENT_EDITABLE")}, {ui::TextInputType::TEXT_INPUT_TYPE_DATE_TIME_FIELD, base::ASCIIToUTF16("TEXT_INPUT_TYPE_DATE_TIME_FIELD")}, + {ui::TextInputType::TEXT_INPUT_TYPE_NULL, + base::ASCIIToUTF16("TEXT_INPUT_TYPE_NULL")}, {ui::TextInputType::TEXT_INPUT_TYPE_MAX, base::ASCIIToUTF16("TEXT_INPUT_TYPE_MAX")}) diff --git a/chromium/ui/views/native_cursor_aura.cc b/chromium/ui/views/native_cursor_aura.cc index 2ab60bc5cd1..bd346bba61b 100644 --- a/chromium/ui/views/native_cursor_aura.cc +++ b/chromium/ui/views/native_cursor_aura.cc @@ -5,7 +5,7 @@ #include "ui/views/native_cursor.h" #include "ui/base/cursor/cursor.h" -#include "ui/base/mojom/cursor_type.mojom-shared.h" +#include "ui/base/cursor/mojom/cursor_type.mojom-shared.h" namespace views { diff --git a/chromium/ui/views/painter.cc b/chromium/ui/views/painter.cc index 561a6653756..b0ca7de96fc 100644 --- a/chromium/ui/views/painter.cc +++ b/chromium/ui/views/painter.cc @@ -6,7 +6,7 @@ #include <utility> -#include "base/logging.h" +#include "base/check.h" #include "ui/compositor/layer.h" #include "ui/compositor/layer_delegate.h" #include "ui/compositor/layer_owner.h" diff --git a/chromium/ui/views/style/typography.cc b/chromium/ui/views/style/typography.cc index 0fc46aa6489..e269daff59a 100644 --- a/chromium/ui/views/style/typography.cc +++ b/chromium/ui/views/style/typography.cc @@ -4,7 +4,7 @@ #include "ui/views/style/typography.h" -#include "base/logging.h" +#include "base/check_op.h" #include "ui/views/layout/layout_provider.h" #include "ui/views/style/typography_provider.h" diff --git a/chromium/ui/views/touchui/touch_selection_controller_impl.cc b/chromium/ui/views/touchui/touch_selection_controller_impl.cc index 92a729807de..a2da9320fb2 100644 --- a/chromium/ui/views/touchui/touch_selection_controller_impl.cc +++ b/chromium/ui/views/touchui/touch_selection_controller_impl.cc @@ -7,8 +7,9 @@ #include <set> #include <utility> -#include "base/logging.h" +#include "base/check_op.h" #include "base/metrics/histogram_macros.h" +#include "base/notreached.h" #include "base/time/time.h" #include "ui/aura/client/cursor_client.h" #include "ui/aura/env.h" @@ -520,6 +521,13 @@ void TouchSelectionControllerImpl::HideHandles(bool quick) { cursor_handle_->SetWidgetVisible(false, quick); } +void TouchSelectionControllerImpl::ShowQuickMenuImmediatelyForTesting() { + if (quick_menu_timer_.IsRunning()) { + quick_menu_timer_.Stop(); + QuickMenuTimerFired(); + } +} + void TouchSelectionControllerImpl::SetDraggingHandle( EditingHandleView* handle) { dragging_handle_ = handle; @@ -636,7 +644,7 @@ void TouchSelectionControllerImpl::OnEvent(const ui::Event& event) { // from touch as this can clear an active selection generated by the pen. if ((event.flags() & (ui::EF_IS_SYNTHESIZED | ui::EF_FROM_TOUCH)) || event.AsMouseEvent()->pointer_details().pointer_type == - ui::EventPointerType::POINTER_TYPE_PEN) { + ui::EventPointerType::kPen) { return; } } diff --git a/chromium/ui/views/touchui/touch_selection_controller_impl.h b/chromium/ui/views/touchui/touch_selection_controller_impl.h index e748afcdbdd..af43216b179 100644 --- a/chromium/ui/views/touchui/touch_selection_controller_impl.h +++ b/chromium/ui/views/touchui/touch_selection_controller_impl.h @@ -21,10 +21,6 @@ namespace views { class WidgetDelegateView; -namespace test { -class WidgetTestInteractive; -} - // Touch specific implementation of TouchEditingControllerDeprecated. // Responsible for displaying selection handles and menu elements relevant in a // touch interface. @@ -45,9 +41,10 @@ class VIEWS_EXPORT TouchSelectionControllerImpl bool IsHandleDragInProgress() override; void HideHandles(bool quick) override; + void ShowQuickMenuImmediatelyForTesting(); + private: friend class TouchSelectionControllerImplTest; - friend class test::WidgetTestInteractive; void SetDraggingHandle(EditingHandleView* handle); diff --git a/chromium/ui/views/touchui/touch_selection_menu_views.cc b/chromium/ui/views/touchui/touch_selection_menu_views.cc index ca02bef42d8..8b014f1386d 100644 --- a/chromium/ui/views/touchui/touch_selection_menu_views.cc +++ b/chromium/ui/views/touchui/touch_selection_menu_views.cc @@ -10,6 +10,7 @@ #include "base/strings/utf_string_conversions.h" #include "ui/aura/window.h" #include "ui/base/l10n/l10n_util.h" +#include "ui/base/pointer/touch_editing_controller.h" #include "ui/display/display.h" #include "ui/display/screen.h" #include "ui/gfx/canvas.h" @@ -25,7 +26,15 @@ namespace views { namespace { -constexpr int kMenuCommands[] = {IDS_APP_CUT, IDS_APP_COPY, IDS_APP_PASTE}; +struct MenuCommand { + int command_id; + int message_id; +} kMenuCommands[] = { + {ui::TouchEditable::kCut, IDS_APP_CUT}, + {ui::TouchEditable::kCopy, IDS_APP_COPY}, + {ui::TouchEditable::kPaste, IDS_APP_PASTE}, +}; + constexpr int kSpacingBetweenButtons = 2; constexpr int kEllipsesButtonTag = -1; @@ -95,8 +104,8 @@ bool TouchSelectionMenuViews::IsMenuAvailable( const ui::TouchSelectionMenuClient* client) { DCHECK(client); - const auto is_enabled = [client](int command) { - return client->IsCommandIdEnabled(command); + const auto is_enabled = [client](MenuCommand command) { + return client->IsCommandIdEnabled(command.command_id); }; return std::any_of(std::cbegin(kMenuCommands), std::cend(kMenuCommands), is_enabled); @@ -113,12 +122,12 @@ void TouchSelectionMenuViews::CloseMenu() { TouchSelectionMenuViews::~TouchSelectionMenuViews() = default; void TouchSelectionMenuViews::CreateButtons() { - for (int command_id : kMenuCommands) { - if (!client_->IsCommandIdEnabled(command_id)) + for (const auto& command : kMenuCommands) { + if (!client_->IsCommandIdEnabled(command.command_id)) continue; - Button* button = - CreateButton(l10n_util::GetStringUTF16(command_id), command_id); + Button* button = CreateButton(l10n_util::GetStringUTF16(command.message_id), + command.command_id); AddChildView(button); } diff --git a/chromium/ui/views/vector_icons/linux_shutdown.icon b/chromium/ui/views/vector_icons/linux_shutdown.icon deleted file mode 100644 index 7feb96f2e3f..00000000000 --- a/chromium/ui/views/vector_icons/linux_shutdown.icon +++ /dev/null @@ -1,16 +0,0 @@ -// Copyright 2019 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. - -CANVAS_DIMENSIONS, 20, -MOVE_TO, 14.24f, 5.76f, -R_ARC_TO, 1, 1, 0, 1, 1, 1.41f, -1.41f, -R_ARC_TO, 8, 8, 0, 1, 1, -11.31f, 0, -R_ARC_TO, 1, 1, 0, 1, 1, 1.41f, 1.41f, -R_ARC_TO, 6, 6, 0, 1, 0, 8.49f, 0, -CLOSE, -MOVE_TO, 9, 3, -R_ARC_TO, 1, 1, 0, 0, 1, 2, 0, -R_V_LINE_TO, 8, -R_ARC_TO, 1, 1, 0, 1, 1, -2, 0, -CLOSE diff --git a/chromium/ui/views/view.cc b/chromium/ui/views/view.cc index 4cb98c1ee34..00b654c8e4f 100644 --- a/chromium/ui/views/view.cc +++ b/chromium/ui/views/view.cc @@ -8,11 +8,12 @@ #include <memory> #include <utility> +#include "base/check_op.h" #include "base/command_line.h" #include "base/containers/adapters.h" #include "base/feature_list.h" -#include "base/logging.h" #include "base/macros.h" +#include "base/notreached.h" #include "base/scoped_observer.h" #include "base/stl_util.h" #include "base/strings/stringprintf.h" @@ -49,6 +50,7 @@ #include "ui/views/border.h" #include "ui/views/buildflags.h" #include "ui/views/context_menu_controller.h" +#include "ui/views/controls/scroll_view.h" #include "ui/views/drag_controller.h" #include "ui/views/layout/layout_manager.h" #include "ui/views/metadata/metadata_impl_macros.h" @@ -1517,11 +1519,6 @@ void View::ShowContextMenu(const gfx::Point& p, context_menu_controller_->ShowContextMenuForView(this, p, source_type); } -// static -bool View::ShouldShowContextMenuOnMousePress() { - return kContextMenuOnMousePress; -} - gfx::Point View::GetKeyboardContextMenuLocation() { gfx::Rect vis_bounds = GetVisibleBounds(); gfx::Point screen_point(vis_bounds.x() + vis_bounds.width() / 2, @@ -1800,7 +1797,8 @@ void View::UpdateChildLayerVisibility(bool ancestor_visible) { layer()->SetVisible(layers_visible); for (ui::Layer* layer_beneath : layers_beneath_) layer_beneath->SetVisible(layers_visible); - } else { + } + { internal::ScopedChildrenLock lock(this); for (auto* child : children_) child->UpdateChildLayerVisibility(layers_visible); @@ -1870,6 +1868,11 @@ void View::OnPaintLayer(const ui::PaintContext& context) { PaintFromPaintRoot(context); } +void View::OnLayerTransformed(const gfx::Transform& old_transform, + ui::PropertyChangeReason reason) { + NotifyAccessibilityEvent(ax::mojom::Event::kLocationChanged, false); +} + void View::OnDeviceScaleFactorChanged(float old_device_scale_factor, float new_device_scale_factor) { snap_layer_to_pixel_boundary_ = @@ -1977,7 +1980,14 @@ void View::OnBlur() {} void View::Focus() { OnFocus(); - ScrollViewToVisible(); + + // If this is the contents root of a |ScrollView|, focus should bring the + // |ScrollView| to visible rather than resetting its content scroll position. + ScrollView* scroll_view = ScrollView::GetScrollViewForContents(this); + if (scroll_view) + scroll_view->ScrollViewToVisible(); + else + ScrollViewToVisible(); for (ViewObserver& observer : observers_) observer.OnViewFocused(this); diff --git a/chromium/ui/views/view.h b/chromium/ui/views/view.h index b1dfe17e2b2..1f4f1eb78fe 100644 --- a/chromium/ui/views/view.h +++ b/chromium/ui/views/view.h @@ -1222,10 +1222,6 @@ class VIEWS_EXPORT View : public ui::LayerDelegate, virtual void ShowContextMenu(const gfx::Point& p, ui::MenuSourceType source_type); - // On some platforms, we show context menu on mouse press instead of release. - // This method returns true for those platforms. - static bool ShouldShowContextMenuOnMousePress(); - // Returns the location, in screen coordinates, to show the context menu at // when the context menu is shown from the keyboard. This implementation // returns the middle of the visible region of this view. @@ -1503,6 +1499,8 @@ class VIEWS_EXPORT View : public ui::LayerDelegate, // Overridden from ui::LayerDelegate: void OnPaintLayer(const ui::PaintContext& context) override; + void OnLayerTransformed(const gfx::Transform& old_transform, + ui::PropertyChangeReason reason) override; void OnDeviceScaleFactorChanged(float old_device_scale_factor, float new_device_scale_factor) override; diff --git a/chromium/ui/views/view_model.cc b/chromium/ui/views/view_model.cc index 4768545b6f2..e3ba79f8cc6 100644 --- a/chromium/ui/views/view_model.cc +++ b/chromium/ui/views/view_model.cc @@ -6,7 +6,7 @@ #include <stddef.h> -#include "base/logging.h" +#include "base/check_op.h" #include "ui/views/view.h" namespace views { diff --git a/chromium/ui/views/view_unittest.cc b/chromium/ui/views/view_unittest.cc index 16294076ab4..dd69ffe1c53 100644 --- a/chromium/ui/views/view_unittest.cc +++ b/chromium/ui/views/view_unittest.cc @@ -379,6 +379,22 @@ TEST_F(ViewTest, OnBoundsChanged) { EXPECT_EQ(v.bounds(), new_rect); } +TEST_F(ViewTest, TransformFiresA11yEvent) { + TestView v; + v.SetPaintToLayer(); + + gfx::Rect bounds(0, 0, 200, 200); + v.last_a11y_event_ = ax::mojom::Event::kNone; + v.SetBoundsRect(bounds); + EXPECT_EQ(v.last_a11y_event_, ax::mojom::Event::kLocationChanged); + + gfx::Transform transform; + transform.Translate(gfx::Vector2dF(10, 10)); + v.last_a11y_event_ = ax::mojom::Event::kNone; + v.layer()->SetTransform(transform); + EXPECT_EQ(v.last_a11y_event_, ax::mojom::Event::kLocationChanged); +} + //////////////////////////////////////////////////////////////////////////////// // OnStateChanged //////////////////////////////////////////////////////////////////////////////// @@ -2012,21 +2028,21 @@ TEST_F(ViewTest, TextfieldCutCopyPaste) { // normal->SelectAll(false); - normal->ExecuteCommand(IDS_APP_CUT, 0); + normal->ExecuteCommand(Textfield::kCut, 0); base::string16 result; clipboard->ReadText(ui::ClipboardBuffer::kCopyPaste, &result); EXPECT_EQ(kNormalText, result); normal->SetText(kNormalText); // Let's revert to the original content. read_only->SelectAll(false); - read_only->ExecuteCommand(IDS_APP_CUT, 0); + read_only->ExecuteCommand(Textfield::kCut, 0); result.clear(); clipboard->ReadText(ui::ClipboardBuffer::kCopyPaste, &result); // Cut should have failed, so the clipboard content should not have changed. EXPECT_EQ(kNormalText, result); password->SelectAll(false); - password->ExecuteCommand(IDS_APP_CUT, 0); + password->ExecuteCommand(Textfield::kCut, 0); result.clear(); clipboard->ReadText(ui::ClipboardBuffer::kCopyPaste, &result); // Cut should have failed, so the clipboard content should not have changed. @@ -2038,19 +2054,19 @@ TEST_F(ViewTest, TextfieldCutCopyPaste) { // Start with |read_only| to observe a change in clipboard text. read_only->SelectAll(false); - read_only->ExecuteCommand(IDS_APP_COPY, 0); + read_only->ExecuteCommand(Textfield::kCopy, 0); result.clear(); clipboard->ReadText(ui::ClipboardBuffer::kCopyPaste, &result); EXPECT_EQ(kReadOnlyText, result); normal->SelectAll(false); - normal->ExecuteCommand(IDS_APP_COPY, 0); + normal->ExecuteCommand(Textfield::kCopy, 0); result.clear(); clipboard->ReadText(ui::ClipboardBuffer::kCopyPaste, &result); EXPECT_EQ(kNormalText, result); password->SelectAll(false); - password->ExecuteCommand(IDS_APP_COPY, 0); + password->ExecuteCommand(Textfield::kCopy, 0); result.clear(); clipboard->ReadText(ui::ClipboardBuffer::kCopyPaste, &result); // Text cannot be copied from an obscured field; the clipboard won't change. @@ -2062,18 +2078,18 @@ TEST_F(ViewTest, TextfieldCutCopyPaste) { // Attempting to paste kNormalText in a read-only text-field should fail. read_only->SelectAll(false); - read_only->ExecuteCommand(IDS_APP_PASTE, 0); + read_only->ExecuteCommand(Textfield::kPaste, 0); EXPECT_EQ(kReadOnlyText, read_only->GetText()); password->SelectAll(false); - password->ExecuteCommand(IDS_APP_PASTE, 0); + password->ExecuteCommand(Textfield::kPaste, 0); EXPECT_EQ(kNormalText, password->GetText()); // Copy from |read_only| to observe a change in the normal textfield text. read_only->SelectAll(false); - read_only->ExecuteCommand(IDS_APP_COPY, 0); + read_only->ExecuteCommand(Textfield::kCopy, 0); normal->SelectAll(false); - normal->ExecuteCommand(IDS_APP_PASTE, 0); + normal->ExecuteCommand(Textfield::kPaste, 0); EXPECT_EQ(kReadOnlyText, normal->GetText()); widget->CloseNow(); } @@ -2224,16 +2240,16 @@ TEST_F(ViewTest, HandleAccelerator) { EXPECT_EQ(1, view->accelerator_count_map_[return_accelerator]); // Add a child view associated with a child widget. - TestView* child_view = new TestView(); - child_view->Reset(); - child_view->AddAccelerator(return_accelerator); - EXPECT_EQ(child_view->accelerator_count_map_[return_accelerator], 0); Widget* child_widget = new Widget; Widget::InitParams child_params = CreateParams(Widget::InitParams::TYPE_CONTROL); child_params.parent = widget->GetNativeView(); child_widget->Init(std::move(child_params)); - child_widget->SetContentsView(child_view); + TestView* child_view = + child_widget->SetContentsView(std::make_unique<TestView>()); + child_view->Reset(); + child_view->AddAccelerator(return_accelerator); + EXPECT_EQ(child_view->accelerator_count_map_[return_accelerator], 0); FocusManager* child_focus_manager = child_widget->GetFocusManager(); ASSERT_TRUE(child_focus_manager); @@ -2526,10 +2542,11 @@ TEST_F(ViewTest, NativeViewHierarchyChanged) { child_params.parent = toplevel1->GetNativeView(); child->Init(std::move(child_params)); - ToplevelWidgetObserverView* observer_view = new ToplevelWidgetObserverView(); - EXPECT_EQ(nullptr, observer_view->toplevel()); + auto owning_observer_view = std::make_unique<ToplevelWidgetObserverView>(); + EXPECT_EQ(nullptr, owning_observer_view->toplevel()); - child->SetContentsView(observer_view); + ToplevelWidgetObserverView* observer_view = + child->SetContentsView(std::move(owning_observer_view)); EXPECT_EQ(toplevel1.get(), observer_view->toplevel()); Widget::ReparentNativeView(child->GetNativeView(), @@ -2760,15 +2777,12 @@ TEST_F(ViewTest, TransformVisibleBound) { widget->Init(std::move(params)); widget->GetRootView()->SetBoundsRect(viewport_bounds); - View* viewport = new View; - widget->SetContentsView(viewport); - View* contents = new View; - viewport->AddChildView(contents); + View* viewport = widget->SetContentsView(std::make_unique<View>()); + View* contents = viewport->AddChildView(std::make_unique<View>()); viewport->SetBoundsRect(viewport_bounds); contents->SetBoundsRect(gfx::Rect(0, 0, 100, 200)); - View* child = new View; - contents->AddChildView(child); + View* child = contents->AddChildView(std::make_unique<View>()); child->SetBoundsRect(gfx::Rect(10, 90, 50, 50)); EXPECT_EQ(gfx::Rect(0, 0, 50, 10), child->GetVisibleBounds()); @@ -2817,17 +2831,15 @@ TEST_F(ViewTest, OnVisibleBoundsChanged) { widget->Init(std::move(params)); widget->GetRootView()->SetBoundsRect(viewport_bounds); - View* viewport = new View; - widget->SetContentsView(viewport); - View* contents = new View; - viewport->AddChildView(contents); + View* viewport = widget->SetContentsView(std::make_unique<View>()); + View* contents = viewport->AddChildView(std::make_unique<View>()); viewport->SetBoundsRect(viewport_bounds); contents->SetBoundsRect(gfx::Rect(0, 0, 100, 200)); // Create a view that cares about visible bounds notifications, and position // it just outside the visible bounds of the viewport. - VisibleBoundsView* child = new VisibleBoundsView; - contents->AddChildView(child); + VisibleBoundsView* child = + contents->AddChildView(std::make_unique<VisibleBoundsView>()); child->SetBoundsRect(gfx::Rect(10, 110, 50, 50)); // The child bound should be fully clipped. @@ -2919,14 +2931,13 @@ TEST_F(ViewTest, AddAndRemoveSchedulePaints) { widget->Init(std::move(params)); widget->GetRootView()->SetBoundsRect(viewport_bounds); - TestView* parent_view = new TestView; - widget->SetContentsView(parent_view); + TestView* parent_view = widget->SetContentsView(std::make_unique<TestView>()); parent_view->SetBoundsRect(viewport_bounds); parent_view->scheduled_paint_rects_.clear(); - View* child_view = new View; - child_view->SetBoundsRect(gfx::Rect(0, 0, 20, 20)); - parent_view->AddChildView(child_view); + auto owning_child_view = std::make_unique<View>(); + owning_child_view->SetBoundsRect(gfx::Rect(0, 0, 20, 20)); + View* child_view = parent_view->AddChildView(std::move(owning_child_view)); ASSERT_EQ(1U, parent_view->scheduled_paint_rects_.size()); EXPECT_EQ(child_view->bounds(), parent_view->scheduled_paint_rects_.front()); @@ -3982,8 +3993,7 @@ TEST_F(ViewLayerTest, LayerToggling) { // Because we lazily create textures the calls to DrawTree are necessary to // ensure we trigger creation of textures. ui::Layer* root_layer = widget()->GetLayer(); - View* content_view = new View; - widget()->SetContentsView(content_view); + View* content_view = widget()->SetContentsView(std::make_unique<View>()); // Create v1, give it a bounds and verify everything is set up correctly. View* v1 = new View; @@ -4038,8 +4048,7 @@ TEST_F(ViewLayerTest, LayerToggling) { // Verifies turning on a layer wires up children correctly. TEST_F(ViewLayerTest, NestedLayerToggling) { - View* content_view = new View; - widget()->SetContentsView(content_view); + View* content_view = widget()->SetContentsView(std::make_unique<View>()); // Create v1, give it a bounds and verify everything is set up correctly. View* v1 = content_view->AddChildView(std::make_unique<View>()); @@ -4063,8 +4072,7 @@ TEST_F(ViewLayerTest, NestedLayerToggling) { } TEST_F(ViewLayerTest, LayerAnimator) { - View* content_view = new View; - widget()->SetContentsView(content_view); + View* content_view = widget()->SetContentsView(std::make_unique<View>()); View* v1 = content_view->AddChildView(std::make_unique<View>()); v1->SetPaintToLayer(); @@ -4083,8 +4091,7 @@ TEST_F(ViewLayerTest, LayerAnimator) { // Verifies the bounds of a layer are updated if the bounds of ancestor that // doesn't have a layer change. TEST_F(ViewLayerTest, BoundsChangeWithLayer) { - View* content_view = new View; - widget()->SetContentsView(content_view); + View* content_view = widget()->SetContentsView(std::make_unique<View>()); View* v1 = content_view->AddChildView(std::make_unique<View>()); v1->SetBoundsRect(gfx::Rect(20, 30, 140, 150)); @@ -4120,8 +4127,7 @@ TEST_F(ViewLayerTest, BoundsChangeWithLayer) { // Make sure layers are positioned correctly in RTL. TEST_F(ViewLayerTest, BoundInRTL) { base::test::ScopedRestoreICUDefaultLocale scoped_locale_("he"); - View* view = new View; - widget()->SetContentsView(view); + View* view = widget()->SetContentsView(std::make_unique<View>()); int content_width = view->width(); @@ -4171,8 +4177,7 @@ TEST_F(ViewLayerTest, BoundInRTL) { // Make sure that resizing a parent in RTL correctly repositions its children. TEST_F(ViewLayerTest, ResizeParentInRTL) { base::test::ScopedRestoreICUDefaultLocale scoped_locale_("he"); - View* view = new View; - widget()->SetContentsView(view); + View* view = widget()->SetContentsView(std::make_unique<View>()); int content_width = view->width(); @@ -4218,11 +4223,10 @@ TEST_F(ViewLayerTest, ResizeParentInRTL) { // Makes sure a transform persists after toggling the visibility. TEST_F(ViewLayerTest, ToggleVisibilityWithTransform) { - View* view = new View; + View* view = widget()->SetContentsView(std::make_unique<View>()); gfx::Transform transform; transform.Scale(2.0, 2.0); view->SetTransform(transform); - widget()->SetContentsView(view); EXPECT_EQ(2.0f, view->GetTransform().matrix().get(0, 0)); view->SetVisible(false); @@ -4234,11 +4238,10 @@ TEST_F(ViewLayerTest, ToggleVisibilityWithTransform) { // Verifies a transform persists after removing/adding a view with a transform. TEST_F(ViewLayerTest, ResetTransformOnLayerAfterAdd) { - View* view = new View; + View* view = widget()->SetContentsView(std::make_unique<View>()); gfx::Transform transform; transform.Scale(2.0, 2.0); view->SetTransform(transform); - widget()->SetContentsView(view); EXPECT_EQ(2.0f, view->GetTransform().matrix().get(0, 0)); ASSERT_TRUE(view->layer() != nullptr); EXPECT_EQ(2.0f, view->layer()->transform().matrix().get(0, 0)); @@ -4254,8 +4257,7 @@ TEST_F(ViewLayerTest, ResetTransformOnLayerAfterAdd) { // Makes sure that layer visibility is correct after toggling View visibility. TEST_F(ViewLayerTest, ToggleVisibilityWithLayer) { - View* content_view = new View; - widget()->SetContentsView(content_view); + View* content_view = widget()->SetContentsView(std::make_unique<View>()); // The view isn't attached to a widget or a parent view yet. But it should // still have a layer, but the layer should not be attached to the root @@ -4289,8 +4291,7 @@ TEST_F(ViewLayerTest, ToggleVisibilityWithLayer) { // Tests that the layers in the subtree are orphaned after a View is removed // from the parent. TEST_F(ViewLayerTest, OrphanLayerAfterViewRemove) { - View* content_view = new View; - widget()->SetContentsView(content_view); + View* content_view = widget()->SetContentsView(std::make_unique<View>()); View* v1 = new View; content_view->AddChildView(v1); @@ -4335,8 +4336,8 @@ class PaintTrackingView : public View { // Makes sure child views with layers aren't painted when paint starts at an // ancestor. TEST_F(ViewLayerTest, DontPaintChildrenWithLayers) { - PaintTrackingView* content_view = new PaintTrackingView; - widget()->SetContentsView(content_view); + PaintTrackingView* content_view = + widget()->SetContentsView(std::make_unique<PaintTrackingView>()); content_view->SetPaintToLayer(); GetRootLayer()->GetCompositor()->ScheduleDraw(); ui::DrawWaiterForTest::WaitForCompositingEnded( @@ -4403,9 +4404,8 @@ TEST_F(ViewLayerTest, NoParentPaintWhenSwitchingPaintToLayerFromTrueToTrue) { // Tests that the visibility of child layers are updated correctly when a View's // visibility changes. TEST_F(ViewLayerTest, VisibilityChildLayers) { - View* v1 = new View; + View* v1 = widget()->SetContentsView(std::make_unique<View>()); v1->SetPaintToLayer(); - widget()->SetContentsView(v1); View* v2 = v1->AddChildView(std::make_unique<View>()); @@ -4448,9 +4448,8 @@ TEST_F(ViewLayerTest, VisibilityChildLayers) { // reparents views etc. Unrelated changes can appear to break this test. So // marking this as FLAKY. TEST_F(ViewLayerTest, DISABLED_ViewLayerTreesInSync) { - View* content = new View; + View* content = widget()->SetContentsView(std::make_unique<View>()); content->SetPaintToLayer(); - widget()->SetContentsView(content); widget()->Show(); ConstructTree(content, 5); @@ -4469,8 +4468,7 @@ TEST_F(ViewLayerTest, DISABLED_ViewLayerTreesInSync) { // Verifies when views are reordered the layer is also reordered. The widget is // providing the parent layer. TEST_F(ViewLayerTest, ReorderUnderWidget) { - View* content = new View; - widget()->SetContentsView(content); + View* content = widget()->SetContentsView(std::make_unique<View>()); View* c1 = content->AddChildView(std::make_unique<View>()); c1->SetPaintToLayer(); View* c2 = content->AddChildView(std::make_unique<View>()); @@ -4490,8 +4488,7 @@ TEST_F(ViewLayerTest, ReorderUnderWidget) { // Verifies that the layer of a view can be acquired properly. TEST_F(ViewLayerTest, AcquireLayer) { - View* content = new View; - widget()->SetContentsView(content); + View* content = widget()->SetContentsView(std::make_unique<View>()); std::unique_ptr<View> c1(new View); c1->SetPaintToLayer(); EXPECT_TRUE(c1->layer()); @@ -4537,8 +4534,7 @@ TEST_F(ViewLayerTest, RecreateLayerZOrder) { // Verify the z-order of the layers as a result of calling RecreateLayer when // the widget is the parent with the layer. TEST_F(ViewLayerTest, RecreateLayerZOrderWidgetParent) { - View* v = new View(); - widget()->SetContentsView(v); + View* v = widget()->SetContentsView(std::make_unique<View>()); View* v1 = v->AddChildView(std::make_unique<View>()); v1->SetPaintToLayer(); @@ -4606,12 +4602,10 @@ std::string ToString(const gfx::Vector2dF& vector) { TEST_F(ViewLayerTest, SnapLayerToPixel) { viz::ParentLocalSurfaceIdAllocator allocator; allocator.GenerateId(); - View* v1 = new View; + View* v1 = widget()->SetContentsView(std::make_unique<View>()); View* v11 = v1->AddChildView(std::make_unique<View>()); - widget()->SetContentsView(v1); - const gfx::Size& size = GetRootLayer()->GetCompositor()->size(); GetRootLayer()->GetCompositor()->SetScaleAndSize( 1.25f, size, allocator.GetCurrentLocalSurfaceIdAllocation()); @@ -4699,8 +4693,7 @@ TEST_F(ViewLayerTest, LayerBeneathAtFractionalScale) { GetRootLayer()->GetCompositor()->SetScaleAndSize( device_scale, size, allocator.GetCurrentLocalSurfaceIdAllocation()); - View* view = new View; - widget()->SetContentsView(view); + View* view = widget()->SetContentsView(std::make_unique<View>()); ui::Layer layer; view->AddLayerBeneathView(&layer); @@ -4828,6 +4821,31 @@ TEST_F(ViewLayerTest, LayerBeneathTransformed) { EXPECT_TRUE(layer.transform().IsIdentity()); } +TEST_F(ViewLayerTest, UpdateChildLayerVisibilityEvenIfLayer) { + View root; + root.SetPaintToLayer(); + + View* view = root.AddChildView(std::make_unique<View>()); + view->SetPaintToLayer(); + View* child = view->AddChildView(std::make_unique<View>()); + child->SetPaintToLayer(); + EXPECT_TRUE(child->layer()->GetAnimator()->GetTargetVisibility()); + + // Makes the view invisible then destroy the layer. + view->SetVisible(false); + view->DestroyLayer(); + EXPECT_FALSE(child->layer()->GetAnimator()->GetTargetVisibility()); + + view->SetVisible(true); + view->SetPaintToLayer(); + EXPECT_TRUE(child->layer()->GetAnimator()->GetTargetVisibility()); + + // Destroys the layer then make the view invisible. + view->DestroyLayer(); + view->SetVisible(false); + EXPECT_FALSE(child->layer()->GetAnimator()->GetTargetVisibility()); +} + TEST_F(ViewLayerTest, LayerBeneathStackedCorrectly) { using ui::test::ChildLayerNamesAsString; @@ -4962,12 +4980,10 @@ class ViewLayerPixelCanvasTest : public ViewLayerTest { TEST_F(ViewLayerPixelCanvasTest, SnapLayerToPixel) { viz::ParentLocalSurfaceIdAllocator allocator; allocator.GenerateId(); - View* v1 = new View; + View* v1 = widget()->SetContentsView(std::make_unique<View>()); View* v2 = v1->AddChildView(std::make_unique<View>()); PaintLayerView* v3 = v2->AddChildView(std::make_unique<PaintLayerView>()); - widget()->SetContentsView(v1); - const gfx::Size& size = GetRootLayer()->GetCompositor()->size(); GetRootLayer()->GetCompositor()->SetScaleAndSize( 1.6f, size, allocator.GetCurrentLocalSurfaceIdAllocation()); @@ -5034,8 +5050,7 @@ TEST_F(ViewLayerPixelCanvasTest, LayerBeneathOnPixelCanvas) { GetRootLayer()->GetCompositor()->SetScaleAndSize( device_scale, size, allocator.GetCurrentLocalSurfaceIdAllocation()); - View* view = new View; - widget()->SetContentsView(view); + View* view = widget()->SetContentsView(std::make_unique<View>()); ui::Layer layer; view->AddLayerBeneathView(&layer); diff --git a/chromium/ui/views/views_features.cc b/chromium/ui/views/views_features.cc index 210657bae32..13bf1c9f0e2 100644 --- a/chromium/ui/views/views_features.cc +++ b/chromium/ui/views/views_features.cc @@ -28,5 +28,11 @@ const base::Feature kEnablePlatformHighContrastInkDrop{ const base::Feature kEnableViewPaintOptimization{ "EnableViewPaintOptimization", base::FEATURE_DISABLED_BY_DEFAULT}; +// Change views::Textfield to take focus on a completed tap, rather than +// immediately on tap down. This only affects touch input. See +// https://crbug.com/1069634. +const base::Feature kTextfieldFocusOnTapUp{"TextfieldFocusOnTapUp", + base::FEATURE_DISABLED_BY_DEFAULT}; + } // namespace features } // namespace views diff --git a/chromium/ui/views/views_features.h b/chromium/ui/views/views_features.h index 1daff6da79c..e4340bbb13a 100644 --- a/chromium/ui/views/views_features.h +++ b/chromium/ui/views/views_features.h @@ -17,6 +17,7 @@ namespace features { VIEWS_EXPORT extern const base::Feature kEnableMDRoundedCornersOnDialogs; VIEWS_EXPORT extern const base::Feature kEnablePlatformHighContrastInkDrop; VIEWS_EXPORT extern const base::Feature kEnableViewPaintOptimization; +VIEWS_EXPORT extern const base::Feature kTextfieldFocusOnTapUp; } // namespace features } // namespace views diff --git a/chromium/ui/views/widget/any_widget_observer_unittest.cc b/chromium/ui/views/widget/any_widget_observer_unittest.cc index 4a1bf692f57..344fd0943bd 100644 --- a/chromium/ui/views/widget/any_widget_observer_unittest.cc +++ b/chromium/ui/views/widget/any_widget_observer_unittest.cc @@ -83,7 +83,7 @@ class NamedWidgetShownWaiterTest : public views::test::WidgetTest { Widget* widget = new Widget; Widget::InitParams params = CreateParams(Widget::InitParams::TYPE_WINDOW); params.native_widget = views::test::CreatePlatformNativeWidgetImpl( - params, widget, views::test::kStubCapture, nullptr); + widget, views::test::kStubCapture, nullptr); params.name = name; widget->Init(std::move(params)); return widget; 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 707f2b4aa33..3ee330f3598 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 @@ -18,23 +18,23 @@ #include "ui/aura/client/drag_drop_delegate.h" #include "ui/aura/window.h" #include "ui/aura/window_tree_host.h" +#include "ui/base/cursor/mojom/cursor_type.mojom-shared.h" #include "ui/base/dragdrop/drop_target_event.h" #include "ui/base/dragdrop/os_exchange_data.h" -#include "ui/base/dragdrop/os_exchange_data_provider_aurax11.h" +#include "ui/base/dragdrop/os_exchange_data_provider_x11.h" #include "ui/base/layout.h" -#include "ui/base/mojom/cursor_type.mojom-shared.h" #include "ui/base/x/selection_utils.h" #include "ui/base/x/x11_drag_context.h" #include "ui/base/x/x11_util.h" +#include "ui/base/x/x11_whole_screen_move_loop.h" #include "ui/display/screen.h" #include "ui/events/event.h" #include "ui/events/event_utils.h" #include "ui/gfx/image/image_skia.h" #include "ui/gfx/x/x11.h" +#include "ui/platform_window/x11/x11_topmost_window_finder.h" #include "ui/views/controls/image_view.h" #include "ui/views/widget/desktop_aura/desktop_native_cursor_manager.h" -#include "ui/views/widget/desktop_aura/x11_topmost_window_finder.h" -#include "ui/views/widget/desktop_aura/x11_whole_screen_move_loop.h" #include "ui/views/widget/widget.h" using aura::client::DragDropDelegate; @@ -171,8 +171,11 @@ int DesktopDragDropClientAuraX11::StartDragAndDrop( // Windows has a specific method, DoDragDrop(), which performs the entire // drag. We have to emulate this, so we spin off a nested runloop which will // track all cursor movement and reroute events to a specific handler. - move_loop_->RunMoveLoop(source_window, cursor_manager_->GetInitializedCursor( - ui::mojom::CursorType::kGrabbing)); + move_loop_->RunMoveLoop( + !source_window->HasCapture(), + source_window->GetHost()->last_cursor().platform(), + cursor_manager_->GetInitializedCursor(ui::mojom::CursorType::kGrabbing) + .platform()); if (alive) { auto resulting_operation = negotiated_operation(); @@ -248,9 +251,9 @@ void DesktopDragDropClientAuraX11::OnMoveLoopEnded() { XDragDropClient::HandleMoveLoopEnded(); } -std::unique_ptr<X11MoveLoop> DesktopDragDropClientAuraX11::CreateMoveLoop( +std::unique_ptr<ui::X11MoveLoop> DesktopDragDropClientAuraX11::CreateMoveLoop( X11MoveLoopDelegate* delegate) { - return base::WrapUnique(new X11WholeScreenMoveLoop(this)); + return base::WrapUnique(new ui::X11WholeScreenMoveLoop(this)); } void DesktopDragDropClientAuraX11::DragTranslate( @@ -280,7 +283,7 @@ void DesktopDragDropClientAuraX11::DragTranslate( DCHECK(target_current_context()); *data = std::make_unique<OSExchangeData>( - std::make_unique<ui::OSExchangeDataProviderAuraX11>( + std::make_unique<ui::OSExchangeDataProviderX11>( xwindow(), target_current_context()->fetched_targets())); gfx::Point location = root_location; aura::Window::ConvertPointToTarget(root_window_, target_window_, &location); @@ -326,7 +329,7 @@ void DesktopDragDropClientAuraX11::NotifyDragLeave() { std::unique_ptr<ui::XTopmostWindowFinder> DesktopDragDropClientAuraX11::CreateWindowFinder() { - return std::make_unique<X11TopmostWindowFinder>(); + return std::make_unique<ui::X11TopmostWindowFinder>(); } int DesktopDragDropClientAuraX11::UpdateDrag(const gfx::Point& screen_point) { @@ -362,7 +365,8 @@ void DesktopDragDropClientAuraX11::UpdateCursor( cursor_type = ui::mojom::CursorType::kDndLink; break; } - move_loop_->UpdateCursor(cursor_manager_->GetInitializedCursor(cursor_type)); + move_loop_->UpdateCursor( + cursor_manager_->GetInitializedCursor(cursor_type).platform()); } void DesktopDragDropClientAuraX11::OnBeginForeignDrag(XID window) { @@ -394,7 +398,7 @@ int DesktopDragDropClientAuraX11::PerformDrop() { aura::client::GetDragDropDelegate(target_window_); if (delegate) { auto data(std::make_unique<ui::OSExchangeData>( - std::make_unique<ui::OSExchangeDataProviderAuraX11>( + std::make_unique<ui::OSExchangeDataProviderX11>( xwindow(), target_current_context()->fetched_targets()))); ui::DropTargetEvent drop_event( diff --git a/chromium/ui/views/widget/desktop_aura/desktop_drag_drop_client_aurax11.h b/chromium/ui/views/widget/desktop_aura/desktop_drag_drop_client_aurax11.h index 7ae53151b9e..a06220b1864 100644 --- a/chromium/ui/views/widget/desktop_aura/desktop_drag_drop_client_aurax11.h +++ b/chromium/ui/views/widget/desktop_aura/desktop_drag_drop_client_aurax11.h @@ -16,6 +16,7 @@ #include "ui/base/cursor/cursor.h" #include "ui/base/dragdrop/drag_drop_types.h" #include "ui/base/x/x11_drag_drop_client.h" +#include "ui/base/x/x11_move_loop_delegate.h" #include "ui/events/event_constants.h" #include "ui/events/platform/x11/x11_event_source.h" #include "ui/events/x/x11_window_event_manager.h" @@ -23,7 +24,6 @@ #include "ui/gfx/geometry/size.h" #include "ui/gfx/x/x11.h" #include "ui/views/views_export.h" -#include "ui/views/widget/desktop_aura/x11_move_loop_delegate.h" namespace aura { namespace client { @@ -40,12 +40,12 @@ namespace ui { class DropTargetEvent; class OSExchangeData; class XTopmostWindowFinder; +class X11MoveLoop; } // namespace ui namespace views { class DesktopNativeCursorManager; class Widget; -class X11MoveLoop; // Implements drag and drop on X11 for aura. On one side, this class takes raw // X11 events forwarded from DesktopWindowTreeHostLinux, while on the other, it @@ -56,7 +56,7 @@ class VIEWS_EXPORT DesktopDragDropClientAuraX11 public aura::client::DragDropClient, public ui::XEventDispatcher, public aura::WindowObserver, - public X11MoveLoopDelegate { + public ui::X11MoveLoopDelegate { public: DesktopDragDropClientAuraX11( aura::Window* root_window, @@ -85,7 +85,7 @@ class VIEWS_EXPORT DesktopDragDropClientAuraX11 // aura::WindowObserver: void OnWindowDestroyed(aura::Window* window) override; - // X11MoveLoopDelegate: + // ui::X11MoveLoopDelegate: void OnMouseMovement(const gfx::Point& screen_point, int flags, base::TimeTicks event_time) override; @@ -97,7 +97,7 @@ class VIEWS_EXPORT DesktopDragDropClientAuraX11 Widget* drag_widget() { return drag_widget_.get(); } // Creates a move loop. Virtual for testing. - virtual std::unique_ptr<X11MoveLoop> CreateMoveLoop( + virtual std::unique_ptr<ui::X11MoveLoop> CreateMoveLoop( X11MoveLoopDelegate* delegate); private: @@ -125,8 +125,8 @@ class VIEWS_EXPORT DesktopDragDropClientAuraX11 void EndMoveLoop() override; // A nested run loop that notifies this object of events through the - // X11MoveLoopDelegate interface. - std::unique_ptr<X11MoveLoop> move_loop_; + // ui::X11MoveLoopDelegate interface. + std::unique_ptr<ui::X11MoveLoop> move_loop_; aura::Window* root_window_; diff --git a/chromium/ui/views/widget/desktop_aura/desktop_drag_drop_client_aurax11_unittest.cc b/chromium/ui/views/widget/desktop_aura/desktop_drag_drop_client_aurax11_unittest.cc index b188e6c9a40..68c1ba2c47d 100644 --- a/chromium/ui/views/widget/desktop_aura/desktop_drag_drop_client_aurax11_unittest.cc +++ b/chromium/ui/views/widget/desktop_aura/desktop_drag_drop_client_aurax11_unittest.cc @@ -20,6 +20,7 @@ #include "ui/aura/window.h" #include "ui/aura/window_tree_host.h" #include "ui/base/dragdrop/os_exchange_data.h" +#include "ui/base/x/x11_move_loop.h" #include "ui/base/x/x11_util.h" #include "ui/events/event_utils.h" #include "ui/gfx/x/x11.h" @@ -29,7 +30,6 @@ #include "ui/views/widget/desktop_aura/desktop_drag_drop_client_aurax11.h" #include "ui/views/widget/desktop_aura/desktop_native_cursor_manager.h" #include "ui/views/widget/desktop_aura/desktop_native_widget_aura.h" -#include "ui/views/widget/desktop_aura/x11_move_loop.h" #include "ui/views/widget/widget.h" namespace views { @@ -66,24 +66,26 @@ class ClientMessageEventCollector { DISALLOW_COPY_AND_ASSIGN(ClientMessageEventCollector); }; -// An implementation of X11MoveLoop where RunMoveLoop() always starts the move -// loop. -class TestMoveLoop : public X11MoveLoop { +// An implementation of ui::X11MoveLoop where RunMoveLoop() always starts the +// move loop. +class TestMoveLoop : public ui::X11MoveLoop { public: - explicit TestMoveLoop(X11MoveLoopDelegate* delegate); + explicit TestMoveLoop(ui::X11MoveLoopDelegate* delegate); ~TestMoveLoop() override; // Returns true if the move loop is running. bool IsRunning() const; - // X11MoveLoop: - bool RunMoveLoop(aura::Window* window, gfx::NativeCursor cursor) override; - void UpdateCursor(gfx::NativeCursor cursor) override; + // ui::X11MoveLoop: + bool RunMoveLoop(bool can_grab_pointer, + ::Cursor old_cursor, + ::Cursor new_cursor) override; + void UpdateCursor(::Cursor cursor) override; void EndMoveLoop() override; private: // Not owned. - X11MoveLoopDelegate* delegate_; + ui::X11MoveLoopDelegate* delegate_; // Ends the move loop. base::OnceClosure quit_closure_; @@ -109,8 +111,8 @@ class SimpleTestDragDropClient : public DesktopDragDropClientAuraX11 { private: // DesktopDragDropClientAuraX11: - std::unique_ptr<X11MoveLoop> CreateMoveLoop( - X11MoveLoopDelegate* delegate) override; + std::unique_ptr<ui::X11MoveLoop> CreateMoveLoop( + ui::X11MoveLoopDelegate* delegate) override; XID FindWindowFor(const gfx::Point& screen_point) override; // The XID of the window which is simulated to be the topmost window. @@ -207,7 +209,7 @@ void ClientMessageEventCollector::RecordEvent( /////////////////////////////////////////////////////////////////////////////// // TestMoveLoop -TestMoveLoop::TestMoveLoop(X11MoveLoopDelegate* delegate) +TestMoveLoop::TestMoveLoop(ui::X11MoveLoopDelegate* delegate) : delegate_(delegate) {} TestMoveLoop::~TestMoveLoop() = default; @@ -216,7 +218,9 @@ bool TestMoveLoop::IsRunning() const { return is_running_; } -bool TestMoveLoop::RunMoveLoop(aura::Window* window, gfx::NativeCursor cursor) { +bool TestMoveLoop::RunMoveLoop(bool can_grab_pointer, + ::Cursor old_cursor, + ::Cursor new_cursor) { is_running_ = true; base::RunLoop run_loop; quit_closure_ = run_loop.QuitClosure(); @@ -224,7 +228,7 @@ bool TestMoveLoop::RunMoveLoop(aura::Window* window, gfx::NativeCursor cursor) { return true; } -void TestMoveLoop::UpdateCursor(gfx::NativeCursor cursor) {} +void TestMoveLoop::UpdateCursor(::Cursor cursor) {} void TestMoveLoop::EndMoveLoop() { if (is_running_) { @@ -255,8 +259,8 @@ bool SimpleTestDragDropClient::IsMoveLoopRunning() { return loop_->IsRunning(); } -std::unique_ptr<X11MoveLoop> SimpleTestDragDropClient::CreateMoveLoop( - X11MoveLoopDelegate* delegate) { +std::unique_ptr<ui::X11MoveLoop> SimpleTestDragDropClient::CreateMoveLoop( + ui::X11MoveLoopDelegate* delegate) { loop_ = new TestMoveLoop(delegate); return base::WrapUnique(loop_); } @@ -306,7 +310,7 @@ void TestDragDropClient::OnStatus(XID target_window, event.data.l[2] = 0; event.data.l[3] = 0; event.data.l[4] = accepted_action; - OnXdndStatus(event); + HandleXdndEvent(event); } void TestDragDropClient::OnFinished(XID target_window, @@ -321,7 +325,7 @@ void TestDragDropClient::OnFinished(XID target_window, event.data.l[2] = performed_action; event.data.l[3] = 0; event.data.l[4] = 0; - OnXdndFinished(event); + HandleXdndEvent(event); } void TestDragDropClient::SetTopmostXWindowAndMoveMouse(::Window xid) { diff --git a/chromium/ui/views/widget/desktop_aura/desktop_drag_drop_client_ozone.cc b/chromium/ui/views/widget/desktop_aura/desktop_drag_drop_client_ozone.cc index 1ed740d20ce..24a959b321d 100644 --- a/chromium/ui/views/widget/desktop_aura/desktop_drag_drop_client_ozone.cc +++ b/chromium/ui/views/widget/desktop_aura/desktop_drag_drop_client_ozone.cc @@ -18,10 +18,10 @@ #include "ui/aura/window.h" #include "ui/aura/window_tree_host.h" #include "ui/base/clipboard/clipboard.h" +#include "ui/base/cursor/mojom/cursor_type.mojom-shared.h" #include "ui/base/dragdrop/drag_drop_types.h" #include "ui/base/dragdrop/drop_target_event.h" #include "ui/base/dragdrop/os_exchange_data_provider_aura.h" -#include "ui/base/mojom/cursor_type.mojom-shared.h" #include "ui/platform_window/platform_window_delegate.h" #include "ui/platform_window/platform_window_handler/wm_drag_handler.h" #include "ui/views/widget/desktop_aura/desktop_native_cursor_manager.h" @@ -239,6 +239,7 @@ void DesktopDragDropClientOzone::PerformDrop() { drag_operation_ = drag_drop_delegate_->OnPerformDrop( *event, std::move(os_exchange_data_)); DragDropSessionCompleted(); + ResetDragDropTarget(); } } // namespace views diff --git a/chromium/ui/views/widget/desktop_aura/desktop_native_cursor_manager.cc b/chromium/ui/views/widget/desktop_aura/desktop_native_cursor_manager.cc index 478b16bc606..ab7213a3889 100644 --- a/chromium/ui/views/widget/desktop_aura/desktop_native_cursor_manager.cc +++ b/chromium/ui/views/widget/desktop_aura/desktop_native_cursor_manager.cc @@ -6,10 +6,11 @@ #include <utility> +#include "base/trace_event/trace_event.h" #include "ui/aura/window_event_dispatcher.h" #include "ui/aura/window_tree_host.h" #include "ui/base/cursor/cursor_loader.h" -#include "ui/base/mojom/cursor_type.mojom-shared.h" +#include "ui/base/cursor/mojom/cursor_type.mojom-shared.h" namespace views { @@ -59,6 +60,8 @@ void DesktopNativeCursorManager::SetCursor( void DesktopNativeCursorManager::SetVisibility( bool visible, wm::NativeCursorManagerDelegate* delegate) { + TRACE_EVENT1("ui,input", "DesktopNativeCursorManager::SetVisibility", + "visible", visible); delegate->CommitVisibility(visible); if (visible) { @@ -83,6 +86,7 @@ void DesktopNativeCursorManager::SetCursorSize( void DesktopNativeCursorManager::SetMouseEventsEnabled( bool enabled, wm::NativeCursorManagerDelegate* delegate) { + TRACE_EVENT0("ui,input", "DesktopNativeCursorManager::SetMouseEventsEnabled"); delegate->CommitMouseEventsEnabled(enabled); // TODO(erg): In the ash version, we set the last mouse location on Env. I'm diff --git a/chromium/ui/views/widget/desktop_aura/desktop_native_cursor_manager.h b/chromium/ui/views/widget/desktop_aura/desktop_native_cursor_manager.h index fdb5d86b797..d926393dd7f 100644 --- a/chromium/ui/views/widget/desktop_aura/desktop_native_cursor_manager.h +++ b/chromium/ui/views/widget/desktop_aura/desktop_native_cursor_manager.h @@ -10,7 +10,7 @@ #include "base/compiler_specific.h" #include "base/macros.h" -#include "ui/base/mojom/cursor_type.mojom-forward.h" +#include "ui/base/cursor/mojom/cursor_type.mojom-forward.h" #include "ui/views/views_export.h" #include "ui/wm/core/native_cursor_manager.h" 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 a28f778d4a5..71e7bbc378b 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 @@ -22,7 +22,7 @@ #include "ui/aura/test/window_occlusion_tracker_test_api.h" #include "ui/aura/window.h" #include "ui/aura/window_tree_host.h" -#include "ui/base/mojom/cursor_type.mojom-shared.h" +#include "ui/base/cursor/mojom/cursor_type.mojom-shared.h" #include "ui/display/screen.h" #include "ui/events/event_processor.h" #include "ui/events/event_utils.h" @@ -286,8 +286,8 @@ 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; - params.native_widget = CreatePlatformNativeWidgetImpl(params, widget.get(), - kStubCapture, nullptr); + params.native_widget = + CreatePlatformNativeWidgetImpl(widget.get(), kStubCapture, nullptr); widget->Init(std::move(params)); widget->Show(); return widget; diff --git a/chromium/ui/views/widget/desktop_aura/desktop_screen_ozone.cc b/chromium/ui/views/widget/desktop_aura/desktop_screen_ozone.cc index a8d86fd87fc..6f9d29fbcbb 100644 --- a/chromium/ui/views/widget/desktop_aura/desktop_screen_ozone.cc +++ b/chromium/ui/views/widget/desktop_aura/desktop_screen_ozone.cc @@ -2,13 +2,28 @@ // 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/aura/screen_ozone.h" #include "ui/views/widget/desktop_aura/desktop_screen.h" +#include "ui/views/widget/desktop_aura/desktop_window_tree_host_platform.h" namespace views { +DesktopScreenOzone::DesktopScreenOzone() = default; + +DesktopScreenOzone::~DesktopScreenOzone() = default; + +gfx::NativeWindow DesktopScreenOzone::GetNativeWindowFromAcceleratedWidget( + gfx::AcceleratedWidget widget) const { + if (!widget) + return nullptr; + return views::DesktopWindowTreeHostPlatform::GetContentWindowForWidget( + widget); +} + display::Screen* CreateDesktopScreen() { - return new aura::ScreenOzone(); + 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..e45fe4c9df0 --- /dev/null +++ b/chromium/ui/views/widget/desktop_aura/desktop_screen_ozone.h @@ -0,0 +1,28 @@ +// Copyright 2020 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/aura/screen_ozone.h" +#include "ui/views/views_export.h" + +namespace views { + +class VIEWS_EXPORT DesktopScreenOzone : public aura::ScreenOzone { + public: + DesktopScreenOzone(); + DesktopScreenOzone(const DesktopScreenOzone&) = delete; + DesktopScreenOzone& operator=(const DesktopScreenOzone&) = delete; + ~DesktopScreenOzone() override; + + private: + // ui::aura::ScreenOzone: + gfx::NativeWindow GetNativeWindowFromAcceleratedWidget( + gfx::AcceleratedWidget widget) const override; +}; + +} // namespace views + +#endif // UI_VIEWS_WIDGET_DESKTOP_AURA_DESKTOP_SCREEN_OZONE_H_ diff --git a/chromium/ui/views/widget/desktop_aura/desktop_screen_win.cc b/chromium/ui/views/widget/desktop_aura/desktop_screen_win.cc index 70553b153c4..963e7053e7a 100644 --- a/chromium/ui/views/widget/desktop_aura/desktop_screen_win.cc +++ b/chromium/ui/views/widget/desktop_aura/desktop_screen_win.cc @@ -4,40 +4,26 @@ #include "ui/views/widget/desktop_aura/desktop_screen_win.h" -#include "base/logging.h" #include "ui/aura/window.h" -#include "ui/aura/window_event_dispatcher.h" #include "ui/aura/window_tree_host.h" -#include "ui/display/display.h" #include "ui/views/widget/desktop_aura/desktop_screen.h" #include "ui/views/widget/desktop_aura/desktop_window_tree_host_win.h" namespace views { -//////////////////////////////////////////////////////////////////////////////// -// DesktopScreenWin, public: - DesktopScreenWin::DesktopScreenWin() = default; DesktopScreenWin::~DesktopScreenWin() = default; -//////////////////////////////////////////////////////////////////////////////// -// DesktopScreenWin, display::win::ScreenWin implementation: - -display::Display DesktopScreenWin::GetDisplayMatching( - const gfx::Rect& match_rect) const { - return GetDisplayNearestPoint(match_rect.CenterPoint()); -} - -HWND DesktopScreenWin::GetHWNDFromNativeView(gfx::NativeView window) const { +HWND DesktopScreenWin::GetHWNDFromNativeWindow(gfx::NativeWindow window) const { aura::WindowTreeHost* host = window->GetHost(); return host ? host->GetAcceleratedWidget() : nullptr; } gfx::NativeWindow DesktopScreenWin::GetNativeWindowFromHWND(HWND hwnd) const { - return (::IsWindow(hwnd)) + return ::IsWindow(hwnd) ? DesktopWindowTreeHostWin::GetContentWindowForHWND(hwnd) - : nullptr; + : gfx::kNullNativeWindow; } //////////////////////////////////////////////////////////////////////////////// diff --git a/chromium/ui/views/widget/desktop_aura/desktop_screen_win.h b/chromium/ui/views/widget/desktop_aura/desktop_screen_win.h index e17fe7611d7..97442c424d8 100644 --- a/chromium/ui/views/widget/desktop_aura/desktop_screen_win.h +++ b/chromium/ui/views/widget/desktop_aura/desktop_screen_win.h @@ -5,7 +5,6 @@ #ifndef UI_VIEWS_WIDGET_DESKTOP_AURA_DESKTOP_SCREEN_WIN_H_ #define UI_VIEWS_WIDGET_DESKTOP_AURA_DESKTOP_SCREEN_WIN_H_ -#include "base/macros.h" #include "ui/display/win/screen_win.h" #include "ui/views/views_export.h" @@ -14,16 +13,14 @@ namespace views { class VIEWS_EXPORT DesktopScreenWin : public display::win::ScreenWin { public: DesktopScreenWin(); + DesktopScreenWin(const DesktopScreenWin&) = delete; + DesktopScreenWin& operator=(const DesktopScreenWin&) = delete; ~DesktopScreenWin() override; private: - // Overridden from display::win::ScreenWin: - display::Display GetDisplayMatching( - const gfx::Rect& match_rect) const override; - HWND GetHWNDFromNativeView(gfx::NativeView window) const override; + // display::win::ScreenWin: + HWND GetHWNDFromNativeWindow(gfx::NativeWindow window) const override; gfx::NativeWindow GetNativeWindowFromHWND(HWND hwnd) const override; - - DISALLOW_COPY_AND_ASSIGN(DesktopScreenWin); }; } // namespace views 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 0ffdc450043..4f4a5d1c395 100644 --- a/chromium/ui/views/widget/desktop_aura/desktop_screen_x11.cc +++ b/chromium/ui/views/widget/desktop_aura/desktop_screen_x11.cc @@ -21,9 +21,9 @@ #include "ui/gfx/geometry/dip_util.h" #include "ui/gfx/native_widget_types.h" #include "ui/gfx/switches.h" +#include "ui/platform_window/x11/x11_topmost_window_finder.h" #include "ui/views/widget/desktop_aura/desktop_screen.h" #include "ui/views/widget/desktop_aura/desktop_window_tree_host_x11.h" -#include "ui/views/widget/desktop_aura/x11_topmost_window_finder.h" namespace views { @@ -60,8 +60,29 @@ bool DesktopScreenX11::IsWindowUnderCursor(gfx::NativeWindow window) { gfx::NativeWindow DesktopScreenX11::GetWindowAtScreenPoint( const gfx::Point& point) { - return X11TopmostWindowFinder().FindLocalProcessWindowAt( - gfx::ConvertPointToPixel(GetXDisplayScaleFactor(), point), {}); + auto accelerated_widget = + ui::X11TopmostWindowFinder().FindLocalProcessWindowAt( + gfx::ConvertPointToPixel(GetXDisplayScaleFactor(), point), {}); + return accelerated_widget + ? views::DesktopWindowTreeHostPlatform::GetContentWindowForWidget( + static_cast<gfx::AcceleratedWidget>(accelerated_widget)) + : nullptr; +} + +gfx::NativeWindow DesktopScreenX11::GetLocalProcessWindowAtPoint( + const gfx::Point& point, + const std::set<gfx::NativeWindow>& ignore) { + std::set<gfx::AcceleratedWidget> ignore_widgets; + for (auto* const window : ignore) + ignore_widgets.emplace(window->GetHost()->GetAcceleratedWidget()); + auto accelerated_widget = + ui::X11TopmostWindowFinder().FindLocalProcessWindowAt( + gfx::ConvertPointToPixel(GetXDisplayScaleFactor(), point), + ignore_widgets); + return accelerated_widget + ? views::DesktopWindowTreeHostPlatform::GetContentWindowForWidget( + static_cast<gfx::AcceleratedWidget>(accelerated_widget)) + : nullptr; } int DesktopScreenX11::GetNumDisplays() const { diff --git a/chromium/ui/views/widget/desktop_aura/desktop_screen_x11.h b/chromium/ui/views/widget/desktop_aura/desktop_screen_x11.h index dcf3e0f725e..b391f41caf2 100644 --- a/chromium/ui/views/widget/desktop_aura/desktop_screen_x11.h +++ b/chromium/ui/views/widget/desktop_aura/desktop_screen_x11.h @@ -40,6 +40,9 @@ class VIEWS_EXPORT DesktopScreenX11 : public display::Screen, gfx::Point GetCursorScreenPoint() override; bool IsWindowUnderCursor(gfx::NativeWindow window) override; gfx::NativeWindow GetWindowAtScreenPoint(const gfx::Point& point) override; + gfx::NativeWindow GetLocalProcessWindowAtPoint( + const gfx::Point& point, + const std::set<gfx::NativeWindow>& ignore) override; int GetNumDisplays() const override; const std::vector<display::Display>& GetAllDisplays() const override; display::Display GetDisplayNearestWindow( diff --git a/chromium/ui/views/widget/desktop_aura/desktop_window_tree_host_linux.cc b/chromium/ui/views/widget/desktop_aura/desktop_window_tree_host_linux.cc index 91cfaf77bb3..4168fa1a51f 100644 --- a/chromium/ui/views/widget/desktop_aura/desktop_window_tree_host_linux.cc +++ b/chromium/ui/views/widget/desktop_aura/desktop_window_tree_host_linux.cc @@ -29,17 +29,11 @@ #include "ui/accessibility/platform/atk_util_auralinux.h" #endif -DEFINE_UI_CLASS_PROPERTY_TYPE(views::DesktopWindowTreeHostLinux*) - namespace views { std::list<gfx::AcceleratedWidget>* DesktopWindowTreeHostLinux::open_windows_ = nullptr; -DEFINE_UI_CLASS_PROPERTY_KEY(DesktopWindowTreeHostLinux*, - kHostForRootWindow, - nullptr) - namespace { class SwapWithNewSizeObserverHelper : public ui::CompositorObserver { @@ -82,30 +76,13 @@ DesktopWindowTreeHostLinux::DesktopWindowTreeHostLinux( : DesktopWindowTreeHostPlatform(native_widget_delegate, desktop_native_widget_aura) {} -DesktopWindowTreeHostLinux::~DesktopWindowTreeHostLinux() { - window()->ClearProperty(kHostForRootWindow); -} - -// static -aura::Window* DesktopWindowTreeHostLinux::GetContentWindowForWidget( - gfx::AcceleratedWidget widget) { - auto* host = DesktopWindowTreeHostLinux::GetHostForWidget(widget); - return host ? host->GetContentWindow() : nullptr; -} - -// static -DesktopWindowTreeHostLinux* DesktopWindowTreeHostLinux::GetHostForWidget( - gfx::AcceleratedWidget widget) { - aura::WindowTreeHost* host = - aura::WindowTreeHost::GetForAcceleratedWidget(widget); - return host ? host->window()->GetProperty(kHostForRootWindow) : nullptr; -} +DesktopWindowTreeHostLinux::~DesktopWindowTreeHostLinux() = default; // static std::vector<aura::Window*> DesktopWindowTreeHostLinux::GetAllOpenWindows() { std::vector<aura::Window*> windows(open_windows().size()); std::transform(open_windows().begin(), open_windows().end(), windows.begin(), - GetContentWindowForWidget); + DesktopWindowTreeHostPlatform::GetContentWindowForWidget); return windows; } @@ -116,7 +93,7 @@ void DesktopWindowTreeHostLinux::CleanUpWindowList( return; while (!open_windows_->empty()) { gfx::AcceleratedWidget widget = open_windows_->front(); - func(GetContentWindowForWidget(widget)); + func(DesktopWindowTreeHostPlatform::GetContentWindowForWidget(widget)); if (!open_windows_->empty() && open_windows_->front() == widget) open_windows_->erase(open_windows_->begin()); } @@ -175,8 +152,6 @@ void DesktopWindowTreeHostLinux::Init(const Widget::InitParams& params) { void DesktopWindowTreeHostLinux::OnNativeWidgetCreated( const Widget::InitParams& params) { - window()->SetProperty(kHostForRootWindow, this); - CreateNonClientEventFilter(); DesktopWindowTreeHostPlatform::OnNativeWidgetCreated(params); } @@ -200,6 +175,15 @@ void DesktopWindowTreeHostLinux::InitModalType(ui::ModalType modal_type) { } } +Widget::MoveLoopResult DesktopWindowTreeHostLinux::RunMoveLoop( + const gfx::Vector2d& drag_offset, + Widget::MoveLoopSource source, + Widget::MoveLoopEscapeBehavior escape_behavior) { + GetContentWindow()->SetCapture(); + return DesktopWindowTreeHostPlatform::RunMoveLoop(drag_offset, source, + escape_behavior); +} + void DesktopWindowTreeHostLinux::OnDisplayMetricsChanged( const display::Display& display, uint32_t changed_metrics) { @@ -310,7 +294,7 @@ bool DesktopWindowTreeHostLinux::OnAtkKeyEvent(AtkKeyEventStruct* atk_event) { } #endif -bool DesktopWindowTreeHostLinux::IsOverrideRedirect() const { +bool DesktopWindowTreeHostLinux::IsOverrideRedirect(bool is_tiling_wm) const { // BrowserDesktopWindowTreeHostLinux implements this for browser windows. return false; } diff --git a/chromium/ui/views/widget/desktop_aura/desktop_window_tree_host_linux.h b/chromium/ui/views/widget/desktop_aura/desktop_window_tree_host_linux.h index 1351c2a66c4..79bef5fe949 100644 --- a/chromium/ui/views/widget/desktop_aura/desktop_window_tree_host_linux.h +++ b/chromium/ui/views/widget/desktop_aura/desktop_window_tree_host_linux.h @@ -43,14 +43,6 @@ class VIEWS_EXPORT DesktopWindowTreeHostLinux DesktopNativeWidgetAura* desktop_native_widget_aura); ~DesktopWindowTreeHostLinux() override; - // A way of converting a |widget| into the content_window() - // of the associated DesktopNativeWidgetAura. - static aura::Window* GetContentWindowForWidget(gfx::AcceleratedWidget widget); - - // A way of converting a |widget| into this object. - static DesktopWindowTreeHostLinux* GetHostForWidget( - gfx::AcceleratedWidget widget); - // Get all open top-level windows. This includes windows that may not be // visible. This list is sorted in their stacking order, i.e. the first window // is the topmost window. @@ -82,6 +74,10 @@ class VIEWS_EXPORT DesktopWindowTreeHostLinux void OnNativeWidgetCreated(const Widget::InitParams& params) override; base::flat_map<std::string, std::string> GetKeyboardLayoutMap() override; void InitModalType(ui::ModalType modal_type) override; + Widget::MoveLoopResult RunMoveLoop( + const gfx::Vector2d& drag_offset, + Widget::MoveLoopSource source, + Widget::MoveLoopEscapeBehavior escape_behavior) override; // PlatformWindowDelegate: void DispatchEvent(ui::Event* event) override; @@ -117,7 +113,7 @@ class VIEWS_EXPORT DesktopWindowTreeHostLinux #if BUILDFLAG(USE_ATK) bool OnAtkKeyEvent(AtkKeyEventStruct* atk_key_event) override; #endif - bool IsOverrideRedirect() const override; + bool IsOverrideRedirect(bool is_tiling_wm) const override; // Enables event listening after closing |dialog|. void EnableEventListening(); diff --git a/chromium/ui/views/widget/desktop_aura/desktop_window_tree_host_linux_interactive_uitest.cc b/chromium/ui/views/widget/desktop_aura/desktop_window_tree_host_linux_interactive_uitest.cc index 0b2ab7e4538..cb61f3c0b4d 100644 --- a/chromium/ui/views/widget/desktop_aura/desktop_window_tree_host_linux_interactive_uitest.cc +++ b/chromium/ui/views/widget/desktop_aura/desktop_window_tree_host_linux_interactive_uitest.cc @@ -9,7 +9,7 @@ #include "ui/base/hit_test.h" #include "ui/platform_window/platform_window.h" #include "ui/platform_window/platform_window_handler/wm_move_resize_handler.h" -#include "ui/views/test/views_interactive_ui_test_base.h" +#include "ui/views/test/widget_test.h" #include "ui/views/widget/desktop_aura/desktop_native_widget_aura.h" #include "ui/views/widget/desktop_aura/window_event_filter_linux.h" #include "ui/views/widget/widget_delegate.h" @@ -180,7 +180,8 @@ class TestDesktopWindowTreeHostLinux : public DesktopWindowTreeHostLinux { } // namespace -class DesktopWindowTreeHostLinuxTest : public ViewsInteractiveUITestBase { +class DesktopWindowTreeHostLinuxTest + : public test::DesktopWidgetTestInteractive { public: DesktopWindowTreeHostLinuxTest() = default; ~DesktopWindowTreeHostLinuxTest() override = default; 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 f3a3175c6c3..aaf1b482f71 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 @@ -21,6 +21,7 @@ #include "ui/gfx/geometry/dip_util.h" #include "ui/platform_window/extensions/workspace_extension.h" #include "ui/platform_window/platform_window.h" +#include "ui/platform_window/platform_window_handler/wm_move_loop_handler.h" #include "ui/platform_window/platform_window_init_properties.h" #include "ui/views/corewm/tooltip_aura.h" #include "ui/views/widget/desktop_aura/desktop_drag_drop_client_ozone.h" @@ -28,9 +29,16 @@ #include "ui/views/widget/widget_aura_utils.h" #include "ui/views/window/native_frame_view.h" #include "ui/wm/core/window_util.h" +#include "ui/wm/public/window_move_client.h" + +DEFINE_UI_CLASS_PROPERTY_TYPE(views::DesktopWindowTreeHostPlatform*) namespace views { +DEFINE_UI_CLASS_PROPERTY_KEY(DesktopWindowTreeHostPlatform*, + kHostForRootWindow, + nullptr) + namespace { bool DetermineInactivity(ui::WindowShowState show_state) { @@ -108,14 +116,31 @@ DesktopWindowTreeHostPlatform::DesktopWindowTreeHostPlatform( internal::NativeWidgetDelegate* native_widget_delegate, DesktopNativeWidgetAura* desktop_native_widget_aura) : native_widget_delegate_(native_widget_delegate), - desktop_native_widget_aura_(desktop_native_widget_aura) {} + desktop_native_widget_aura_(desktop_native_widget_aura), + window_move_client_(this) {} DesktopWindowTreeHostPlatform::~DesktopWindowTreeHostPlatform() { + window()->ClearProperty(kHostForRootWindow); DCHECK(!platform_window()) << "The host must be closed before destroying it."; desktop_native_widget_aura_->OnDesktopWindowTreeHostDestroyed(this); DestroyDispatcher(); } +// static +aura::Window* DesktopWindowTreeHostPlatform::GetContentWindowForWidget( + gfx::AcceleratedWidget widget) { + auto* host = DesktopWindowTreeHostPlatform::GetHostForWidget(widget); + return host ? host->GetContentWindow() : nullptr; +} + +// static +DesktopWindowTreeHostPlatform* DesktopWindowTreeHostPlatform::GetHostForWidget( + gfx::AcceleratedWidget widget) { + aura::WindowTreeHost* host = + aura::WindowTreeHost::GetForAcceleratedWidget(widget); + return host ? host->window()->GetProperty(kHostForRootWindow) : nullptr; +} + aura::Window* DesktopWindowTreeHostPlatform::GetContentWindow() { return desktop_native_widget_aura_->content_window(); } @@ -160,6 +185,11 @@ void DesktopWindowTreeHostPlatform::Init(const Widget::InitParams& params) { void DesktopWindowTreeHostPlatform::OnNativeWidgetCreated( const Widget::InitParams& params) { + window()->SetProperty(kHostForRootWindow, this); + // This reroutes RunMoveLoop requests to the DesktopWindowTreeHostPlatform. + // The availability of this feature depends on a platform (PlatformWindow) + // that implements RunMoveLoop. + wm::SetWindowMoveClient(window(), &window_move_client_); platform_window()->SetUseNativeFrame(params.type == Widget::InitParams::TYPE_WINDOW && !params.remove_standard_frame); @@ -504,14 +534,16 @@ Widget::MoveLoopResult DesktopWindowTreeHostPlatform::RunMoveLoop( const gfx::Vector2d& drag_offset, Widget::MoveLoopSource source, Widget::MoveLoopEscapeBehavior escape_behavior) { - // TODO(crbug.com/896640): needs PlatformWindow support. - NOTIMPLEMENTED_LOG_ONCE(); + auto* move_loop_handler = ui::GetWmMoveLoopHandler(*platform_window()); + if (move_loop_handler && move_loop_handler->RunMoveLoop(drag_offset)) + return Widget::MOVE_LOOP_SUCCESSFUL; return Widget::MOVE_LOOP_CANCELED; } void DesktopWindowTreeHostPlatform::EndMoveLoop() { - // TODO(crbug.com/896640): needs PlatformWindow support. - NOTIMPLEMENTED_LOG_ONCE(); + auto* move_loop_handler = ui::GetWmMoveLoopHandler(*platform_window()); + if (move_loop_handler) + move_loop_handler->EndMoveLoop(); } void DesktopWindowTreeHostPlatform::SetVisibilityChangedAnimationsEnabled( @@ -646,6 +678,7 @@ void DesktopWindowTreeHostPlatform::HideImpl() { } void DesktopWindowTreeHostPlatform::OnClosed() { + wm::SetWindowMoveClient(window(), nullptr); SetPlatformWindow(nullptr); desktop_native_widget_aura_->OnHostClosed(); } 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 24a5fa974dd..75860eaabf1 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 @@ -16,6 +16,7 @@ #include "ui/platform_window/extensions/workspace_extension_delegate.h" #include "ui/views/views_export.h" #include "ui/views/widget/desktop_aura/desktop_window_tree_host.h" +#include "ui/views/widget/desktop_aura/window_move_client_platform.h" namespace views { @@ -29,6 +30,14 @@ class VIEWS_EXPORT DesktopWindowTreeHostPlatform DesktopNativeWidgetAura* desktop_native_widget_aura); ~DesktopWindowTreeHostPlatform() override; + // A way of converting a |widget| into the content_window() + // of the associated DesktopNativeWidgetAura. + static aura::Window* GetContentWindowForWidget(gfx::AcceleratedWidget widget); + + // A way of converting a |widget| into this object. + static DesktopWindowTreeHostPlatform* GetHostForWidget( + gfx::AcceleratedWidget widget); + // Accessor for DesktopNativeWidgetAura::content_window(). aura::Window* GetContentWindow(); const aura::Window* GetContentWindow() const; @@ -161,6 +170,9 @@ class VIEWS_EXPORT DesktopWindowTreeHostPlatform // normal state. ui::PlatformWindowState old_state_ = ui::PlatformWindowState::kUnknown; + // Used for tab dragging in move loop requests. + WindowMoveClientPlatform window_move_client_; + base::WeakPtrFactory<DesktopWindowTreeHostPlatform> close_widget_factory_{ this}; 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 6f576d95707..6051aadee62 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 @@ -12,6 +12,7 @@ #include "base/containers/flat_set.h" #include "base/memory/ptr_util.h" #include "base/metrics/histogram_macros.h" +#include "base/trace_event/trace_event.h" #include "base/win/win_util.h" #include "base/win/windows_version.h" #include "third_party/skia/include/core/SkPath.h" @@ -622,6 +623,8 @@ DesktopWindowTreeHostWin::GetKeyboardLayoutMap() { } void DesktopWindowTreeHostWin::SetCursorNative(gfx::NativeCursor cursor) { + TRACE_EVENT1("ui,input", "DesktopWindowTreeHostWin::SetCursorNative", + "cursor", cursor.type()); ui::CursorLoaderWin cursor_loader; cursor_loader.SetPlatformCursor(&cursor); @@ -922,8 +925,20 @@ bool DesktopWindowTreeHostWin::HandleMouseEvent(ui::MouseEvent* event) { // marked occluded, or getting stuck in the occluded state. Event can cause // this object to be deleted so check occlusion state before we do anything // with the event. - if (GetNativeWindowOcclusionState() == aura::Window::OcclusionState::OCCLUDED) - UMA_HISTOGRAM_BOOLEAN("OccludedWindowMouseEvents", true); + // This stat tries to detect the user moving the mouse over a window falsely + // determined to be occluded, so ignore mouse events that have the same + // location as the first event, and exit events. + if (GetNativeWindowOcclusionState() == + aura::Window::OcclusionState::OCCLUDED) { + if (occluded_window_mouse_event_loc_ != gfx::Point() && + event->location() != occluded_window_mouse_event_loc_ && + event->type() != ui::ET_MOUSE_EXITED) { + UMA_HISTOGRAM_BOOLEAN("OccludedWindowMouseEvents", true); + } + occluded_window_mouse_event_loc_ = event->location(); + } else { + occluded_window_mouse_event_loc_ = gfx::Point(); + } SendEventToSink(event); return event->handled(); 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 4b217541512..75ca9e99f2c 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 @@ -307,6 +307,11 @@ class VIEWS_EXPORT DesktopWindowTreeHostWin // become activated. bool wants_mouse_events_when_inactive_ = false; + // The location of the most recent mouse event on an occluded window. This is + // used to generate the OccludedWindowMouseEvents stat and can be removed + // when that stat is no longer tracked. + gfx::Point occluded_window_mouse_event_loc_; + // The z-order level of the window; the window exhibits "always on top" // behavior if > 0. ui::ZOrderLevel z_order_ = ui::ZOrderLevel::kNormal; 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 2c94941cf27..44e9eef3492 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 @@ -31,7 +31,7 @@ #include "ui/aura/window_event_dispatcher.h" #include "ui/base/buildflags.h" #include "ui/base/class_property.h" -#include "ui/base/dragdrop/os_exchange_data_provider_aurax11.h" +#include "ui/base/dragdrop/os_exchange_data_provider_x11.h" #include "ui/base/hit_test.h" #include "ui/base/ime/input_method.h" #include "ui/base/layout.h" @@ -57,7 +57,6 @@ #include "ui/views/widget/desktop_aura/desktop_drag_drop_client_aurax11.h" #include "ui/views/widget/desktop_aura/desktop_native_cursor_manager.h" #include "ui/views/widget/desktop_aura/desktop_native_widget_aura.h" -#include "ui/views/widget/desktop_aura/x11_desktop_window_move_client.h" #include "ui/views/window/native_frame_view.h" #include "ui/wm/core/compound_event_filter.h" #include "ui/wm/core/window_util.h" @@ -72,12 +71,7 @@ DesktopWindowTreeHostX11::DesktopWindowTreeHostX11( : DesktopWindowTreeHostLinux(native_widget_delegate, desktop_native_widget_aura) {} -DesktopWindowTreeHostX11::~DesktopWindowTreeHostX11() { - wm::SetWindowMoveClient(window(), nullptr); - - // ~DWTHPlatform notifies the DestkopNativeWidgetAura about destruction and - // also destroyes the dispatcher. -} +DesktopWindowTreeHostX11::~DesktopWindowTreeHostX11() = default; //////////////////////////////////////////////////////////////////////////////// // DesktopWindowTreeHostX11, DesktopWindowTreeHost implementation: @@ -94,14 +88,6 @@ void DesktopWindowTreeHostX11::Init(const Widget::InitParams& params) { static_cast<ui::X11Window*>(platform_window())->SetXEventDelegate(this); } -void DesktopWindowTreeHostX11::OnNativeWidgetCreated( - const Widget::InitParams& params) { - x11_window_move_client_ = std::make_unique<X11DesktopWindowMoveClient>(); - wm::SetWindowMoveClient(window(), x11_window_move_client_.get()); - - DesktopWindowTreeHostLinux::OnNativeWidgetCreated(params); -} - std::unique_ptr<aura::client::DragDropClient> DesktopWindowTreeHostX11::CreateDragDropClient( DesktopNativeCursorManager* cursor_manager) { @@ -112,25 +98,6 @@ DesktopWindowTreeHostX11::CreateDragDropClient( return base::WrapUnique(drag_drop_client_); } -Widget::MoveLoopResult DesktopWindowTreeHostX11::RunMoveLoop( - const gfx::Vector2d& drag_offset, - Widget::MoveLoopSource source, - Widget::MoveLoopEscapeBehavior escape_behavior) { - wm::WindowMoveSource window_move_source = - source == Widget::MoveLoopSource::kMouse ? wm::WINDOW_MOVE_SOURCE_MOUSE - : wm::WINDOW_MOVE_SOURCE_TOUCH; - if (x11_window_move_client_->RunMoveLoop(GetContentWindow(), drag_offset, - window_move_source) == - wm::MOVE_SUCCESSFUL) - return Widget::MOVE_LOOP_SUCCESSFUL; - - return Widget::MOVE_LOOP_CANCELED; -} - -void DesktopWindowTreeHostX11::EndMoveLoop() { - x11_window_move_client_->EndMoveLoop(); -} - //////////////////////////////////////////////////////////////////////////////// // DesktopWindowTreeHostX11 implementation: @@ -143,21 +110,7 @@ void DesktopWindowTreeHostX11::OnXWindowSelectionEvent(XEvent* xev) { void DesktopWindowTreeHostX11::OnXWindowDragDropEvent(XEvent* xev) { DCHECK(xev); DCHECK(drag_drop_client_); - - ::Atom message_type = xev->xclient.message_type; - if (message_type == gfx::GetAtom("XdndEnter")) { - drag_drop_client_->OnXdndEnter(xev->xclient); - } else if (message_type == gfx::GetAtom("XdndLeave")) { - drag_drop_client_->OnXdndLeave(xev->xclient); - } else if (message_type == gfx::GetAtom("XdndPosition")) { - drag_drop_client_->OnXdndPosition(xev->xclient); - } else if (message_type == gfx::GetAtom("XdndStatus")) { - drag_drop_client_->OnXdndStatus(xev->xclient); - } else if (message_type == gfx::GetAtom("XdndFinished")) { - drag_drop_client_->OnXdndFinished(xev->xclient); - } else if (message_type == gfx::GetAtom("XdndDrop")) { - drag_drop_client_->OnXdndDrop(xev->xclient); - } + drag_drop_client_->HandleXdndEvent(xev->xclient); } const ui::XWindow* DesktopWindowTreeHostX11::GetXWindow() const { 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 53da309c14d..8cd6b80ee1e 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 @@ -21,7 +21,6 @@ class X11Window; namespace views { class DesktopDragDropClientAuraX11; -class X11DesktopWindowMoveClient; class VIEWS_EXPORT DesktopWindowTreeHostX11 : public DesktopWindowTreeHostLinux, public ui::XEventDelegate { @@ -34,14 +33,8 @@ class VIEWS_EXPORT DesktopWindowTreeHostX11 : public DesktopWindowTreeHostLinux, protected: // Overridden from DesktopWindowTreeHost: void Init(const Widget::InitParams& params) override; - void OnNativeWidgetCreated(const Widget::InitParams& params) override; std::unique_ptr<aura::client::DragDropClient> CreateDragDropClient( DesktopNativeCursorManager* cursor_manager) override; - Widget::MoveLoopResult RunMoveLoop( - const gfx::Vector2d& drag_offset, - Widget::MoveLoopSource source, - Widget::MoveLoopEscapeBehavior escape_behavior) override; - void EndMoveLoop() override; private: friend class DesktopWindowTreeHostX11HighDPITest; @@ -58,8 +51,6 @@ class VIEWS_EXPORT DesktopWindowTreeHostX11 : public DesktopWindowTreeHostLinux, DesktopDragDropClientAuraX11* drag_drop_client_ = nullptr; - std::unique_ptr<X11DesktopWindowMoveClient> x11_window_move_client_; - DISALLOW_COPY_AND_ASSIGN(DesktopWindowTreeHostX11); }; diff --git a/chromium/ui/views/widget/desktop_aura/desktop_window_tree_host_x11_interactive_uitest.cc b/chromium/ui/views/widget/desktop_aura/desktop_window_tree_host_x11_interactive_uitest.cc index 7135ad984e3..c63bfe26350 100644 --- a/chromium/ui/views/widget/desktop_aura/desktop_window_tree_host_x11_interactive_uitest.cc +++ b/chromium/ui/views/widget/desktop_aura/desktop_window_tree_host_x11_interactive_uitest.cc @@ -11,6 +11,7 @@ #include "ui/aura/window.h" #include "ui/aura/window_tree_host.h" #include "ui/base/ime/input_method.h" +#include "ui/base/x/test/x11_property_change_waiter.h" #include "ui/base/x/x11_util.h" #include "ui/events/event_handler.h" #include "ui/events/platform/x11/x11_event_source.h" @@ -19,8 +20,7 @@ #include "ui/gfx/x/x11.h" #include "ui/gfx/x/x11_atom_cache.h" #include "ui/views/controls/textfield/textfield.h" -#include "ui/views/test/views_interactive_ui_test_base.h" -#include "ui/views/test/x11_property_change_waiter.h" +#include "ui/views/test/widget_test.h" #include "ui/views/widget/desktop_aura/desktop_native_widget_aura.h" namespace views { @@ -28,16 +28,17 @@ namespace views { namespace { // Blocks till |window| gets activated. -class ActivationWaiter : public X11PropertyChangeWaiter { +class ActivationWaiter : public ui::X11PropertyChangeWaiter { public: explicit ActivationWaiter(XID window) - : X11PropertyChangeWaiter(ui::GetX11RootWindow(), "_NET_ACTIVE_WINDOW"), + : ui::X11PropertyChangeWaiter(ui::GetX11RootWindow(), + "_NET_ACTIVE_WINDOW"), window_(window) {} ~ActivationWaiter() override = default; private: - // X11PropertyChangeWaiter: + // ui::X11PropertyChangeWaiter: bool ShouldKeepOnWaiting(XEvent* event) override { XID xid = 0; ui::GetXIDProperty(ui::GetX11RootWindow(), "_NET_ACTIVE_WINDOW", &xid); @@ -108,23 +109,22 @@ void DispatchMouseMotionEvent(DesktopWindowTreeHostX11* desktop_host, } // namespace -class DesktopWindowTreeHostX11Test : public ViewsInteractiveUITestBase { +class DesktopWindowTreeHostX11Test : public test::DesktopWidgetTestInteractive { public: DesktopWindowTreeHostX11Test() = default; ~DesktopWindowTreeHostX11Test() override = default; - // testing::Test + // DesktopWidgetTestInteractive void SetUp() override { - ViewsInteractiveUITestBase::SetUp(); - // Make X11 synchronous for our display connection. This does not force the // window manager to behave synchronously. XSynchronize(gfx::GetXDisplay(), x11::True); + DesktopWidgetTestInteractive::SetUp(); } void TearDown() override { XSynchronize(gfx::GetXDisplay(), x11::False); - ViewsInteractiveUITestBase::TearDown(); + DesktopWidgetTestInteractive::TearDown(); } private: @@ -235,6 +235,12 @@ TEST_F(DesktopWindowTreeHostX11Test, CaptureEventForwarding) { TEST_F(DesktopWindowTreeHostX11Test, InputMethodFocus) { std::unique_ptr<Widget> widget(CreateWidget(gfx::Rect(100, 100, 100, 100))); + + // Waiter should be created as early as possible so that PropertyNotify has + // time to be set before widget is activated. + ActivationWaiter waiter( + widget->GetNativeWindow()->GetHost()->GetAcceleratedWidget()); + std::unique_ptr<Textfield> textfield(new Textfield); textfield->SetBounds(0, 0, 200, 20); widget->GetRootView()->AddChildView(textfield.get()); @@ -247,11 +253,6 @@ TEST_F(DesktopWindowTreeHostX11Test, InputMethodFocus) { // EXPECT_EQ(ui::TEXT_INPUT_TYPE_NONE, // widget->GetInputMethod()->GetTextInputType()); - // Waiter should be created before widget->Activate is called. Otherwise, - // there is a race, and waiter might not be able to set property changes mask - // on time and miss the events. - ActivationWaiter waiter( - widget->GetNativeWindow()->GetHost()->GetAcceleratedWidget()); widget->Activate(); waiter.Wait(); diff --git a/chromium/ui/views/widget/desktop_aura/desktop_window_tree_host_x11_unittest.cc b/chromium/ui/views/widget/desktop_aura/desktop_window_tree_host_x11_unittest.cc index 88b625f1b59..1f4b3a5244c 100644 --- a/chromium/ui/views/widget/desktop_aura/desktop_window_tree_host_x11_unittest.cc +++ b/chromium/ui/views/widget/desktop_aura/desktop_window_tree_host_x11_unittest.cc @@ -16,6 +16,7 @@ #include "ui/aura/window.h" #include "ui/aura/window_tree_host.h" #include "ui/base/hit_test.h" +#include "ui/base/x/test/x11_property_change_waiter.h" #include "ui/base/x/x11_util.h" #include "ui/display/display_switches.h" #include "ui/events/devices/x11/touch_factory_x11.h" @@ -28,7 +29,6 @@ #include "ui/gfx/x/x11.h" #include "ui/gfx/x/x11_atom_cache.h" #include "ui/views/test/views_test_base.h" -#include "ui/views/test/x11_property_change_waiter.h" #include "ui/views/widget/desktop_aura/desktop_native_widget_aura.h" #include "ui/views/widget/desktop_aura/desktop_window_tree_host_x11.h" #include "ui/views/widget/widget_delegate.h" @@ -41,10 +41,10 @@ namespace { const int kPointerDeviceId = 1; // Blocks till the window state hint, |hint|, is set or unset. -class WMStateWaiter : public X11PropertyChangeWaiter { +class WMStateWaiter : public ui::X11PropertyChangeWaiter { public: WMStateWaiter(XID window, const char* hint, bool wait_till_set) - : X11PropertyChangeWaiter(window, "_NET_WM_STATE"), + : ui::X11PropertyChangeWaiter(window, "_NET_WM_STATE"), hint_(hint), wait_till_set_(wait_till_set) {} diff --git a/chromium/ui/views/widget/desktop_aura/window_event_filter_linux.cc b/chromium/ui/views/widget/desktop_aura/window_event_filter_linux.cc index d69da05aa30..915fa77545d 100644 --- a/chromium/ui/views/widget/desktop_aura/window_event_filter_linux.cc +++ b/chromium/ui/views/widget/desktop_aura/window_event_filter_linux.cc @@ -112,6 +112,10 @@ void WindowEventFilterLinux::OnClickedCaption(ui::MouseEvent* event, if (!view || !view->context_menu_controller()) break; gfx::Point location(event->location()); + // Controller requires locations to be in DIP, while |this| receives the + // location in px. + desktop_window_tree_host_->GetRootTransform().TransformPointReverse( + &location); views::View::ConvertPointToScreen(view, &location); view->ShowContextMenu(location, ui::MENU_SOURCE_MOUSE); event->SetHandled(); diff --git a/chromium/ui/views/widget/desktop_aura/window_move_client_platform.cc b/chromium/ui/views/widget/desktop_aura/window_move_client_platform.cc new file mode 100644 index 00000000000..d561307dbca --- /dev/null +++ b/chromium/ui/views/widget/desktop_aura/window_move_client_platform.cc @@ -0,0 +1,37 @@ +// Copyright 2020 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/window_move_client_platform.h" + +#include "ui/views/widget/desktop_aura/desktop_window_tree_host_platform.h" + +namespace views { + +WindowMoveClientPlatform::WindowMoveClientPlatform( + DesktopWindowTreeHostPlatform* host) + : host_(host) {} + +WindowMoveClientPlatform::~WindowMoveClientPlatform() = default; + +wm::WindowMoveResult WindowMoveClientPlatform::RunMoveLoop( + aura::Window* source, + const gfx::Vector2d& drag_offset, + wm::WindowMoveSource move_source) { + DCHECK(host_->GetContentWindow()->Contains(source)); + auto move_loop_result = host_->RunMoveLoop( + drag_offset, + move_source == wm::WindowMoveSource::WINDOW_MOVE_SOURCE_MOUSE + ? Widget::MoveLoopSource::kMouse + : Widget::MoveLoopSource::kTouch, + Widget::MoveLoopEscapeBehavior::MOVE_LOOP_ESCAPE_BEHAVIOR_HIDE); + + return move_loop_result == Widget::MOVE_LOOP_SUCCESSFUL ? wm::MOVE_SUCCESSFUL + : wm::MOVE_CANCELED; +} + +void WindowMoveClientPlatform::EndMoveLoop() { + host_->EndMoveLoop(); +} + +} // namespace views diff --git a/chromium/ui/views/widget/desktop_aura/window_move_client_platform.h b/chromium/ui/views/widget/desktop_aura/window_move_client_platform.h new file mode 100644 index 00000000000..09d46399247 --- /dev/null +++ b/chromium/ui/views/widget/desktop_aura/window_move_client_platform.h @@ -0,0 +1,37 @@ +// Copyright 2020 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_WINDOW_MOVE_CLIENT_PLATFORM_H_ +#define UI_VIEWS_WIDGET_DESKTOP_AURA_WINDOW_MOVE_CLIENT_PLATFORM_H_ + +#include "ui/views/views_export.h" +#include "ui/wm/public/window_move_client.h" + +namespace views { + +class DesktopWindowTreeHostPlatform; + +// Reroutes move loop requests to DesktopWindowTreeHostPlatform. +class VIEWS_EXPORT WindowMoveClientPlatform : public wm::WindowMoveClient { + public: + explicit WindowMoveClientPlatform(DesktopWindowTreeHostPlatform* host); + WindowMoveClientPlatform(const WindowMoveClientPlatform& host) = delete; + WindowMoveClientPlatform& operator=(const WindowMoveClientPlatform& host) = + delete; + ~WindowMoveClientPlatform() override; + + // Overridden from wm::WindowMoveClient: + wm::WindowMoveResult RunMoveLoop(aura::Window* window, + const gfx::Vector2d& drag_offset, + wm::WindowMoveSource move_source) override; + void EndMoveLoop() override; + + private: + // The RunMoveLoop request is forwarded to this host. + DesktopWindowTreeHostPlatform* host_ = nullptr; +}; + +} // namespace views + +#endif // UI_VIEWS_WIDGET_DESKTOP_AURA_WINDOW_MOVE_CLIENT_PLATFORM_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 deleted file mode 100644 index 80b34d05334..00000000000 --- a/chromium/ui/views/widget/desktop_aura/x11_desktop_window_move_client.cc +++ /dev/null @@ -1,56 +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/widget/desktop_aura/x11_desktop_window_move_client.h" - -#include "base/run_loop.h" -#include "ui/aura/env.h" -#include "ui/aura/window.h" -#include "ui/aura/window_tree_host.h" -#include "ui/base/x/x11_util.h" -#include "ui/events/event.h" -#include "ui/gfx/x/x11.h" - -namespace views { - -X11DesktopWindowMoveClient::X11DesktopWindowMoveClient() = default; - -X11DesktopWindowMoveClient::~X11DesktopWindowMoveClient() = default; - -void X11DesktopWindowMoveClient::OnMouseMovement(const gfx::Point& screen_point, - int flags, - base::TimeTicks event_time) { - gfx::Point system_loc = screen_point - window_offset_; - host_->SetBoundsInPixels( - gfx::Rect(system_loc, host_->GetBoundsInPixels().size())); -} - -void X11DesktopWindowMoveClient::OnMouseReleased() { - EndMoveLoop(); -} - -void X11DesktopWindowMoveClient::OnMoveLoopEnded() { - host_ = nullptr; -} - -//////////////////////////////////////////////////////////////////////////////// -// DesktopWindowTreeHostLinux, wm::WindowMoveClient implementation: - -wm::WindowMoveResult X11DesktopWindowMoveClient::RunMoveLoop( - aura::Window* source, - const gfx::Vector2d& drag_offset, - wm::WindowMoveSource move_source) { - window_offset_ = drag_offset; - host_ = source->GetHost(); - - source->SetCapture(); - bool success = move_loop_.RunMoveLoop(source, host_->last_cursor()); - return success ? wm::MOVE_SUCCESSFUL : wm::MOVE_CANCELED; -} - -void X11DesktopWindowMoveClient::EndMoveLoop() { - move_loop_.EndMoveLoop(); -} - -} // namespace views 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 deleted file mode 100644 index 3305ca520bc..00000000000 --- a/chromium/ui/views/widget/desktop_aura/x11_desktop_window_move_client.h +++ /dev/null @@ -1,59 +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_WIDGET_DESKTOP_AURA_X11_DESKTOP_WINDOW_MOVE_CLIENT_H_ -#define UI_VIEWS_WIDGET_DESKTOP_AURA_X11_DESKTOP_WINDOW_MOVE_CLIENT_H_ - -#include "base/callback.h" -#include "base/compiler_specific.h" -#include "ui/gfx/geometry/point.h" -#include "ui/gfx/x/x11.h" -#include "ui/views/views_export.h" -#include "ui/views/widget/desktop_aura/x11_move_loop_delegate.h" -#include "ui/views/widget/desktop_aura/x11_whole_screen_move_loop.h" -#include "ui/wm/public/window_move_client.h" - -namespace aura { -class WindowTreeHost; -} - -namespace views { - -// When we're dragging tabs, we need to manually position our window. -class VIEWS_EXPORT X11DesktopWindowMoveClient - : public views::X11MoveLoopDelegate, - public wm::WindowMoveClient { - public: - X11DesktopWindowMoveClient(); - ~X11DesktopWindowMoveClient() override; - - // Overridden from X11WholeScreenMoveLoopDelegate: - void OnMouseMovement(const gfx::Point& screen_point, - int flags, - base::TimeTicks event_time) override; - void OnMouseReleased() override; - void OnMoveLoopEnded() override; - - // Overridden from wm::WindowMoveClient: - wm::WindowMoveResult RunMoveLoop(aura::Window* window, - const gfx::Vector2d& drag_offset, - wm::WindowMoveSource move_source) override; - void EndMoveLoop() override; - - private: - X11WholeScreenMoveLoop move_loop_{this}; - - // We need to keep track of this so we can actually move it when reacting to - // mouse events. - aura::WindowTreeHost* host_ = nullptr; - - // Our cursor offset from the top left window origin when the drag - // started. Used to calculate the window's new bounds relative to the current - // location of the cursor. - gfx::Vector2d window_offset_; -}; - -} // namespace views - -#endif // UI_VIEWS_WIDGET_DESKTOP_AURA_X11_DESKTOP_WINDOW_MOVE_CLIENT_H_ diff --git a/chromium/ui/views/widget/desktop_aura/x11_move_loop.h b/chromium/ui/views/widget/desktop_aura/x11_move_loop.h deleted file mode 100644 index b12cbecde9c..00000000000 --- a/chromium/ui/views/widget/desktop_aura/x11_move_loop.h +++ /dev/null @@ -1,34 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef UI_VIEWS_WIDGET_DESKTOP_AURA_X11_MOVE_LOOP_H_ -#define UI_VIEWS_WIDGET_DESKTOP_AURA_X11_MOVE_LOOP_H_ - -#include "ui/gfx/native_widget_types.h" -#include "ui/gfx/x/x11_types.h" - -namespace views { - -// Runs a nested run loop and grabs the mouse. This is used to implement -// dragging. -class X11MoveLoop { - public: - virtual ~X11MoveLoop() {} - - // Runs the nested run loop. While the mouse is grabbed, use |cursor| as - // the mouse cursor. Returns true if the move-loop is completed successfully. - // If the pointer-grab fails, or the move-loop is canceled by the user (e.g. - // by pressing escape), then returns false. - virtual bool RunMoveLoop(aura::Window* window, gfx::NativeCursor cursor) = 0; - - // Updates the cursor while the move loop is running. - virtual void UpdateCursor(gfx::NativeCursor cursor) = 0; - - // Ends the move loop that's currently in progress. - virtual void EndMoveLoop() = 0; -}; - -} // namespace views - -#endif // UI_VIEWS_WIDGET_DESKTOP_AURA_X11_MOVE_LOOP_H_ diff --git a/chromium/ui/views/widget/desktop_aura/x11_move_loop_delegate.h b/chromium/ui/views/widget/desktop_aura/x11_move_loop_delegate.h deleted file mode 100644 index b5eee9b7257..00000000000 --- a/chromium/ui/views/widget/desktop_aura/x11_move_loop_delegate.h +++ /dev/null @@ -1,32 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef UI_VIEWS_WIDGET_DESKTOP_AURA_X11_MOVE_LOOP_DELEGATE_H_ -#define UI_VIEWS_WIDGET_DESKTOP_AURA_X11_MOVE_LOOP_DELEGATE_H_ - -namespace gfx { -class Point; -} - -namespace views { - -// Receives mouse events while the X11MoveLoop is tracking a drag. -class X11MoveLoopDelegate { - public: - // Called when we receive a mouse move event. - virtual void OnMouseMovement(const gfx::Point& screen_point, - int flags, - base::TimeTicks event_time) = 0; - - // Called when the mouse button is released. - virtual void OnMouseReleased() = 0; - - // Called when the user has released the mouse button. The move loop will - // release the grab after this has been called. - virtual void OnMoveLoopEnded() = 0; -}; - -} // namespace views - -#endif // UI_VIEWS_WIDGET_DESKTOP_AURA_X11_MOVE_LOOP_DELEGATE_H_ diff --git a/chromium/ui/views/widget/desktop_aura/x11_topmost_window_finder.cc b/chromium/ui/views/widget/desktop_aura/x11_topmost_window_finder.cc deleted file mode 100644 index 9acc365e582..00000000000 --- a/chromium/ui/views/widget/desktop_aura/x11_topmost_window_finder.cc +++ /dev/null @@ -1,92 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "ui/views/widget/desktop_aura/x11_topmost_window_finder.h" - -#include <stddef.h> - -#include <vector> - -#include "ui/aura/client/screen_position_client.h" -#include "ui/aura/window.h" -#include "ui/views/widget/desktop_aura/desktop_window_tree_host_x11.h" - -namespace views { - -X11TopmostWindowFinder::X11TopmostWindowFinder() = default; - -X11TopmostWindowFinder::~X11TopmostWindowFinder() = default; - -aura::Window* X11TopmostWindowFinder::FindLocalProcessWindowAt( - const gfx::Point& screen_loc_in_pixels, - const std::set<aura::Window*>& ignore) { - screen_loc_in_pixels_ = screen_loc_in_pixels; - ignore_ = ignore; - - std::vector<aura::Window*> local_process_windows = - DesktopWindowTreeHostLinux::GetAllOpenWindows(); - if (std::none_of(local_process_windows.cbegin(), local_process_windows.cend(), - [this](auto* window) { - return ShouldStopIteratingAtLocalProcessWindow(window); - })) - return nullptr; - - ui::EnumerateTopLevelWindows(this); - return DesktopWindowTreeHostLinux::GetContentWindowForWidget( - static_cast<gfx::AcceleratedWidget>(toplevel_)); -} - -XID X11TopmostWindowFinder::FindWindowAt( - const gfx::Point& screen_loc_in_pixels) { - screen_loc_in_pixels_ = screen_loc_in_pixels; - ui::EnumerateTopLevelWindows(this); - return toplevel_; -} - -bool X11TopmostWindowFinder::ShouldStopIterating(XID xid) { - if (!ui::IsWindowVisible(xid)) - return false; - - auto* window = DesktopWindowTreeHostLinux::GetContentWindowForWidget( - static_cast<gfx::AcceleratedWidget>(xid)); - if (window) { - if (ShouldStopIteratingAtLocalProcessWindow(window)) { - toplevel_ = xid; - return true; - } - return false; - } - - if (ui::WindowContainsPoint(xid, screen_loc_in_pixels_)) { - toplevel_ = xid; - return true; - } - return false; -} - -bool X11TopmostWindowFinder::ShouldStopIteratingAtLocalProcessWindow( - aura::Window* window) { - if (ignore_.find(window) != ignore_.end()) - return false; - - // Currently |window|->IsVisible() always returns true. - // TODO(pkotwicz): Fix this. crbug.com/353038 - if (!window->IsVisible()) - return false; - - auto* host = DesktopWindowTreeHostLinux::GetHostForWidget( - window->GetHost()->GetAcceleratedWidget()); - if (!static_cast<DesktopWindowTreeHostX11*>(host) - ->GetXRootWindowOuterBounds() - .Contains(screen_loc_in_pixels_)) - return false; - - aura::client::ScreenPositionClient* screen_position_client = - aura::client::GetScreenPositionClient(window->GetRootWindow()); - gfx::Point window_loc(screen_loc_in_pixels_); - screen_position_client->ConvertPointFromScreen(window, &window_loc); - return host->ContainsPointInXRegion(window_loc); -} - -} // namespace views diff --git a/chromium/ui/views/widget/desktop_aura/x11_topmost_window_finder.h b/chromium/ui/views/widget/desktop_aura/x11_topmost_window_finder.h deleted file mode 100644 index 0d9e535153a..00000000000 --- a/chromium/ui/views/widget/desktop_aura/x11_topmost_window_finder.h +++ /dev/null @@ -1,56 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef UI_VIEWS_WIDGET_DESKTOP_AURA_X11_TOPMOST_WINDOW_FINDER_H_ -#define UI_VIEWS_WIDGET_DESKTOP_AURA_X11_TOPMOST_WINDOW_FINDER_H_ - -#include <set> - -#include "base/macros.h" -#include "ui/base/x/x11_topmost_window_finder.h" -#include "ui/base/x/x11_util.h" -#include "ui/gfx/geometry/point.h" -#include "ui/gfx/x/x11.h" -#include "ui/views/views_export.h" - -namespace aura { -class Window; -} - -namespace views { - -// Utility class for finding the topmost window at a given screen position. -class VIEWS_EXPORT X11TopmostWindowFinder : public ui::EnumerateWindowsDelegate, - public ui::XTopmostWindowFinder { - public: - X11TopmostWindowFinder(); - ~X11TopmostWindowFinder() override; - - // Returns the topmost window at |screen_loc_in_pixels|, ignoring the windows - // in |ignore|. Returns NULL if the topmost window at |screen_loc_in_pixels| - // does not belong to Chrome. - aura::Window* FindLocalProcessWindowAt(const gfx::Point& screen_loc_in_pixels, - const std::set<aura::Window*>& ignore); - - // Returns the topmost window at |screen_loc_in_pixels|. - XID FindWindowAt(const gfx::Point& screen_loc_in_pixels) override; - - private: - // ui::EnumerateWindowsDelegate: - bool ShouldStopIterating(XID xid) override; - - // Returns true if |window| does not not belong to |ignore|, is visible and - // contains |screen_loc_|. - bool ShouldStopIteratingAtLocalProcessWindow(aura::Window* window); - - gfx::Point screen_loc_in_pixels_; - std::set<aura::Window*> ignore_; - XID toplevel_ = x11::None; - - DISALLOW_COPY_AND_ASSIGN(X11TopmostWindowFinder); -}; - -} // namespace views - -#endif // UI_VIEWS_WIDGET_DESKTOP_AURA_X11_TOPMOST_WINDOW_FINDER_H_ diff --git a/chromium/ui/views/widget/desktop_aura/x11_topmost_window_finder_interactive_uitest.cc b/chromium/ui/views/widget/desktop_aura/x11_topmost_window_finder_interactive_uitest.cc index ce5c9ef430d..aa249625d3f 100644 --- a/chromium/ui/views/widget/desktop_aura/x11_topmost_window_finder_interactive_uitest.cc +++ b/chromium/ui/views/widget/desktop_aura/x11_topmost_window_finder_interactive_uitest.cc @@ -2,7 +2,7 @@ // 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/x11_topmost_window_finder.h" +#include "ui/platform_window/x11/x11_topmost_window_finder.h" #include <stddef.h> @@ -14,13 +14,14 @@ #include "third_party/skia/include/core/SkRect.h" #include "ui/aura/window.h" #include "ui/aura/window_tree_host.h" +#include "ui/base/x/test/x11_property_change_waiter.h" #include "ui/events/platform/x11/x11_event_source.h" #include "ui/gfx/x/x11.h" #include "ui/gfx/x/x11_atom_cache.h" #include "ui/gfx/x/x11_path.h" -#include "ui/views/test/views_interactive_ui_test_base.h" -#include "ui/views/test/x11_property_change_waiter.h" +#include "ui/views/test/widget_test.h" #include "ui/views/widget/desktop_aura/desktop_native_widget_aura.h" +#include "ui/views/widget/desktop_aura/desktop_window_tree_host_linux.h" #include "ui/views/widget/widget.h" namespace views { @@ -28,15 +29,15 @@ namespace views { namespace { // Waits till |window| is minimized. -class MinimizeWaiter : public X11PropertyChangeWaiter { +class MinimizeWaiter : public ui::X11PropertyChangeWaiter { public: explicit MinimizeWaiter(XID window) - : X11PropertyChangeWaiter(window, "_NET_WM_STATE") {} + : ui::X11PropertyChangeWaiter(window, "_NET_WM_STATE") {} ~MinimizeWaiter() override = default; private: - // X11PropertyChangeWaiter: + // ui::X11PropertyChangeWaiter: bool ShouldKeepOnWaiting(XEvent* event) override { std::vector<Atom> wm_states; if (ui::GetAtomArrayProperty(xwindow(), "_NET_WM_STATE", &wm_states)) { @@ -50,11 +51,11 @@ class MinimizeWaiter : public X11PropertyChangeWaiter { // Waits till |_NET_CLIENT_LIST_STACKING| is updated to include // |expected_windows|. -class StackingClientListWaiter : public X11PropertyChangeWaiter { +class StackingClientListWaiter : public ui::X11PropertyChangeWaiter { public: StackingClientListWaiter(XID* expected_windows, size_t count) - : X11PropertyChangeWaiter(ui::GetX11RootWindow(), - "_NET_CLIENT_LIST_STACKING"), + : ui::X11PropertyChangeWaiter(ui::GetX11RootWindow(), + "_NET_CLIENT_LIST_STACKING"), expected_windows_(expected_windows, expected_windows + count) {} ~StackingClientListWaiter() override = default; @@ -66,11 +67,11 @@ class StackingClientListWaiter : public X11PropertyChangeWaiter { if (!ShouldKeepOnWaiting(nullptr)) return; - X11PropertyChangeWaiter::Wait(); + ui::X11PropertyChangeWaiter::Wait(); } private: - // X11PropertyChangeWaiter: + // ui::X11PropertyChangeWaiter: bool ShouldKeepOnWaiting(XEvent* event) override { std::vector<XID> stack; ui::GetXWindowStack(ui::GetX11RootWindow(), &stack); @@ -86,12 +87,24 @@ class StackingClientListWaiter : public X11PropertyChangeWaiter { } // namespace -class X11TopmostWindowFinderTest : public ViewsInteractiveUITestBase { +class X11TopmostWindowFinderTest : public test::DesktopWidgetTestInteractive { public: X11TopmostWindowFinderTest() = default; - ~X11TopmostWindowFinderTest() override = default; + // DesktopWidgetTestInteractive + void SetUp() override { + // Make X11 synchronous for our display connection. This does not force the + // window manager to behave synchronously. + XSynchronize(xdisplay(), x11::True); + DesktopWidgetTestInteractive::SetUp(); + } + + void TearDown() override { + XSynchronize(xdisplay(), x11::False); + DesktopWidgetTestInteractive::TearDown(); + } + // Creates and shows a Widget with |bounds|. The caller takes ownership of // the returned widget. std::unique_ptr<Widget> CreateAndShowWidget(const gfx::Rect& bounds) { @@ -135,16 +148,19 @@ class X11TopmostWindowFinderTest : public ViewsInteractiveUITestBase { // Returns the topmost X window at the passed in screen position. XID FindTopmostXWindowAt(int screen_x, int screen_y) { - X11TopmostWindowFinder finder; + ui::X11TopmostWindowFinder finder; return finder.FindWindowAt(gfx::Point(screen_x, screen_y)); } // Returns the topmost aura::Window at the passed in screen position. Returns // NULL if the topmost window does not have an associated aura::Window. aura::Window* FindTopmostLocalProcessWindowAt(int screen_x, int screen_y) { - X11TopmostWindowFinder finder; - return finder.FindLocalProcessWindowAt(gfx::Point(screen_x, screen_y), - std::set<aura::Window*>()); + ui::X11TopmostWindowFinder finder; + auto widget = + finder.FindLocalProcessWindowAt(gfx::Point(screen_x, screen_y), {}); + return widget ? DesktopWindowTreeHostPlatform::GetContentWindowForWidget( + static_cast<gfx::AcceleratedWidget>(widget)) + : nullptr; } // Returns the topmost aura::Window at the passed in screen position ignoring @@ -154,25 +170,14 @@ class X11TopmostWindowFinderTest : public ViewsInteractiveUITestBase { int screen_x, int screen_y, aura::Window* ignore_window) { - std::set<aura::Window*> ignore; - ignore.insert(ignore_window); - X11TopmostWindowFinder finder; - return finder.FindLocalProcessWindowAt(gfx::Point(screen_x, screen_y), - ignore); - } - - // ViewsInteractiveUITestBase: - void SetUp() override { - ViewsInteractiveUITestBase::SetUp(); - - // Make X11 synchronous for our display connection. This does not force the - // window manager to behave synchronously. - XSynchronize(xdisplay(), x11::True); - } - - void TearDown() override { - XSynchronize(xdisplay(), x11::False); - ViewsInteractiveUITestBase::TearDown(); + std::set<gfx::AcceleratedWidget> ignore; + ignore.insert(ignore_window->GetHost()->GetAcceleratedWidget()); + ui::X11TopmostWindowFinder finder; + auto widget = + finder.FindLocalProcessWindowAt(gfx::Point(screen_x, screen_y), ignore); + return widget ? DesktopWindowTreeHostPlatform::GetContentWindowForWidget( + static_cast<gfx::AcceleratedWidget>(widget)) + : nullptr; } private: diff --git a/chromium/ui/views/widget/desktop_aura/x11_whole_screen_move_loop.cc b/chromium/ui/views/widget/desktop_aura/x11_whole_screen_move_loop.cc deleted file mode 100644 index 01509c2d677..00000000000 --- a/chromium/ui/views/widget/desktop_aura/x11_whole_screen_move_loop.cc +++ /dev/null @@ -1,275 +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/widget/desktop_aura/x11_whole_screen_move_loop.h" - -#include <stddef.h> - -#include <memory> -#include <utility> - -#include "base/bind.h" -#include "base/logging.h" -#include "base/message_loop/message_loop_current.h" -#include "base/run_loop.h" -#include "base/single_thread_task_runner.h" -#include "base/stl_util.h" -#include "base/threading/thread_task_runner_handle.h" -#include "ui/aura/client/capture_client.h" -#include "ui/aura/env.h" -#include "ui/aura/window.h" -#include "ui/aura/window_event_dispatcher.h" -#include "ui/aura/window_tree_host.h" -#include "ui/base/mojom/cursor_type.mojom-shared.h" -#include "ui/base/x/x11_pointer_grab.h" -#include "ui/base/x/x11_util.h" -#include "ui/events/event.h" -#include "ui/events/event_utils.h" -#include "ui/events/keycodes/keyboard_code_conversion_x.h" -#include "ui/events/platform/platform_event_source.h" -#include "ui/events/platform/scoped_event_dispatcher.h" -#include "ui/events/platform/x11/x11_event_source.h" -#include "ui/events/x/events_x_utils.h" -#include "ui/events/x/x11_window_event_manager.h" -#include "ui/gfx/x/x11.h" - -namespace views { - -// XGrabKey requires the modifier mask to explicitly be specified. -const unsigned int kModifiersMasks[] = {0, // No additional modifier. - Mod2Mask, // Num lock - LockMask, // Caps lock - Mod5Mask, // Scroll lock - Mod2Mask | LockMask, - Mod2Mask | Mod5Mask, - LockMask | Mod5Mask, - Mod2Mask | LockMask | Mod5Mask}; - -X11WholeScreenMoveLoop::X11WholeScreenMoveLoop(X11MoveLoopDelegate* delegate) - : delegate_(delegate), - in_move_loop_(false), - initial_cursor_(ui::mojom::CursorType::kNull), - should_reset_mouse_flags_(false), - grab_input_window_(x11::None), - grabbed_pointer_(false), - canceled_(false) {} - -X11WholeScreenMoveLoop::~X11WholeScreenMoveLoop() = default; - -void X11WholeScreenMoveLoop::DispatchMouseMovement() { - if (!last_motion_in_screen_) - return; - delegate_->OnMouseMovement(last_motion_in_screen_->root_location(), - last_motion_in_screen_->flags(), - last_motion_in_screen_->time_stamp()); - last_motion_in_screen_.reset(); -} - -void X11WholeScreenMoveLoop::PostDispatchIfNeeded(const ui::MouseEvent& event) { - bool dispatch_mouse_event = !last_motion_in_screen_; - last_motion_in_screen_ = std::make_unique<ui::MouseEvent>(event); - if (dispatch_mouse_event) { - // Post a task to dispatch mouse movement event when control returns to the - // message loop. This allows smoother dragging since the events are - // dispatched without waiting for the drag widget updates. - base::ThreadTaskRunnerHandle::Get()->PostTask( - FROM_HERE, - base::BindOnce(&X11WholeScreenMoveLoop::DispatchMouseMovement, - weak_factory_.GetWeakPtr())); - } -} - -//////////////////////////////////////////////////////////////////////////////// -// DesktopWindowTreeHostLinux, ui::PlatformEventDispatcher implementation: - -bool X11WholeScreenMoveLoop::CanDispatchEvent(const ui::PlatformEvent& event) { - return in_move_loop_; -} - -uint32_t X11WholeScreenMoveLoop::DispatchEvent(const ui::PlatformEvent& event) { - DCHECK(base::MessageLoopCurrentForUI::IsSet()); - - // This method processes all events while the move loop is active. - if (!in_move_loop_) - return ui::POST_DISPATCH_PERFORM_DEFAULT; - - switch (event->type()) { - case ui::ET_MOUSE_MOVED: - case ui::ET_MOUSE_DRAGGED: { - PostDispatchIfNeeded(*event->AsMouseEvent()); - return ui::POST_DISPATCH_NONE; - } - case ui::ET_MOUSE_RELEASED: { - if (event->AsMouseEvent()->IsLeftMouseButton()) { - // Assume that drags are being done with the left mouse button. Only - // break the drag if the left mouse button was released. - DispatchMouseMovement(); - delegate_->OnMouseReleased(); - - if (!grabbed_pointer_) { - // If the source widget had capture prior to the move loop starting, - // it may be relying on views::Widget getting the mouse release and - // releasing capture in Widget::OnMouseEvent(). - return ui::POST_DISPATCH_PERFORM_DEFAULT; - } - } - return ui::POST_DISPATCH_NONE; - } - case ui::ET_KEY_PRESSED: - if (event->AsKeyEvent()->key_code() == ui::VKEY_ESCAPE) { - canceled_ = true; - EndMoveLoop(); - return ui::POST_DISPATCH_NONE; - } - break; - default: - break; - } - return ui::POST_DISPATCH_PERFORM_DEFAULT; -} - -bool X11WholeScreenMoveLoop::RunMoveLoop(aura::Window* source, - gfx::NativeCursor cursor) { - DCHECK(!in_move_loop_); // Can only handle one nested loop at a time. - - // Query the mouse cursor prior to the move loop starting so that it can be - // restored when the move loop finishes. - initial_cursor_ = source->GetHost()->last_cursor(); - - CreateDragInputWindow(gfx::GetXDisplay()); - - // Only grab mouse capture of |grab_input_window_| if |source| does not have - // capture. - // - The caller may intend to transfer capture to a different aura::Window - // when the move loop ends and not release capture. - // - Releasing capture and X window destruction are both asynchronous. We drop - // events targeted at |grab_input_window_| in the time between the move - // loop ends and |grab_input_window_| loses capture. - grabbed_pointer_ = false; - if (!source->HasCapture()) { - aura::client::CaptureClient* capture_client = - aura::client::GetCaptureClient(source->GetRootWindow()); - CHECK(capture_client->GetGlobalCaptureWindow() == nullptr); - grabbed_pointer_ = GrabPointer(cursor); - if (!grabbed_pointer_) { - XDestroyWindow(gfx::GetXDisplay(), grab_input_window_); - return false; - } - } - - GrabEscKey(); - - std::unique_ptr<ui::ScopedEventDispatcher> old_dispatcher = - std::move(nested_dispatcher_); - nested_dispatcher_ = - ui::PlatformEventSource::GetInstance()->OverrideDispatcher(this); - - // We are handling a mouse drag outside of the aura::Window system. We must - // manually make aura think that the mouse button is pressed so that we don't - // draw extraneous tooltips. - aura::Env* env = aura::Env::GetInstance(); - if (!env->IsMouseButtonDown()) { - env->set_mouse_button_flags(ui::EF_LEFT_MOUSE_BUTTON); - should_reset_mouse_flags_ = true; - } - - base::WeakPtr<X11WholeScreenMoveLoop> alive(weak_factory_.GetWeakPtr()); - - in_move_loop_ = true; - canceled_ = false; - base::RunLoop run_loop(base::RunLoop::Type::kNestableTasksAllowed); - quit_closure_ = run_loop.QuitClosure(); - run_loop.Run(); - - if (!alive) - return false; - - nested_dispatcher_ = std::move(old_dispatcher); - return !canceled_; -} - -void X11WholeScreenMoveLoop::UpdateCursor(gfx::NativeCursor cursor) { - if (in_move_loop_) - ui::ChangeActivePointerGrabCursor(cursor.platform()); -} - -void X11WholeScreenMoveLoop::EndMoveLoop() { - if (!in_move_loop_) - return; - - // Prevent DispatchMouseMovement from dispatching any posted motion event. - last_motion_in_screen_.reset(); - - // We undo our emulated mouse click from RunMoveLoop(); - if (should_reset_mouse_flags_) { - aura::Env::GetInstance()->set_mouse_button_flags(0); - should_reset_mouse_flags_ = false; - } - - // TODO(erg): Is this ungrab the cause of having to click to give input focus - // on drawn out windows? Not ungrabbing here screws the X server until I kill - // the chrome process. - - // Ungrab before we let go of the window. - if (grabbed_pointer_) - ui::UngrabPointer(); - else - UpdateCursor(initial_cursor_); - - XDisplay* display = gfx::GetXDisplay(); - unsigned int esc_keycode = XKeysymToKeycode(display, XK_Escape); - for (auto mask : kModifiersMasks) - XUngrabKey(display, esc_keycode, mask, grab_input_window_); - - // Restore the previous dispatcher. - nested_dispatcher_.reset(); - delegate_->OnMoveLoopEnded(); - grab_input_window_events_.reset(); - XDestroyWindow(display, grab_input_window_); - grab_input_window_ = x11::None; - - in_move_loop_ = false; - std::move(quit_closure_).Run(); -} - -bool X11WholeScreenMoveLoop::GrabPointer(gfx::NativeCursor cursor) { - XDisplay* display = gfx::GetXDisplay(); - - // Pass "owner_events" as false so that X sends all mouse events to - // |grab_input_window_|. - int ret = ui::GrabPointer(grab_input_window_, false, cursor.platform()); - if (ret != GrabSuccess) { - DLOG(ERROR) << "Grabbing pointer for dragging failed: " - << ui::GetX11ErrorString(display, ret); - } - XFlush(display); - return ret == GrabSuccess; -} - -void X11WholeScreenMoveLoop::GrabEscKey() { - XDisplay* display = gfx::GetXDisplay(); - unsigned int esc_keycode = XKeysymToKeycode(display, XK_Escape); - for (auto mask : kModifiersMasks) { - XGrabKey(display, esc_keycode, mask, grab_input_window_, x11::False, - GrabModeAsync, GrabModeAsync); - } -} - -void X11WholeScreenMoveLoop::CreateDragInputWindow(XDisplay* display) { - XSetWindowAttributes swa; - memset(&swa, 0, sizeof(swa)); - swa.override_redirect = x11::True; - grab_input_window_ = XCreateWindow(display, DefaultRootWindow(display), -100, - -100, 10, 10, 0, CopyFromParent, InputOnly, - CopyFromParent, CWOverrideRedirect, &swa); - uint32_t event_mask = ButtonPressMask | ButtonReleaseMask | - PointerMotionMask | KeyPressMask | KeyReleaseMask | - StructureNotifyMask; - grab_input_window_events_ = std::make_unique<ui::XScopedEventSelector>( - grab_input_window_, event_mask); - - XMapRaised(display, grab_input_window_); -} - -} // namespace views diff --git a/chromium/ui/views/widget/desktop_aura/x11_whole_screen_move_loop.h b/chromium/ui/views/widget/desktop_aura/x11_whole_screen_move_loop.h deleted file mode 100644 index 1fa5bbd5cca..00000000000 --- a/chromium/ui/views/widget/desktop_aura/x11_whole_screen_move_loop.h +++ /dev/null @@ -1,105 +0,0 @@ -// Copyright (c) 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. - -#ifndef UI_VIEWS_WIDGET_DESKTOP_AURA_X11_WHOLE_SCREEN_MOVE_LOOP_H_ -#define UI_VIEWS_WIDGET_DESKTOP_AURA_X11_WHOLE_SCREEN_MOVE_LOOP_H_ - -#include <stdint.h> - -#include <memory> - -#include "base/callback.h" -#include "base/compiler_specific.h" -#include "base/macros.h" -#include "base/memory/weak_ptr.h" -#include "ui/base/cursor/cursor.h" -#include "ui/events/platform/platform_event_dispatcher.h" -#include "ui/gfx/geometry/vector2d_f.h" -#include "ui/gfx/image/image_skia.h" -#include "ui/gfx/native_widget_types.h" -#include "ui/gfx/x/x11_types.h" -#include "ui/views/widget/desktop_aura/x11_move_loop.h" -#include "ui/views/widget/desktop_aura/x11_move_loop_delegate.h" - -namespace aura { -class Window; -} - -namespace ui { -class MouseEvent; -class ScopedEventDispatcher; -class XScopedEventSelector; -} // namespace ui - -namespace views { - -// Runs a nested run loop and grabs the mouse. This is used to implement -// dragging. -class X11WholeScreenMoveLoop : public X11MoveLoop, - public ui::PlatformEventDispatcher { - public: - explicit X11WholeScreenMoveLoop(X11MoveLoopDelegate* delegate); - ~X11WholeScreenMoveLoop() override; - - // ui:::PlatformEventDispatcher: - bool CanDispatchEvent(const ui::PlatformEvent& event) override; - uint32_t DispatchEvent(const ui::PlatformEvent& event) override; - - // X11MoveLoop: - bool RunMoveLoop(aura::Window* window, gfx::NativeCursor cursor) override; - void UpdateCursor(gfx::NativeCursor cursor) override; - void EndMoveLoop() override; - - private: - // Grabs the pointer, setting the mouse cursor to |cursor|. Returns true if - // successful. - bool GrabPointer(gfx::NativeCursor cursor); - - void GrabEscKey(); - - // Creates an input-only window to be used during the drag. - void CreateDragInputWindow(XDisplay* display); - - // Dispatch mouse movement event to |delegate_| in a posted task. - void DispatchMouseMovement(); - - void PostDispatchIfNeeded(const ui::MouseEvent& event); - - X11MoveLoopDelegate* delegate_; - - // Are we running a nested run loop from RunMoveLoop()? - bool in_move_loop_; - std::unique_ptr<ui::ScopedEventDispatcher> nested_dispatcher_; - - // Cursor in use prior to the move loop starting. Restored when the move loop - // quits. - gfx::NativeCursor initial_cursor_; - - bool should_reset_mouse_flags_; - - // An invisible InputOnly window. Keyboard grab and sometimes mouse grab - // are set on this window. - XID grab_input_window_; - - // Events selected on |grab_input_window_|. - std::unique_ptr<ui::XScopedEventSelector> grab_input_window_events_; - - // Whether the pointer was grabbed on |grab_input_window_|. - bool grabbed_pointer_; - - base::OnceClosure quit_closure_; - - // Keeps track of whether the move-loop is cancled by the user (e.g. by - // pressing escape). - bool canceled_; - - std::unique_ptr<ui::MouseEvent> last_motion_in_screen_; - base::WeakPtrFactory<X11WholeScreenMoveLoop> weak_factory_{this}; - - DISALLOW_COPY_AND_ASSIGN(X11WholeScreenMoveLoop); -}; - -} // namespace views - -#endif // UI_VIEWS_WIDGET_DESKTOP_AURA_X11_WHOLE_SCREEN_MOVE_LOOP_H_ diff --git a/chromium/ui/views/widget/desktop_widget_unittest.cc b/chromium/ui/views/widget/desktop_widget_unittest.cc index 6295852a6cb..7eb0473bb00 100644 --- a/chromium/ui/views/widget/desktop_widget_unittest.cc +++ b/chromium/ui/views/widget/desktop_widget_unittest.cc @@ -64,7 +64,7 @@ TEST_F(DesktopScreenPositionClientTest, PositionControlWithNonRootParent) { params2.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; params2.child = true; params2.native_widget = test::CreatePlatformNativeWidgetImpl( - params2, &widget2, test::kStubCapture, nullptr); + &widget2, test::kStubCapture, nullptr); widget2.Init(std::move(params2)); Widget::InitParams params3 = CreateParams(Widget::InitParams::TYPE_CONTROL); @@ -73,7 +73,7 @@ TEST_F(DesktopScreenPositionClientTest, PositionControlWithNonRootParent) { params3.child = true; params3.bounds = gfx::Rect(origin, gfx::Size(500, work_area.height() - 200)); params3.native_widget = test::CreatePlatformNativeWidgetImpl( - params3, &widget3, test::kStubCapture, nullptr); + &widget3, test::kStubCapture, nullptr); widget3.Init(std::move(params3)); // The origin of the 3rd window should be the sum of all parent origins. @@ -147,7 +147,7 @@ TEST_F(DesktopScreenPositionClientTest, InitialBoundsConstrainedToParent) { params2.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; params2.child = true; params2.native_widget = test::CreatePlatformNativeWidgetImpl( - params2, &widget2, test::kStubCapture, nullptr); + &widget2, test::kStubCapture, nullptr); widget2.Init(std::move(params2)); // The bounds of the child window should be fully in the parent. diff --git a/chromium/ui/views/widget/native_widget_aura.cc b/chromium/ui/views/widget/native_widget_aura.cc index efd53803fa4..51f57997afa 100644 --- a/chromium/ui/views/widget/native_widget_aura.cc +++ b/chromium/ui/views/widget/native_widget_aura.cc @@ -1072,7 +1072,7 @@ void NativeWidgetAura::SetInitialFocus(ui::WindowShowState show_state) { // Widget, public: namespace { -#if BUILDFLAG(ENABLE_DESKTOP_AURA) +#if BUILDFLAG(ENABLE_DESKTOP_AURA) && (defined(OS_WIN) || defined(OS_LINUX)) void CloseWindow(aura::Window* window) { if (window) { Widget* widget = Widget::GetWidgetForNativeView(window); diff --git a/chromium/ui/views/widget/native_widget_aura_interactive_uitest.cc b/chromium/ui/views/widget/native_widget_aura_interactive_uitest.cc index 6715dff1b73..6e13e1376a9 100644 --- a/chromium/ui/views/widget/native_widget_aura_interactive_uitest.cc +++ b/chromium/ui/views/widget/native_widget_aura_interactive_uitest.cc @@ -7,7 +7,6 @@ #include "ui/aura/window.h" #include "ui/views/controls/textfield/textfield.h" #include "ui/views/test/native_widget_factory.h" -#include "ui/views/test/views_interactive_ui_test_base.h" #include "ui/views/test/widget_test.h" #include "ui/wm/core/base_focus_rules.h" #include "ui/wm/core/focus_controller.h" @@ -41,7 +40,7 @@ class TestFocusRules : public wm::BaseFocusRules { } // namespace -using NativeWidgetAuraTest = ViewsInteractiveUITestBase; +using NativeWidgetAuraTest = DesktopWidgetTestInteractive; // When requesting view focus from a non-active top level widget, focus is not // instantly given. Instead, the view is firstly stored and then it is attempted @@ -58,7 +57,7 @@ TEST_F(NativeWidgetAuraTest, NonActiveWindowRequestImeFocus) { Widget::InitParams params1(Widget::InitParams::TYPE_WINDOW_FRAMELESS); params1.context = GetContext(); params1.native_widget = - CreatePlatformNativeWidgetImpl(params1, widget1, kDefault, nullptr); + CreatePlatformNativeWidgetImpl(widget1, kDefault, nullptr); params1.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; widget1->Init(std::move(params1)); Textfield* textfield1 = new Textfield; @@ -68,7 +67,7 @@ TEST_F(NativeWidgetAuraTest, NonActiveWindowRequestImeFocus) { Widget::InitParams params2(Widget::InitParams::TYPE_WINDOW_FRAMELESS); params2.context = GetContext(); params2.native_widget = - CreatePlatformNativeWidgetImpl(params2, widget2, kDefault, nullptr); + CreatePlatformNativeWidgetImpl(widget2, kDefault, nullptr); params2.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; widget2->Init(std::move(params2)); Textfield* textfield2a = new Textfield; diff --git a/chromium/ui/views/widget/native_widget_aura_unittest.cc b/chromium/ui/views/widget/native_widget_aura_unittest.cc index 5bd2526da57..4e9ce929364 100644 --- a/chromium/ui/views/widget/native_widget_aura_unittest.cc +++ b/chromium/ui/views/widget/native_widget_aura_unittest.cc @@ -413,9 +413,9 @@ TEST_F(NativeWidgetAuraTest, DontCaptureOnGesture) { widget->SetContentsView(view); widget->Show(); - ui::TouchEvent press( - ui::ET_TOUCH_PRESSED, gfx::Point(41, 51), ui::EventTimeForNow(), - ui::PointerDetails(ui::EventPointerType::POINTER_TYPE_TOUCH, 1)); + ui::TouchEvent press(ui::ET_TOUCH_PRESSED, gfx::Point(41, 51), + ui::EventTimeForNow(), + ui::PointerDetails(ui::EventPointerType::kTouch, 1)); ui::EventDispatchDetails details = event_sink()->OnEventFromSource(&press); ASSERT_FALSE(details.dispatcher_destroyed); // Both views should get the press. @@ -428,9 +428,9 @@ TEST_F(NativeWidgetAuraTest, DontCaptureOnGesture) { // Release touch. Only |view| should get the release since that it consumed // the press. - ui::TouchEvent release( - ui::ET_TOUCH_RELEASED, gfx::Point(250, 251), ui::EventTimeForNow(), - ui::PointerDetails(ui::EventPointerType::POINTER_TYPE_TOUCH, 1)); + ui::TouchEvent release(ui::ET_TOUCH_RELEASED, gfx::Point(250, 251), + ui::EventTimeForNow(), + ui::PointerDetails(ui::EventPointerType::kTouch, 1)); details = event_sink()->OnEventFromSource(&release); ASSERT_FALSE(details.dispatcher_destroyed); EXPECT_TRUE(view->got_gesture_event()); diff --git a/chromium/ui/views/widget/native_widget_mac_unittest.mm b/chromium/ui/views/widget/native_widget_mac_unittest.mm index 90c111179a7..9a6b9ac6e90 100644 --- a/chromium/ui/views/widget/native_widget_mac_unittest.mm +++ b/chromium/ui/views/widget/native_widget_mac_unittest.mm @@ -232,30 +232,6 @@ class WidgetChangeObserver : public TestWidgetObserver { DISALLOW_COPY_AND_ASSIGN(WidgetChangeObserver); }; -class NativeHostHolder { - public: - NativeHostHolder() - : view_([[NSView alloc] init]), host_(new NativeViewHost()) { - host_->set_owned_by_client(); - } - - void AttachNativeView() { - DCHECK(!host_->native_view()); - host_->Attach(view_.get()); - } - - void Detach() { host_->Detach(); } - - NSView* view() const { return view_.get(); } - NativeViewHost* host() const { return host_.get(); } - - private: - base::scoped_nsobject<NSView> view_; - std::unique_ptr<NativeViewHost> host_; - - DISALLOW_COPY_AND_ASSIGN(NativeHostHolder); -}; - // This class gives public access to the protected ctor of // BubbleDialogDelegateView. class SimpleBubbleView : public BubbleDialogDelegateView { @@ -1624,8 +1600,8 @@ class ParentCloseMonitor : public WidgetObserver { Widget::InitParams init_params(Widget::InitParams::TYPE_WINDOW_FRAMELESS); init_params.parent = parent->GetNativeView(); init_params.bounds = gfx::Rect(100, 100, 100, 100); - init_params.native_widget = CreatePlatformNativeWidgetImpl( - init_params, child, kStubCapture, nullptr); + init_params.native_widget = + CreatePlatformNativeWidgetImpl(child, kStubCapture, nullptr); child->Init(std::move(init_params)); child->Show(); @@ -1814,7 +1790,7 @@ TEST_F(NativeWidgetMacTest, DISABLED_DoesHideTitle) { Widget::InitParams params = CreateParams(Widget::InitParams::TYPE_WINDOW); Widget* widget = new Widget; params.native_widget = - CreatePlatformNativeWidgetImpl(params, widget, kStubCapture, nullptr); + CreatePlatformNativeWidgetImpl(widget, kStubCapture, nullptr); CustomTitleWidgetDelegate delegate(widget); params.delegate = &delegate; params.bounds = gfx::Rect(0, 0, 800, 600); @@ -2247,6 +2223,29 @@ class NativeWidgetMacViewsOrderTest : public WidgetTest { NativeWidgetMacViewsOrderTest() {} protected: + class NativeHostHolder { + public: + static std::unique_ptr<NativeHostHolder> CreateAndAddToParent( + View* parent) { + std::unique_ptr<NativeHostHolder> holder(new NativeHostHolder( + parent->AddChildView(std::make_unique<NativeViewHost>()))); + holder->host()->Attach(holder->view()); + return holder; + } + + NSView* view() const { return view_.get(); } + NativeViewHost* host() const { return host_; } + + private: + NativeHostHolder(NativeViewHost* host) + : host_(host), view_([[NSView alloc] init]) {} + + NativeViewHost* const host_; + base::scoped_nsobject<NSView> view_; + + DISALLOW_COPY_AND_ASSIGN(NativeHostHolder); + }; + // testing::Test: void SetUp() override { WidgetTest::SetUp(); @@ -2261,10 +2260,8 @@ class NativeWidgetMacViewsOrderTest : public WidgetTest { const size_t kNativeViewCount = 3; for (size_t i = 0; i < kNativeViewCount; ++i) { - auto holder = std::make_unique<NativeHostHolder>(); - native_host_parent_->AddChildView(holder->host()); - holder->AttachNativeView(); - hosts_.push_back(std::move(holder)); + hosts_.push_back( + NativeHostHolder::CreateAndAddToParent(native_host_parent_)); } EXPECT_EQ(kNativeViewCount, native_host_parent_->children().size()); EXPECT_NSEQ([widget_->GetNativeView().GetNativeNSView() subviews], @@ -2275,6 +2272,7 @@ class NativeWidgetMacViewsOrderTest : public WidgetTest { void TearDown() override { widget_->CloseNow(); + hosts_.clear(); WidgetTest::TearDown(); } @@ -2296,13 +2294,14 @@ class NativeWidgetMacViewsOrderTest : public WidgetTest { // Test that NativeViewHost::Attach()/Detach() method saves the NativeView // z-order. TEST_F(NativeWidgetMacViewsOrderTest, NativeViewAttached) { - hosts_[1]->Detach(); + NativeHostHolder* second_host = hosts_[1].get(); + second_host->host()->Detach(); EXPECT_NSEQ([GetContentNativeView() subviews], ([GetStartingSubviews() arrayByAddingObjectsFromArray:@[ hosts_[0]->view(), hosts_[2]->view() ]])); - hosts_[1]->AttachNativeView(); + second_host->host()->Attach(second_host->view()); EXPECT_NSEQ([GetContentNativeView() subviews], ([GetStartingSubviews() arrayByAddingObjectsFromArray:@[ hosts_[0]->view(), hosts_[1]->view(), hosts_[2]->view() diff --git a/chromium/ui/views/widget/tooltip_manager_aura.cc b/chromium/ui/views/widget/tooltip_manager_aura.cc index ca24a4c51b5..2aceae76419 100644 --- a/chromium/ui/views/widget/tooltip_manager_aura.cc +++ b/chromium/ui/views/widget/tooltip_manager_aura.cc @@ -4,7 +4,6 @@ #include "ui/views/widget/tooltip_manager_aura.h" -#include "base/logging.h" #include "ui/aura/client/screen_position_client.h" #include "ui/aura/window_event_dispatcher.h" #include "ui/aura/window_tree_host.h" diff --git a/chromium/ui/views/widget/widget.cc b/chromium/ui/views/widget/widget.cc index 6472b4210a0..ecd7be9185f 100644 --- a/chromium/ui/views/widget/widget.cc +++ b/chromium/ui/views/widget/widget.cc @@ -8,9 +8,10 @@ #include <utility> #include "base/auto_reset.h" +#include "base/check_op.h" #include "base/containers/adapters.h" -#include "base/logging.h" #include "base/macros.h" +#include "base/notreached.h" #include "base/strings/utf_string_conversions.h" #include "base/trace_event/trace_event.h" #include "ui/base/cursor/cursor.h" @@ -92,19 +93,18 @@ bool Widget::g_disable_activation_change_handling_ = false; // WidgetDelegate is supplied. class DefaultWidgetDelegate : public WidgetDelegate { public: - explicit DefaultWidgetDelegate(Widget* widget) : widget_(widget) {} + explicit DefaultWidgetDelegate(Widget* widget) : widget_(widget) { + // In most situations where a Widget is used without a delegate the Widget + // is used as a container, so that we want focus to advance to the top-level + // widget. A good example of this is the find bar. + SetFocusTraversesOut(true); + } ~DefaultWidgetDelegate() override = default; // WidgetDelegate: void DeleteDelegate() override { delete this; } Widget* GetWidget() override { return widget_; } const Widget* GetWidget() const override { return widget_; } - bool ShouldAdvanceFocusToTopLevelWidget() const override { - // In most situations where a Widget is used without a delegate the Widget - // is used as a container, so that we want focus to advance to the top-level - // widget. A good example of this is the find bar. - return true; - } private: Widget* widget_; @@ -162,10 +162,8 @@ ui::ZOrderLevel Widget::InitParams::EffectiveZOrderLevel() const { switch (type) { case TYPE_MENU: return ui::ZOrderLevel::kFloatingWindow; - break; case TYPE_DRAG: return ui::ZOrderLevel::kFloatingUIElement; - break; default: return ui::ZOrderLevel::kNormal; } @@ -769,7 +767,7 @@ bool Widget::IsVisible() const { const ui::ThemeProvider* Widget::GetThemeProvider() const { const Widget* root_widget = GetTopLevelWidget(); return (root_widget && root_widget != this) ? root_widget->GetThemeProvider() - : &default_theme_provider_; + : nullptr; } FocusManager* Widget::GetFocusManager() { diff --git a/chromium/ui/views/widget/widget.h b/chromium/ui/views/widget/widget.h index 105fc39ed5e..0cbc349dd3d 100644 --- a/chromium/ui/views/widget/widget.h +++ b/chromium/ui/views/widget/widget.h @@ -16,7 +16,6 @@ #include "base/optional.h" #include "base/scoped_observer.h" #include "build/build_config.h" -#include "ui/base/default_theme_provider.h" #include "ui/base/ui_base_types.h" #include "ui/events/event_source.h" #include "ui/gfx/geometry/rect.h" @@ -498,7 +497,18 @@ class VIEWS_EXPORT Widget : public internal::NativeWidgetDelegate, // Sets the specified view as the contents of this Widget. There can only // be one contents view child of this Widget's RootView. This view is sized to // fit the entire size of the RootView. The RootView takes ownership of this - // View, unless it is set as not being parent-owned. + // View, unless it is passed in as a raw pointer and set as not being + // parent-owned. Prefer using SetContentsView(std::unique_ptr) over passing a + // raw pointer for new code. + template <typename T> + T* SetContentsView(std::unique_ptr<T> view) { + DCHECK(!view->owned_by_client()) + << "This should only be called if the client is passing over the " + "ownership of |view|."; + T* raw_pointer = view.get(); + SetContentsView(view.release()); + return raw_pointer; + } void SetContentsView(View* view); // NOTE: This may not be the same view as WidgetDelegate::GetContentsView(). @@ -1050,9 +1060,6 @@ class VIEWS_EXPORT Widget : public internal::NativeWidgetDelegate, // must be destroyed AFTER root_view_. This is enforced in DestroyRootView(). std::unique_ptr<FocusManager> focus_manager_; - // A theme provider to use when no other theme provider is specified. - const ui::DefaultThemeProvider default_theme_provider_; - // Valid for the lifetime of RunShellDrag(), indicates the view the drag // started from. View* dragged_view_ = nullptr; diff --git a/chromium/ui/views/widget/widget_aura_utils.cc b/chromium/ui/views/widget/widget_aura_utils.cc index 85cedf9f62b..424460fe0ed 100644 --- a/chromium/ui/views/widget/widget_aura_utils.cc +++ b/chromium/ui/views/widget/widget_aura_utils.cc @@ -4,7 +4,7 @@ #include "ui/views/widget/widget_aura_utils.h" -#include "base/logging.h" +#include "base/notreached.h" namespace views { diff --git a/chromium/ui/views/widget/widget_delegate.cc b/chromium/ui/views/widget/widget_delegate.cc index 1d7c735d497..4ba80f860b3 100644 --- a/chromium/ui/views/widget/widget_delegate.cc +++ b/chromium/ui/views/widget/widget_delegate.cc @@ -4,7 +4,7 @@ #include "ui/views/widget/widget_delegate.h" -#include "base/logging.h" +#include "base/check.h" #include "base/strings/utf_string_conversions.h" #include "ui/accessibility/ax_enums.mojom.h" #include "ui/display/display.h" @@ -20,6 +20,9 @@ namespace views { //////////////////////////////////////////////////////////////////////////////// // WidgetDelegate: +WidgetDelegate::Params::Params() = default; +WidgetDelegate::Params::~Params() = default; + WidgetDelegate::WidgetDelegate() = default; WidgetDelegate::~WidgetDelegate() { CHECK(can_delete_this_) << "A WidgetDelegate must outlive its Widget"; @@ -52,15 +55,15 @@ DialogDelegate* WidgetDelegate::AsDialogDelegate() { } bool WidgetDelegate::CanResize() const { - return false; + return params_.can_resize; } bool WidgetDelegate::CanMaximize() const { - return false; + return params_.can_maximize; } bool WidgetDelegate::CanMinimize() const { - return false; + return params_.can_minimize; } bool WidgetDelegate::CanActivate() const { @@ -80,19 +83,23 @@ base::string16 WidgetDelegate::GetAccessibleWindowTitle() const { } base::string16 WidgetDelegate::GetWindowTitle() const { - return base::string16(); + return params_.title; } bool WidgetDelegate::ShouldShowWindowTitle() const { - return true; + return params_.show_title; } bool WidgetDelegate::ShouldCenterWindowTitleText() const { +#if defined(USE_AURA) + return params_.center_title; +#else return false; +#endif } bool WidgetDelegate::ShouldShowCloseButton() const { - return true; + return params_.show_close_button; } gfx::ImageSkia WidgetDelegate::GetWindowAppIcon() { @@ -102,11 +109,11 @@ gfx::ImageSkia WidgetDelegate::GetWindowAppIcon() { // Returns the icon to be displayed in the window. gfx::ImageSkia WidgetDelegate::GetWindowIcon() { - return gfx::ImageSkia(); + return params_.icon; } bool WidgetDelegate::ShouldShowWindowIcon() const { - return false; + return params_.show_icon; } bool WidgetDelegate::ExecuteWindowsCommand(int command_id) { @@ -145,6 +152,24 @@ bool WidgetDelegate::ShouldRestoreWindowSize() const { return true; } +void WidgetDelegate::WindowWillClose() { + // TODO(ellyjones): For this and the other callback methods, establish whether + // any other code calls these methods. If not, DCHECK here and below that + // these methods are only called once. + for (auto&& callback : window_will_close_callbacks_) + std::move(callback).Run(); +} + +void WidgetDelegate::WindowClosing() { + for (auto&& callback : window_closing_callbacks_) + std::move(callback).Run(); +} + +void WidgetDelegate::DeleteDelegate() { + for (auto&& callback : delete_delegate_callbacks_) + std::move(callback).Run(); +} + View* WidgetDelegate::GetContentsView() { if (!default_contents_view_) default_contents_view_ = new View; @@ -175,16 +200,72 @@ void WidgetDelegate::GetWidgetHitTestMask(SkPath* mask) const { DCHECK(mask); } -bool WidgetDelegate::ShouldAdvanceFocusToTopLevelWidget() const { - return false; -} - bool WidgetDelegate::ShouldDescendIntoChildForEventHandling( gfx::NativeView child, const gfx::Point& location) { return true; } +void WidgetDelegate::SetCanMaximize(bool can_maximize) { + params_.can_maximize = can_maximize; +} + +void WidgetDelegate::SetCanMinimize(bool can_minimize) { + params_.can_minimize = can_minimize; +} + +void WidgetDelegate::SetCanResize(bool can_resize) { + params_.can_resize = can_resize; +} + +void WidgetDelegate::SetFocusTraversesOut(bool focus_traverses_out) { + params_.focus_traverses_out = focus_traverses_out; +} + +void WidgetDelegate::SetIcon(const gfx::ImageSkia& icon) { + params_.icon = icon; +} + +void WidgetDelegate::SetShowCloseButton(bool show_close_button) { + params_.show_close_button = show_close_button; +} + +void WidgetDelegate::SetShowIcon(bool show_icon) { + params_.show_icon = show_icon; +} + +void WidgetDelegate::SetShowTitle(bool show_title) { + params_.show_title = show_title; +} + +void WidgetDelegate::SetTitle(const base::string16& title) { + if (params_.title == title) + return; + params_.title = title; + if (GetWidget()) + GetWidget()->UpdateWindowTitle(); +} + +#if defined(USE_AURA) +void WidgetDelegate::SetCenterTitle(bool center_title) { + params_.center_title = center_title; +} +#endif + +void WidgetDelegate::RegisterWindowWillCloseCallback( + base::OnceClosure callback) { + window_will_close_callbacks_.emplace_back(std::move(callback)); +} + +void WidgetDelegate::RegisterWindowClosingCallback(base::OnceClosure callback) { + window_closing_callbacks_.emplace_back(std::move(callback)); +} + +void WidgetDelegate::RegisterDeleteDelegateCallback( + base::OnceClosure callback) { + delete_delegate_callbacks_.emplace_back(std::move(callback)); +} + //////////////////////////////////////////////////////////////////////////////// // WidgetDelegateView: diff --git a/chromium/ui/views/widget/widget_delegate.h b/chromium/ui/views/widget/widget_delegate.h index a3ec229f11c..23396ad8c3a 100644 --- a/chromium/ui/views/widget/widget_delegate.h +++ b/chromium/ui/views/widget/widget_delegate.h @@ -29,6 +29,44 @@ class View; // Handles events on Widgets in context-specific ways. class VIEWS_EXPORT WidgetDelegate { public: + struct Params { + Params(); + ~Params(); + + // Whether the window should display controls for the user to minimize, + // maximize, or resize it. + bool can_maximize = false; + bool can_minimize = false; + bool can_resize = false; + +#if defined(USE_AURA) + // Whether to center the widget's title within the frame. + bool center_title = false; +#endif + + // Controls focus traversal past the first/last focusable view. + // If true, focus moves out of this Widget and to this Widget's toplevel + // Widget; if false, focus cycles within this Widget. + bool focus_traverses_out = false; + + // The widget's icon, if any. + gfx::ImageSkia icon; + + // Whether to show a close button in the widget frame. + bool show_close_button = true; + + // Whether to show the widget's icon. + // TODO(ellyjones): What if this was implied by !icon.isNull()? + bool show_icon = false; + + // Whether to display the widget's title in the frame. + bool show_title = true; + + // The widget's title, if any. + // TODO(ellyjones): Should it be illegal to have show_title && !title? + base::string16 title; + }; + WidgetDelegate(); // Sets the return value of CanActivate(). Default is true. @@ -90,9 +128,6 @@ class VIEWS_EXPORT WidgetDelegate { // Returns true if the window should show a title in the title bar. virtual bool ShouldShowWindowTitle() const; - // Returns true if the title text should be centered. Default is false. - virtual bool ShouldCenterWindowTitleText() const; - // Returns true if the window should show a close button in the title bar. virtual bool ShouldShowCloseButton() const; @@ -146,9 +181,12 @@ class VIEWS_EXPORT WidgetDelegate { // Close() or CloseNow(). // Important note: for OS-initiated window closes, steps 1 and 2 don't happen // - i.e, WindowWillClose() is never invoked. - virtual void WindowWillClose() {} - virtual void WindowClosing() {} - virtual void DeleteDelegate() {} + // + // The default implementations of these methods simply call the corresponding + // callbacks; see Set*Callback() below. If you override these it is not + // necessary to call the base implementations. + virtual void WindowClosing(); + virtual void DeleteDelegate(); // Called when the user begins/ends to change the bounds of the window. virtual void OnWindowBeginUserBoundsChange() {} @@ -187,11 +225,6 @@ class VIEWS_EXPORT WidgetDelegate { // Provides the hit-test mask if HasHitTestMask above returns true. virtual void GetWidgetHitTestMask(SkPath* mask) const; - // Returns true if focus should advance to the top level widget when - // tab/shift-tab is hit and on the last/first focusable view. Default returns - // false, which means tab/shift-tab never advance to the top level Widget. - virtual bool ShouldAdvanceFocusToTopLevelWidget() const; - // Returns true if event handling should descend into |child|. // |location| is in terms of the Window. virtual bool ShouldDescendIntoChildForEventHandling( @@ -202,18 +235,52 @@ class VIEWS_EXPORT WidgetDelegate { // be cycled through with keyboard focus. virtual void GetAccessiblePanes(std::vector<View*>* panes) {} + // Setters for data parameters of the WidgetDelegate. If you use these + // setters, there is no need to override the corresponding virtual getters. + void SetCanMaximize(bool can_maximize); + void SetCanMinimize(bool can_minimize); + void SetCanResize(bool can_resize); + void SetFocusTraversesOut(bool focus_traverses_out); + void SetIcon(const gfx::ImageSkia& icon); + void SetShowCloseButton(bool show_close_button); + void SetShowIcon(bool show_icon); + void SetShowTitle(bool show_title); + void SetTitle(const base::string16& title); +#if defined(USE_AURA) + void SetCenterTitle(bool center_title); +#endif + + void RegisterWindowWillCloseCallback(base::OnceClosure callback); + void RegisterWindowClosingCallback(base::OnceClosure callback); + void RegisterDeleteDelegateCallback(base::OnceClosure callback); + + // Call this to notify the WidgetDelegate that its Widget is about to start + // closing. + void WindowWillClose(); + + // Returns true if the title text should be centered. + bool ShouldCenterWindowTitleText() const; + + bool focus_traverses_out() const { return params_.focus_traverses_out; } + protected: virtual ~WidgetDelegate(); private: friend class Widget; + Params params_; + View* default_contents_view_ = nullptr; bool can_activate_ = true; // Managed by Widget. Ensures |this| outlives its Widget. bool can_delete_this_ = true; + std::vector<base::OnceClosure> window_will_close_callbacks_; + std::vector<base::OnceClosure> window_closing_callbacks_; + std::vector<base::OnceClosure> delete_delegate_callbacks_; + DISALLOW_COPY_AND_ASSIGN(WidgetDelegate); }; diff --git a/chromium/ui/views/widget/widget_interactive_uitest.cc b/chromium/ui/views/widget/widget_interactive_uitest.cc index 0f50819e3c4..f4ba5ba6cad 100644 --- a/chromium/ui/views/widget/widget_interactive_uitest.cc +++ b/chromium/ui/views/widget/widget_interactive_uitest.cc @@ -29,12 +29,12 @@ #include "ui/events/event_utils.h" #include "ui/events/test/event_generator.h" #include "ui/gfx/native_widget_types.h" +#include "ui/views/bubble/bubble_dialog_delegate_view.h" #include "ui/views/controls/textfield/textfield.h" #include "ui/views/controls/textfield/textfield_test_api.h" #include "ui/views/focus/focus_manager.h" #include "ui/views/test/focus_manager_test.h" #include "ui/views/test/native_widget_factory.h" -#include "ui/views/test/views_interactive_ui_test_base.h" #include "ui/views/test/widget_test.h" #include "ui/views/touchui/touch_selection_controller_impl.h" #include "ui/views/widget/root_view.h" @@ -290,39 +290,6 @@ class WidgetTestInteractive : public WidgetTest { SetUpForInteractiveTests(); WidgetTest::SetUp(); } - - protected: -#if defined(USE_AURA) - static void ShowQuickMenuImmediately( - TouchSelectionControllerImpl* controller) { - DCHECK(controller); - if (controller->quick_menu_timer_.IsRunning()) { - controller->quick_menu_timer_.Stop(); - controller->QuickMenuTimerFired(); - } - } -#endif // defined (USE_AURA) -}; - -class DesktopWidgetTestInteractive : public WidgetTestInteractive { - public: - DesktopWidgetTestInteractive() = default; - ~DesktopWidgetTestInteractive() override = default; - - // WidgetTestInteractive: - void SetUp() override { - set_native_widget_type(NativeWidgetType::kDesktop); - WidgetTestInteractive::SetUp(); - } - - protected: - Widget* CreateWidget() { - Widget* widget = CreateTopLevelNativeWidget(); - widget->SetBounds(gfx::Rect(0, 0, 200, 200)); - return widget; - } - - DISALLOW_COPY_AND_ASSIGN(DesktopWidgetTestInteractive); }; #if defined(OS_WIN) @@ -340,10 +307,10 @@ TEST_F(DesktopWidgetTestInteractive, // Create widget 1 and expect the active window to be its window. View* focusable_view1 = new View; focusable_view1->SetFocusBehavior(View::FocusBehavior::ALWAYS); - Widget* widget1 = CreateWidget(); + WidgetAutoclosePtr widget1(CreateTopLevelNativeWidget()); widget1->GetContentsView()->AddChildView(focusable_view1); widget1->Show(); - aura::Window* root_window1 = GetRootWindow(widget1); + aura::Window* root_window1 = GetRootWindow(widget1.get()); focusable_view1->RequestFocus(); EXPECT_TRUE(root_window1 != nullptr); @@ -354,12 +321,12 @@ TEST_F(DesktopWidgetTestInteractive, // Create widget 2 and expect the active window to be its window. View* focusable_view2 = new View; - Widget* widget2 = CreateWidget(); + WidgetAutoclosePtr widget2(CreateTopLevelNativeWidget()); widget1->GetContentsView()->AddChildView(focusable_view2); widget2->Show(); - aura::Window* root_window2 = GetRootWindow(widget2); + aura::Window* root_window2 = GetRootWindow(widget2.get()); focusable_view2->RequestFocus(); - ActivatePlatformWindow(widget2); + ActivatePlatformWindow(widget2.get()); wm::ActivationClient* activation_client2 = wm::GetActivationClient(root_window2); @@ -371,13 +338,41 @@ TEST_F(DesktopWidgetTestInteractive, // Now set focus back to widget 1 and expect the active window to be its // window. focusable_view1->RequestFocus(); - ActivatePlatformWindow(widget1); + ActivatePlatformWindow(widget1.get()); EXPECT_EQ(activation_client2->GetActiveWindow(), reinterpret_cast<aura::Window*>(NULL)); EXPECT_EQ(activation_client1->GetActiveWindow(), widget1->GetNativeView()); +} - widget2->CloseNow(); - widget1->CloseNow(); +// Verifies bubbles result in a focus lost when shown. +TEST_F(DesktopWidgetTestInteractive, FocusChangesOnBubble) { + WidgetAutoclosePtr widget(CreateTopLevelNativeWidget()); + View* focusable_view = + widget->GetContentsView()->AddChildView(std::make_unique<View>()); + focusable_view->SetFocusBehavior(View::FocusBehavior::ALWAYS); + widget->Show(); + focusable_view->RequestFocus(); + EXPECT_TRUE(focusable_view->HasFocus()); + + // Show a bubble. + auto owned_bubble_delegate_view = + std::make_unique<views::BubbleDialogDelegateView>(focusable_view, + BubbleBorder::NONE); + owned_bubble_delegate_view->SetFocusBehavior(View::FocusBehavior::ALWAYS); + BubbleDialogDelegateView* bubble_delegate_view = + owned_bubble_delegate_view.get(); + BubbleDialogDelegateView::CreateBubble(owned_bubble_delegate_view.release()) + ->Show(); + bubble_delegate_view->RequestFocus(); + + // |focusable_view| should no longer have focus. + EXPECT_FALSE(focusable_view->HasFocus()); + EXPECT_TRUE(bubble_delegate_view->HasFocus()); + + bubble_delegate_view->GetWidget()->CloseNow(); + + // Closing the bubble should result in focus going back to the contents view. + EXPECT_TRUE(focusable_view->HasFocus()); } class TouchEventHandler : public ui::EventHandler { @@ -433,210 +428,23 @@ TEST_F(DesktopWidgetTestInteractive, DISABLED_TouchNoActivateWindow) { View* focusable_view = new View; focusable_view->SetFocusBehavior(View::FocusBehavior::ALWAYS); - Widget* widget = CreateWidget(); + WidgetAutoclosePtr widget(CreateTopLevelNativeWidget()); widget->GetContentsView()->AddChildView(focusable_view); widget->Show(); { - TouchEventHandler touch_event_handler(widget); + TouchEventHandler touch_event_handler(widget.get()); ASSERT_TRUE(ui_controls::SendTouchEvents(ui_controls::PRESS, 1, 100, 100)); touch_event_handler.WaitForEvents(); } - - widget->CloseNow(); } #endif // defined(OS_WIN) -TEST_F(WidgetTestInteractive, CaptureAutoReset) { - Widget* toplevel = CreateTopLevelFramelessPlatformWidget(); - View* container = new View; - toplevel->SetContentsView(container); - - EXPECT_FALSE(toplevel->HasCapture()); - toplevel->SetCapture(nullptr); - EXPECT_TRUE(toplevel->HasCapture()); - - // By default, mouse release removes capture. - gfx::Point click_location(45, 15); - ui::MouseEvent release(ui::ET_MOUSE_RELEASED, click_location, click_location, - ui::EventTimeForNow(), ui::EF_LEFT_MOUSE_BUTTON, - ui::EF_LEFT_MOUSE_BUTTON); - toplevel->OnMouseEvent(&release); - EXPECT_FALSE(toplevel->HasCapture()); - - // Now a mouse release shouldn't remove capture. - toplevel->set_auto_release_capture(false); - toplevel->SetCapture(nullptr); - EXPECT_TRUE(toplevel->HasCapture()); - toplevel->OnMouseEvent(&release); - EXPECT_TRUE(toplevel->HasCapture()); - toplevel->ReleaseCapture(); - EXPECT_FALSE(toplevel->HasCapture()); - - toplevel->Close(); - RunPendingMessages(); -} - -TEST_F(WidgetTestInteractive, ResetCaptureOnGestureEnd) { - Widget* toplevel = CreateTopLevelFramelessPlatformWidget(); - View* container = new View; - toplevel->SetContentsView(container); - - View* gesture = new GestureCaptureView; - gesture->SetBounds(0, 0, 30, 30); - container->AddChildView(gesture); - - MouseView* mouse = new MouseView; - mouse->SetBounds(30, 0, 30, 30); - container->AddChildView(mouse); - - toplevel->SetSize(gfx::Size(100, 100)); - toplevel->Show(); - - // Start a gesture on |gesture|. - ui::GestureEvent tap_down(15, 15, 0, base::TimeTicks(), - ui::GestureEventDetails(ui::ET_GESTURE_TAP_DOWN)); - ui::GestureEvent end(15, 15, 0, base::TimeTicks(), - ui::GestureEventDetails(ui::ET_GESTURE_END)); - toplevel->OnGestureEvent(&tap_down); - - // Now try to click on |mouse|. Since |gesture| will have capture, |mouse| - // will not receive the event. - gfx::Point click_location(45, 15); - - ui::MouseEvent press(ui::ET_MOUSE_PRESSED, click_location, click_location, - ui::EventTimeForNow(), ui::EF_LEFT_MOUSE_BUTTON, - ui::EF_LEFT_MOUSE_BUTTON); - ui::MouseEvent release(ui::ET_MOUSE_RELEASED, click_location, click_location, - ui::EventTimeForNow(), ui::EF_LEFT_MOUSE_BUTTON, - ui::EF_LEFT_MOUSE_BUTTON); - - EXPECT_TRUE(toplevel->HasCapture()); - - toplevel->OnMouseEvent(&press); - toplevel->OnMouseEvent(&release); - EXPECT_EQ(0, mouse->pressed()); - - EXPECT_FALSE(toplevel->HasCapture()); - - // The end of the gesture should release the capture, and pressing on |mouse| - // should now reach |mouse|. - toplevel->OnGestureEvent(&end); - toplevel->OnMouseEvent(&press); - toplevel->OnMouseEvent(&release); - EXPECT_EQ(1, mouse->pressed()); - - toplevel->Close(); - RunPendingMessages(); -} - -// Checks that if a mouse-press triggers a capture on a different widget (which -// consumes the mouse-release event), then the target of the press does not have -// capture. -TEST_F(WidgetTestInteractive, DisableCaptureWidgetFromMousePress) { - // The test creates two widgets: |first| and |second|. - // The View in |first| makes |second| visible, sets capture on it, and starts - // a nested loop (like a menu does). The View in |second| terminates the - // nested loop and closes the widget. - // The test sends a mouse-press event to |first|, and posts a task to send a - // release event to |second|, to make sure that the release event is - // dispatched after the nested loop starts. - - Widget* first = CreateTopLevelFramelessPlatformWidget(); - Widget* second = CreateTopLevelFramelessPlatformWidget(); - - NestedLoopCaptureView* container = new NestedLoopCaptureView(second); - first->SetContentsView(container); - - second->SetContentsView(new ExitLoopOnRelease(container->GetQuitClosure())); - - first->SetSize(gfx::Size(100, 100)); - first->Show(); - - gfx::Point location(20, 20); - base::ThreadTaskRunnerHandle::Get()->PostTask( - FROM_HERE, - base::BindOnce( - &Widget::OnMouseEvent, base::Unretained(second), - base::Owned(new ui::MouseEvent( - ui::ET_MOUSE_RELEASED, location, location, ui::EventTimeForNow(), - ui::EF_LEFT_MOUSE_BUTTON, ui::EF_LEFT_MOUSE_BUTTON)))); - ui::MouseEvent press(ui::ET_MOUSE_PRESSED, location, location, - ui::EventTimeForNow(), ui::EF_LEFT_MOUSE_BUTTON, - ui::EF_LEFT_MOUSE_BUTTON); - first->OnMouseEvent(&press); - EXPECT_FALSE(first->HasCapture()); - first->Close(); - RunPendingMessages(); -} - -// Tests some grab/ungrab events. -// TODO(estade): can this be enabled now that this is an interactive ui test? -TEST_F(WidgetTestInteractive, DISABLED_GrabUngrab) { - Widget* toplevel = CreateTopLevelPlatformWidget(); - Widget* child1 = CreateChildNativeWidgetWithParent(toplevel); - Widget* child2 = CreateChildNativeWidgetWithParent(toplevel); - - toplevel->SetBounds(gfx::Rect(0, 0, 500, 500)); - - child1->SetBounds(gfx::Rect(10, 10, 300, 300)); - View* view = new MouseView(); - view->SetBounds(0, 0, 300, 300); - child1->GetRootView()->AddChildView(view); - - child2->SetBounds(gfx::Rect(200, 10, 200, 200)); - view = new MouseView(); - view->SetBounds(0, 0, 200, 200); - child2->GetRootView()->AddChildView(view); - - toplevel->Show(); - RunPendingMessages(); - - // Click on child1 - gfx::Point p1(45, 45); - ui::MouseEvent pressed(ui::ET_MOUSE_PRESSED, p1, p1, ui::EventTimeForNow(), - ui::EF_LEFT_MOUSE_BUTTON, ui::EF_LEFT_MOUSE_BUTTON); - toplevel->OnMouseEvent(&pressed); - - EXPECT_TRUE(toplevel->HasCapture()); - EXPECT_TRUE(child1->HasCapture()); - EXPECT_FALSE(child2->HasCapture()); - - ui::MouseEvent released(ui::ET_MOUSE_RELEASED, p1, p1, ui::EventTimeForNow(), - ui::EF_LEFT_MOUSE_BUTTON, ui::EF_LEFT_MOUSE_BUTTON); - toplevel->OnMouseEvent(&released); - - EXPECT_FALSE(toplevel->HasCapture()); - EXPECT_FALSE(child1->HasCapture()); - EXPECT_FALSE(child2->HasCapture()); - - RunPendingMessages(); - - // Click on child2 - gfx::Point p2(315, 45); - ui::MouseEvent pressed2(ui::ET_MOUSE_PRESSED, p2, p2, ui::EventTimeForNow(), - ui::EF_LEFT_MOUSE_BUTTON, ui::EF_LEFT_MOUSE_BUTTON); - toplevel->OnMouseEvent(&pressed2); - EXPECT_TRUE(pressed2.handled()); - EXPECT_TRUE(toplevel->HasCapture()); - EXPECT_TRUE(child2->HasCapture()); - EXPECT_FALSE(child1->HasCapture()); - - ui::MouseEvent released2(ui::ET_MOUSE_RELEASED, p2, p2, ui::EventTimeForNow(), - ui::EF_LEFT_MOUSE_BUTTON, ui::EF_LEFT_MOUSE_BUTTON); - toplevel->OnMouseEvent(&released2); - EXPECT_FALSE(toplevel->HasCapture()); - EXPECT_FALSE(child1->HasCapture()); - EXPECT_FALSE(child2->HasCapture()); - - toplevel->CloseNow(); -} - // Tests mouse move outside of the window into the "resize controller" and back // will still generate an OnMouseEntered and OnMouseExited event.. TEST_F(WidgetTestInteractive, CheckResizeControllerEvents) { - Widget* toplevel = CreateTopLevelFramelessPlatformWidget(); + WidgetAutoclosePtr toplevel(CreateTopLevelFramelessPlatformWidget()); toplevel->SetBounds(gfx::Rect(0, 0, 100, 100)); @@ -678,20 +486,16 @@ TEST_F(WidgetTestInteractive, CheckResizeControllerEvents) { toplevel->OnMouseEvent(&moved_over); EXPECT_EQ(1, view->EnteredCalls()); EXPECT_EQ(0, view->ExitedCalls()); - - RunPendingMessages(); - - toplevel->CloseNow(); } // Test view focus restoration when a widget is deactivated and re-activated. TEST_F(WidgetTestInteractive, ViewFocusOnWidgetActivationChanges) { - Widget* widget1 = CreateTopLevelPlatformWidget(); + WidgetAutoclosePtr widget1(CreateTopLevelPlatformWidget()); View* view1 = new View; view1->SetFocusBehavior(View::FocusBehavior::ALWAYS); widget1->GetContentsView()->AddChildView(view1); - Widget* widget2 = CreateTopLevelPlatformWidget(); + WidgetAutoclosePtr widget2(CreateTopLevelPlatformWidget()); View* view2a = new View; View* view2b = new View; view2a->SetFocusBehavior(View::FocusBehavior::ALWAYS); @@ -699,12 +503,12 @@ TEST_F(WidgetTestInteractive, ViewFocusOnWidgetActivationChanges) { widget2->GetContentsView()->AddChildView(view2a); widget2->GetContentsView()->AddChildView(view2b); - ShowSync(widget1); + ShowSync(widget1.get()); EXPECT_TRUE(widget1->IsActive()); view1->RequestFocus(); EXPECT_EQ(view1, widget1->GetFocusManager()->GetFocusedView()); - ShowSync(widget2); + ShowSync(widget2.get()); EXPECT_TRUE(widget2->IsActive()); EXPECT_FALSE(widget1->IsActive()); EXPECT_EQ(nullptr, widget1->GetFocusManager()->GetFocusedView()); @@ -713,20 +517,17 @@ TEST_F(WidgetTestInteractive, ViewFocusOnWidgetActivationChanges) { view2b->RequestFocus(); EXPECT_EQ(view2b, widget2->GetFocusManager()->GetFocusedView()); - ActivateSync(widget1); + ActivateSync(widget1.get()); EXPECT_TRUE(widget1->IsActive()); EXPECT_EQ(view1, widget1->GetFocusManager()->GetFocusedView()); EXPECT_FALSE(widget2->IsActive()); EXPECT_EQ(nullptr, widget2->GetFocusManager()->GetFocusedView()); - ActivateSync(widget2); + ActivateSync(widget2.get()); EXPECT_TRUE(widget2->IsActive()); EXPECT_EQ(view2b, widget2->GetFocusManager()->GetFocusedView()); EXPECT_FALSE(widget1->IsActive()); EXPECT_EQ(nullptr, widget1->GetFocusManager()->GetFocusedView()); - - widget1->CloseNow(); - widget2->CloseNow(); } // Test z-order of child widgets relative to their parent. @@ -801,7 +602,7 @@ TEST_F(WidgetTestInteractive, ChildStackedRelativeToParent) { // Test view focus retention when a widget's HWND is disabled and re-enabled. TEST_F(WidgetTestInteractive, ViewFocusOnHWNDEnabledChanges) { - Widget* widget = CreateTopLevelFramelessPlatformWidget(); + WidgetAutoclosePtr widget(CreateTopLevelFramelessPlatformWidget()); widget->SetContentsView(new View); for (size_t i = 0; i < 2; ++i) { auto child = std::make_unique<View>(); @@ -811,7 +612,7 @@ TEST_F(WidgetTestInteractive, ViewFocusOnHWNDEnabledChanges) { widget->Show(); widget->GetNativeWindow()->GetHost()->Show(); - const HWND hwnd = HWNDForWidget(widget); + const HWND hwnd = HWNDForWidget(widget.get()); EXPECT_TRUE(::IsWindow(hwnd)); EXPECT_TRUE(::IsWindowEnabled(hwnd)); EXPECT_EQ(hwnd, ::GetActiveWindow()); @@ -836,8 +637,6 @@ TEST_F(WidgetTestInteractive, ViewFocusOnHWNDEnabledChanges) { EXPECT_TRUE(widget->IsActive()); EXPECT_EQ(view, widget->GetFocusManager()->GetFocusedView()); } - - widget->CloseNow(); } // This class subclasses the Widget class to listen for activation change @@ -1089,61 +888,6 @@ TEST_F(DesktopWidgetTestInteractive, WindowModalWindowDestroyedActivationTest) { } #endif -// Disabled on Mac. Desktop Mac doesn't have system modal windows since Carbon -// was deprecated. It does have application modal windows, but only Ash requests -// those. -#if defined(OS_MACOSX) -#define MAYBE_SystemModalWindowReleasesCapture \ - DISABLED_SystemModalWindowReleasesCapture -#elif defined(OS_CHROMEOS) -// Investigate enabling for Chrome OS. It probably requires help from the window -// service. -#define MAYBE_SystemModalWindowReleasesCapture \ - DISABLED_SystemModalWindowReleasesCapture -#else -#define MAYBE_SystemModalWindowReleasesCapture SystemModalWindowReleasesCapture -#endif - -// Test that when opening a system-modal window, capture is released. -TEST_F(DesktopWidgetTestInteractive, MAYBE_SystemModalWindowReleasesCapture) { - TestWidgetFocusChangeListener focus_listener; - WidgetFocusManager::GetInstance()->AddFocusChangeListener(&focus_listener); - - // Create a top level widget. - Widget top_level_widget; - Widget::InitParams init_params = - CreateParams(Widget::InitParams::TYPE_WINDOW); - init_params.show_state = ui::SHOW_STATE_NORMAL; - gfx::Rect initial_bounds(0, 0, 500, 500); - init_params.bounds = initial_bounds; - init_params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; - top_level_widget.Init(std::move(init_params)); - ShowSync(&top_level_widget); - - ASSERT_FALSE(focus_listener.focus_changes().empty()); - EXPECT_EQ(top_level_widget.GetNativeView(), - focus_listener.focus_changes().back()); - - EXPECT_FALSE(top_level_widget.HasCapture()); - top_level_widget.SetCapture(nullptr); - EXPECT_TRUE(top_level_widget.HasCapture()); - - // Create a modal dialog. - ModalDialogDelegate* dialog_delegate = - new ModalDialogDelegate(ui::MODAL_TYPE_SYSTEM); - - Widget* modal_dialog_widget = views::DialogDelegate::CreateDialogWidget( - dialog_delegate, nullptr, top_level_widget.GetNativeView()); - modal_dialog_widget->SetBounds(gfx::Rect(100, 100, 200, 200)); - ShowSync(modal_dialog_widget); - - EXPECT_FALSE(top_level_widget.HasCapture()); - - modal_dialog_widget->CloseNow(); - top_level_widget.CloseNow(); - WidgetFocusManager::GetInstance()->RemoveFocusChangeListener(&focus_listener); -} - TEST_F(DesktopWidgetTestInteractive, CanActivateFlagIsHonored) { Widget widget; Widget::InitParams init_params = @@ -1171,7 +915,8 @@ TEST_F(DesktopWidgetTestInteractive, CanActivateFlagIsHonored) { // Test that touch selection quick menu is not activated when opened. TEST_F(DesktopWidgetTestInteractive, MAYBE_TouchSelectionQuickMenuIsNotActivated) { - Widget* widget = CreateWidget(); + WidgetAutoclosePtr widget(CreateTopLevelNativeWidget()); + widget->SetBounds(gfx::Rect(0, 0, 200, 200)); Textfield* textfield = new Textfield; textfield->SetBounds(0, 0, 200, 20); @@ -1185,16 +930,16 @@ TEST_F(DesktopWidgetTestInteractive, RunPendingMessages(); - ui::test::EventGenerator generator(GetRootWindow(widget)); + ui::test::EventGenerator generator(GetRootWindow(widget.get())); generator.GestureTapAt(textfield->GetBoundsInScreen().origin() + gfx::Vector2d(10, 10)); - ShowQuickMenuImmediately(static_cast<TouchSelectionControllerImpl*>( - textfield_test_api.touch_selection_controller())); + static_cast<TouchSelectionControllerImpl*>( + textfield_test_api.touch_selection_controller()) + ->ShowQuickMenuImmediatelyForTesting(); EXPECT_TRUE(textfield->HasFocus()); EXPECT_TRUE(widget->IsActive()); EXPECT_TRUE(ui::TouchSelectionMenuRunner::GetInstance()->IsRunning()); - widget->CloseNow(); } #endif // defined(USE_AURA) @@ -1253,90 +998,76 @@ TEST_F(WidgetTestInteractive, DisableViewDoesNotActivateWidget) { } // namespace test TEST_F(WidgetTestInteractive, ShowCreatesActiveWindow) { - Widget* widget = CreateTopLevelPlatformWidget(); - - ShowSync(widget); - EXPECT_EQ(GetWidgetShowState(widget), ui::SHOW_STATE_NORMAL); + WidgetAutoclosePtr widget(CreateTopLevelPlatformWidget()); - widget->CloseNow(); + ShowSync(widget.get()); + EXPECT_EQ(GetWidgetShowState(widget.get()), ui::SHOW_STATE_NORMAL); } TEST_F(WidgetTestInteractive, ShowInactive) { WidgetTest::WaitForSystemAppActivation(); - Widget* widget = CreateTopLevelPlatformWidget(); + WidgetAutoclosePtr widget(CreateTopLevelPlatformWidget()); - ShowInactiveSync(widget); - EXPECT_EQ(GetWidgetShowState(widget), ui::SHOW_STATE_INACTIVE); - - widget->CloseNow(); + ShowInactiveSync(widget.get()); + EXPECT_EQ(GetWidgetShowState(widget.get()), ui::SHOW_STATE_INACTIVE); } TEST_F(WidgetTestInteractive, InactiveBeforeShow) { - Widget* widget = CreateTopLevelPlatformWidget(); + WidgetAutoclosePtr widget(CreateTopLevelPlatformWidget()); EXPECT_FALSE(widget->IsActive()); EXPECT_FALSE(widget->IsVisible()); - ShowSync(widget); + ShowSync(widget.get()); EXPECT_TRUE(widget->IsActive()); EXPECT_TRUE(widget->IsVisible()); - - widget->CloseNow(); } TEST_F(WidgetTestInteractive, ShowInactiveAfterShow) { // Create 2 widgets to ensure window layering does not change. - Widget* widget = CreateTopLevelPlatformWidget(); - Widget* widget2 = CreateTopLevelPlatformWidget(); + WidgetAutoclosePtr widget(CreateTopLevelPlatformWidget()); + WidgetAutoclosePtr widget2(CreateTopLevelPlatformWidget()); - ShowSync(widget2); + ShowSync(widget2.get()); EXPECT_FALSE(widget->IsActive()); EXPECT_TRUE(widget2->IsVisible()); EXPECT_TRUE(widget2->IsActive()); - ShowSync(widget); + ShowSync(widget.get()); EXPECT_TRUE(widget->IsActive()); EXPECT_FALSE(widget2->IsActive()); - ShowInactiveSync(widget); + ShowInactiveSync(widget.get()); EXPECT_TRUE(widget->IsActive()); EXPECT_FALSE(widget2->IsActive()); - EXPECT_EQ(GetWidgetShowState(widget), ui::SHOW_STATE_NORMAL); - - widget2->CloseNow(); - widget->CloseNow(); + EXPECT_EQ(GetWidgetShowState(widget.get()), ui::SHOW_STATE_NORMAL); } TEST_F(WidgetTestInteractive, ShowAfterShowInactive) { - Widget* widget = CreateTopLevelPlatformWidget(); + WidgetAutoclosePtr widget(CreateTopLevelPlatformWidget()); widget->SetBounds(gfx::Rect(100, 100, 100, 100)); - ShowInactiveSync(widget); - ShowSync(widget); - EXPECT_EQ(GetWidgetShowState(widget), ui::SHOW_STATE_NORMAL); - - widget->CloseNow(); + ShowInactiveSync(widget.get()); + ShowSync(widget.get()); + EXPECT_EQ(GetWidgetShowState(widget.get()), ui::SHOW_STATE_NORMAL); } #if BUILDFLAG(ENABLE_DESKTOP_AURA) || defined(OS_MACOSX) TEST_F(WidgetTestInteractive, InactiveWidgetDoesNotGrabActivation) { - Widget* widget = CreateTopLevelPlatformWidget(); - ShowSync(widget); - EXPECT_EQ(GetWidgetShowState(widget), ui::SHOW_STATE_NORMAL); + WidgetAutoclosePtr widget(CreateTopLevelPlatformWidget()); + ShowSync(widget.get()); + EXPECT_EQ(GetWidgetShowState(widget.get()), ui::SHOW_STATE_NORMAL); - Widget widget2; + WidgetAutoclosePtr widget2(new Widget()); Widget::InitParams params = CreateParams(Widget::InitParams::TYPE_POPUP); params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; - widget2.Init(std::move(params)); - widget2.Show(); + widget2->Init(std::move(params)); + widget2->Show(); RunPendingMessagesForActiveStatusChange(); - EXPECT_EQ(GetWidgetShowState(&widget2), ui::SHOW_STATE_INACTIVE); - EXPECT_EQ(GetWidgetShowState(widget), ui::SHOW_STATE_NORMAL); - - widget->CloseNow(); - widget2.CloseNow(); + EXPECT_EQ(GetWidgetShowState(widget2.get()), ui::SHOW_STATE_INACTIVE); + EXPECT_EQ(GetWidgetShowState(widget.get()), ui::SHOW_STATE_NORMAL); } #endif // BUILDFLAG(ENABLE_DESKTOP_AURA) || defined(OS_MACOSX) @@ -1352,37 +1083,33 @@ TEST_F(WidgetTestInteractive, InactiveWidgetDoesNotGrabActivation) { // Test that window state is not changed after getting out of full screen. TEST_F(WidgetTestInteractive, MAYBE_ExitFullscreenRestoreState) { - Widget* toplevel = CreateTopLevelPlatformWidget(); + WidgetAutoclosePtr toplevel(CreateTopLevelPlatformWidget()); toplevel->Show(); RunPendingMessages(); // This should be a normal state window. - EXPECT_EQ(ui::SHOW_STATE_NORMAL, GetWidgetShowState(toplevel)); + EXPECT_EQ(ui::SHOW_STATE_NORMAL, GetWidgetShowState(toplevel.get())); toplevel->SetFullscreen(true); - EXPECT_EQ(ui::SHOW_STATE_FULLSCREEN, GetWidgetShowState(toplevel)); + EXPECT_EQ(ui::SHOW_STATE_FULLSCREEN, GetWidgetShowState(toplevel.get())); toplevel->SetFullscreen(false); - EXPECT_NE(ui::SHOW_STATE_FULLSCREEN, GetWidgetShowState(toplevel)); + EXPECT_NE(ui::SHOW_STATE_FULLSCREEN, GetWidgetShowState(toplevel.get())); // And it should still be in normal state after getting out of full screen. - EXPECT_EQ(ui::SHOW_STATE_NORMAL, GetWidgetShowState(toplevel)); + EXPECT_EQ(ui::SHOW_STATE_NORMAL, GetWidgetShowState(toplevel.get())); // Now, make it maximized. toplevel->Maximize(); - EXPECT_EQ(ui::SHOW_STATE_MAXIMIZED, GetWidgetShowState(toplevel)); + EXPECT_EQ(ui::SHOW_STATE_MAXIMIZED, GetWidgetShowState(toplevel.get())); toplevel->SetFullscreen(true); - EXPECT_EQ(ui::SHOW_STATE_FULLSCREEN, GetWidgetShowState(toplevel)); + EXPECT_EQ(ui::SHOW_STATE_FULLSCREEN, GetWidgetShowState(toplevel.get())); toplevel->SetFullscreen(false); - EXPECT_NE(ui::SHOW_STATE_FULLSCREEN, GetWidgetShowState(toplevel)); + EXPECT_NE(ui::SHOW_STATE_FULLSCREEN, GetWidgetShowState(toplevel.get())); // And it stays maximized after getting out of full screen. - EXPECT_EQ(ui::SHOW_STATE_MAXIMIZED, GetWidgetShowState(toplevel)); - - // Clean up. - toplevel->Close(); - RunPendingMessages(); + EXPECT_EQ(ui::SHOW_STATE_MAXIMIZED, GetWidgetShowState(toplevel.get())); } // Testing initial focus is assigned properly for normal top-level widgets, @@ -1412,23 +1139,21 @@ TEST_F(WidgetTestInteractive, InitialFocus) { } TEST_F(DesktopWidgetTestInteractive, RestoreAfterMinimize) { - Widget* widget = CreateWidget(); - ShowSync(widget); + WidgetAutoclosePtr widget(CreateTopLevelNativeWidget()); + ShowSync(widget.get()); ASSERT_FALSE(widget->IsMinimized()); PropertyWaiter minimize_waiter( - base::BindRepeating(&Widget::IsMinimized, base::Unretained(widget)), + base::BindRepeating(&Widget::IsMinimized, base::Unretained(widget.get())), true); widget->Minimize(); EXPECT_TRUE(minimize_waiter.Wait()); PropertyWaiter restore_waiter( - base::BindRepeating(&Widget::IsMinimized, base::Unretained(widget)), + base::BindRepeating(&Widget::IsMinimized, base::Unretained(widget.get())), false); widget->Restore(); EXPECT_TRUE(restore_waiter.Wait()); - - widget->CloseNow(); } #if defined(OS_WIN) @@ -1437,14 +1162,14 @@ TEST_F(DesktopWidgetTestInteractive, RestoreAfterMinimize) { // Tests that root window visibility toggles correctly when the desktop widget // is minimized and maximized on Windows, and the Widget remains visible. TEST_F(DesktopWidgetTestInteractive, RestoreAndMinimizeVisibility) { - Widget* widget = CreateWidget(); - aura::Window* root_window = GetRootWindow(widget); - ShowSync(widget); + WidgetAutoclosePtr widget(CreateTopLevelNativeWidget()); + aura::Window* root_window = GetRootWindow(widget.get()); + ShowSync(widget.get()); ASSERT_FALSE(widget->IsMinimized()); EXPECT_TRUE(root_window->IsVisible()); PropertyWaiter minimize_widget_waiter( - base::BindRepeating(&Widget::IsMinimized, base::Unretained(widget)), + base::BindRepeating(&Widget::IsMinimized, base::Unretained(widget.get())), true); widget->Minimize(); EXPECT_TRUE(minimize_widget_waiter.Wait()); @@ -1452,22 +1177,21 @@ TEST_F(DesktopWidgetTestInteractive, RestoreAndMinimizeVisibility) { EXPECT_FALSE(root_window->IsVisible()); PropertyWaiter restore_widget_waiter( - base::BindRepeating(&Widget::IsMinimized, base::Unretained(widget)), + base::BindRepeating(&Widget::IsMinimized, base::Unretained(widget.get())), false); widget->Restore(); EXPECT_TRUE(restore_widget_waiter.Wait()); EXPECT_TRUE(widget->IsVisible()); EXPECT_TRUE(root_window->IsVisible()); - widget->CloseNow(); } // Test that focus is restored to the widget after a minimized window // is activated. TEST_F(DesktopWidgetTestInteractive, MinimizeAndActivateFocus) { - Widget* widget = CreateWidget(); - aura::Window* root_window = GetRootWindow(widget); + WidgetAutoclosePtr widget(CreateTopLevelNativeWidget()); + aura::Window* root_window = GetRootWindow(widget.get()); auto* widget_window = widget->GetNativeWindow(); - ShowSync(widget); + ShowSync(widget.get()); ASSERT_FALSE(widget->IsMinimized()); EXPECT_TRUE(root_window->IsVisible()); widget_window->Focus(); @@ -1477,7 +1201,7 @@ TEST_F(DesktopWidgetTestInteractive, MinimizeAndActivateFocus) { EXPECT_TRUE(widget->GetContentsView()->HasFocus()); PropertyWaiter minimize_widget_waiter( - base::BindRepeating(&Widget::IsMinimized, base::Unretained(widget)), + base::BindRepeating(&Widget::IsMinimized, base::Unretained(widget.get())), true); widget->Minimize(); EXPECT_TRUE(minimize_widget_waiter.Wait()); @@ -1485,7 +1209,7 @@ TEST_F(DesktopWidgetTestInteractive, MinimizeAndActivateFocus) { EXPECT_FALSE(root_window->IsVisible()); PropertyWaiter restore_widget_waiter( - base::BindRepeating(&Widget::IsMinimized, base::Unretained(widget)), + base::BindRepeating(&Widget::IsMinimized, base::Unretained(widget.get())), false); widget->Activate(); EXPECT_TRUE(widget->GetContentsView()->HasFocus()); @@ -1493,7 +1217,6 @@ TEST_F(DesktopWidgetTestInteractive, MinimizeAndActivateFocus) { EXPECT_TRUE(widget->IsVisible()); EXPECT_TRUE(root_window->IsVisible()); EXPECT_TRUE(widget_window->CanFocus()); - widget->CloseNow(); } #endif // defined(OS_WIN) @@ -1502,8 +1225,8 @@ TEST_F(DesktopWidgetTestInteractive, MinimizeAndActivateFocus) { // Tests that minimizing a widget causes the gesture_handler // to be cleared when the widget is minimized. TEST_F(DesktopWidgetTestInteractive, EventHandlersClearedOnWidgetMinimize) { - Widget* widget = CreateWidget(); - ShowSync(widget); + WidgetAutoclosePtr widget(CreateTopLevelNativeWidget()); + ShowSync(widget.get()); ASSERT_FALSE(widget->IsMinimized()); View mouse_handler_view; internal::RootView* root_view = @@ -1515,8 +1238,6 @@ TEST_F(DesktopWidgetTestInteractive, EventHandlersClearedOnWidgetMinimize) { widget->Minimize(); EXPECT_FALSE(GetGestureHandler(root_view)); - - widget->CloseNow(); } #endif @@ -1527,16 +1248,16 @@ TEST_F(DesktopWidgetTestInteractive, EventHandlersClearedOnWidgetMinimize) { TEST_F(DesktopWidgetTestInteractive, DesktopNativeWidgetWithModalTransientChild) { // Create a desktop native Widget for Widget::Deactivate(). - Widget* deactivate_widget = CreateWidget(); - ShowSync(deactivate_widget); + WidgetAutoclosePtr deactivate_widget(CreateTopLevelNativeWidget()); + ShowSync(deactivate_widget.get()); // Create a top level desktop native widget. - Widget* top_level = CreateWidget(); + WidgetAutoclosePtr top_level(CreateTopLevelNativeWidget()); Textfield* textfield = new Textfield; textfield->SetBounds(0, 0, 200, 20); top_level->GetRootView()->AddChildView(textfield); - ShowSync(top_level); + ShowSync(top_level.get()); textfield->RequestFocus(); EXPECT_TRUE(textfield->HasFocus()); @@ -1557,18 +1278,15 @@ TEST_F(DesktopWidgetTestInteractive, EXPECT_TRUE(dialog_textfield->HasFocus()); EXPECT_FALSE(textfield->HasFocus()); - DeactivateSync(top_level); + DeactivateSync(top_level.get()); EXPECT_FALSE(dialog_textfield->HasFocus()); EXPECT_FALSE(textfield->HasFocus()); // After deactivation and activation of top level widget, only modal dialog // should restore focused view. - ActivateSync(top_level); + ActivateSync(top_level.get()); EXPECT_TRUE(dialog_textfield->HasFocus()); EXPECT_FALSE(textfield->HasFocus()); - - top_level->CloseNow(); - deactivate_widget->CloseNow(); } #endif // defined(OS_LINUX) && BUILDFLAG(ENABLE_DESKTOP_AURA) @@ -1615,7 +1333,7 @@ class CaptureLostTrackingWidget : public Widget { } // namespace -class WidgetCaptureTest : public ViewsInteractiveUITestBase { +class WidgetCaptureTest : public DesktopWidgetTestInteractive { public: WidgetCaptureTest() = default; ~WidgetCaptureTest() override = default; @@ -1625,22 +1343,12 @@ class WidgetCaptureTest : public ViewsInteractiveUITestBase { void TestCapture(bool use_desktop_native_widget) { CaptureLostState capture_state1; CaptureLostTrackingWidget widget1(&capture_state1); - Widget::InitParams params1 = - CreateParams(views::Widget::InitParams::TYPE_WINDOW); - params1.native_widget = - CreateNativeWidget(params1, use_desktop_native_widget, &widget1); - params1.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; - widget1.Init(std::move(params1)); + InitPlatformWidget(&widget1, use_desktop_native_widget); widget1.Show(); CaptureLostState capture_state2; CaptureLostTrackingWidget widget2(&capture_state2); - Widget::InitParams params2 = - CreateParams(views::Widget::InitParams::TYPE_WINDOW); - params2.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; - params2.native_widget = - CreateNativeWidget(params2, use_desktop_native_widget, &widget2); - widget2.Init(std::move(params2)); + InitPlatformWidget(&widget2, use_desktop_native_widget); widget2.Show(); // Set capture to widget2 and verity it gets it. @@ -1665,13 +1373,16 @@ class WidgetCaptureTest : public ViewsInteractiveUITestBase { EXPECT_FALSE(capture_state2.GetAndClearGotCaptureLost()); } - NativeWidget* CreateNativeWidget(const Widget::InitParams& params, - bool create_desktop_native_widget, - Widget* widget) { - // The test base class by default returns DesktopNativeWidgetAura. - if (create_desktop_native_widget) - return nullptr; - return CreatePlatformNativeWidgetImpl(params, widget, kDefault, nullptr); + void InitPlatformWidget(Widget* widget, bool use_desktop_native_widget) { + Widget::InitParams params = + CreateParams(views::Widget::InitParams::TYPE_WINDOW); + // The test class by default returns DesktopNativeWidgetAura. + params.native_widget = + use_desktop_native_widget + ? nullptr + : CreatePlatformNativeWidgetImpl(widget, kDefault, nullptr); + params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; + widget->Init(std::move(params)); } private: @@ -1767,6 +1478,243 @@ TEST_F(WidgetCaptureTest, FailedCaptureRequestIsNoop) { EXPECT_TRUE(mouse_view2->pressed()); } +TEST_F(WidgetCaptureTest, CaptureAutoReset) { + WidgetAutoclosePtr toplevel(CreateTopLevelFramelessPlatformWidget()); + View* container = new View; + toplevel->SetContentsView(container); + + EXPECT_FALSE(toplevel->HasCapture()); + toplevel->SetCapture(nullptr); + EXPECT_TRUE(toplevel->HasCapture()); + + // By default, mouse release removes capture. + gfx::Point click_location(45, 15); + ui::MouseEvent release(ui::ET_MOUSE_RELEASED, click_location, click_location, + ui::EventTimeForNow(), ui::EF_LEFT_MOUSE_BUTTON, + ui::EF_LEFT_MOUSE_BUTTON); + toplevel->OnMouseEvent(&release); + EXPECT_FALSE(toplevel->HasCapture()); + + // Now a mouse release shouldn't remove capture. + toplevel->set_auto_release_capture(false); + toplevel->SetCapture(nullptr); + EXPECT_TRUE(toplevel->HasCapture()); + toplevel->OnMouseEvent(&release); + EXPECT_TRUE(toplevel->HasCapture()); + toplevel->ReleaseCapture(); + EXPECT_FALSE(toplevel->HasCapture()); +} + +TEST_F(WidgetCaptureTest, ResetCaptureOnGestureEnd) { + WidgetAutoclosePtr toplevel(CreateTopLevelFramelessPlatformWidget()); + View* container = new View; + toplevel->SetContentsView(container); + + View* gesture = new GestureCaptureView; + gesture->SetBounds(0, 0, 30, 30); + container->AddChildView(gesture); + + MouseView* mouse = new MouseView; + mouse->SetBounds(30, 0, 30, 30); + container->AddChildView(mouse); + + toplevel->SetSize(gfx::Size(100, 100)); + toplevel->Show(); + + // Start a gesture on |gesture|. + ui::GestureEvent tap_down(15, 15, 0, base::TimeTicks(), + ui::GestureEventDetails(ui::ET_GESTURE_TAP_DOWN)); + ui::GestureEvent end(15, 15, 0, base::TimeTicks(), + ui::GestureEventDetails(ui::ET_GESTURE_END)); + toplevel->OnGestureEvent(&tap_down); + + // Now try to click on |mouse|. Since |gesture| will have capture, |mouse| + // will not receive the event. + gfx::Point click_location(45, 15); + + ui::MouseEvent press(ui::ET_MOUSE_PRESSED, click_location, click_location, + ui::EventTimeForNow(), ui::EF_LEFT_MOUSE_BUTTON, + ui::EF_LEFT_MOUSE_BUTTON); + ui::MouseEvent release(ui::ET_MOUSE_RELEASED, click_location, click_location, + ui::EventTimeForNow(), ui::EF_LEFT_MOUSE_BUTTON, + ui::EF_LEFT_MOUSE_BUTTON); + + EXPECT_TRUE(toplevel->HasCapture()); + + toplevel->OnMouseEvent(&press); + toplevel->OnMouseEvent(&release); + EXPECT_EQ(0, mouse->pressed()); + + EXPECT_FALSE(toplevel->HasCapture()); + + // The end of the gesture should release the capture, and pressing on |mouse| + // should now reach |mouse|. + toplevel->OnGestureEvent(&end); + toplevel->OnMouseEvent(&press); + toplevel->OnMouseEvent(&release); + EXPECT_EQ(1, mouse->pressed()); +} + +// Checks that if a mouse-press triggers a capture on a different widget (which +// consumes the mouse-release event), then the target of the press does not have +// capture. +TEST_F(WidgetCaptureTest, DisableCaptureWidgetFromMousePress) { + // The test creates two widgets: |first| and |second|. + // The View in |first| makes |second| visible, sets capture on it, and starts + // a nested loop (like a menu does). The View in |second| terminates the + // nested loop and closes the widget. + // The test sends a mouse-press event to |first|, and posts a task to send a + // release event to |second|, to make sure that the release event is + // dispatched after the nested loop starts. + + WidgetAutoclosePtr first(CreateTopLevelFramelessPlatformWidget()); + Widget* second = CreateTopLevelFramelessPlatformWidget(); + + NestedLoopCaptureView* container = new NestedLoopCaptureView(second); + first->SetContentsView(container); + + second->SetContentsView(new ExitLoopOnRelease(container->GetQuitClosure())); + + first->SetSize(gfx::Size(100, 100)); + first->Show(); + + gfx::Point location(20, 20); + base::ThreadTaskRunnerHandle::Get()->PostTask( + FROM_HERE, + base::BindOnce( + &Widget::OnMouseEvent, base::Unretained(second), + base::Owned(new ui::MouseEvent( + ui::ET_MOUSE_RELEASED, location, location, ui::EventTimeForNow(), + ui::EF_LEFT_MOUSE_BUTTON, ui::EF_LEFT_MOUSE_BUTTON)))); + ui::MouseEvent press(ui::ET_MOUSE_PRESSED, location, location, + ui::EventTimeForNow(), ui::EF_LEFT_MOUSE_BUTTON, + ui::EF_LEFT_MOUSE_BUTTON); + first->OnMouseEvent(&press); + EXPECT_FALSE(first->HasCapture()); +} + +// Tests some grab/ungrab events. Only one Widget can have capture at any given +// time. +TEST_F(WidgetCaptureTest, GrabUngrab) { + auto top_level = CreateTestWidget(); + top_level->SetContentsView(new MouseView()); + + Widget* child1 = new Widget; + Widget::InitParams params1 = CreateParams(Widget::InitParams::TYPE_CONTROL); + params1.parent = top_level->GetNativeView(); + params1.bounds = gfx::Rect(10, 10, 100, 100); + child1->Init(std::move(params1)); + child1->SetContentsView(new MouseView()); + + Widget* child2 = new Widget; + Widget::InitParams params2 = CreateParams(Widget::InitParams::TYPE_CONTROL); + params2.parent = top_level->GetNativeView(); + params2.bounds = gfx::Rect(110, 10, 100, 100); + child2->Init(std::move(params2)); + child2->SetContentsView(new MouseView()); + + top_level->Show(); + RunPendingMessages(); + + // Click on child1. + ui::test::EventGenerator generator(GetRootWindow(top_level.get()), + child1->GetNativeWindow()); + generator.PressLeftButton(); + + EXPECT_FALSE(top_level->HasCapture()); + EXPECT_TRUE(child1->HasCapture()); + EXPECT_FALSE(child2->HasCapture()); + + generator.ReleaseLeftButton(); + EXPECT_FALSE(top_level->HasCapture()); + EXPECT_FALSE(child1->HasCapture()); + EXPECT_FALSE(child2->HasCapture()); + + // Click on child2. + generator.SetTargetWindow(child2->GetNativeWindow()); + generator.set_current_screen_location( + generator.delegate()->CenterOfWindow(child2->GetNativeWindow())); + generator.PressLeftButton(); + + EXPECT_FALSE(top_level->HasCapture()); + EXPECT_FALSE(child1->HasCapture()); + EXPECT_TRUE(child2->HasCapture()); + + generator.ReleaseLeftButton(); + EXPECT_FALSE(top_level->HasCapture()); + EXPECT_FALSE(child1->HasCapture()); + EXPECT_FALSE(child2->HasCapture()); + + // Click on top_level. + generator.SetTargetWindow(top_level->GetNativeWindow()); + generator.set_current_screen_location(gfx::Point()); + generator.PressLeftButton(); + + EXPECT_TRUE(top_level->HasCapture()); + EXPECT_FALSE(child1->HasCapture()); + EXPECT_FALSE(child2->HasCapture()); + + generator.ReleaseLeftButton(); + EXPECT_FALSE(top_level->HasCapture()); + EXPECT_FALSE(child1->HasCapture()); + EXPECT_FALSE(child2->HasCapture()); +} + +// Disabled on Mac. Desktop Mac doesn't have system modal windows since Carbon +// was deprecated. It does have application modal windows, but only Ash requests +// those. +#if defined(OS_MACOSX) +#define MAYBE_SystemModalWindowReleasesCapture \ + DISABLED_SystemModalWindowReleasesCapture +#elif defined(OS_CHROMEOS) +// Investigate enabling for Chrome OS. It probably requires help from the window +// service. +#define MAYBE_SystemModalWindowReleasesCapture \ + DISABLED_SystemModalWindowReleasesCapture +#else +#define MAYBE_SystemModalWindowReleasesCapture SystemModalWindowReleasesCapture +#endif + +// Test that when opening a system-modal window, capture is released. +TEST_F(WidgetCaptureTest, MAYBE_SystemModalWindowReleasesCapture) { + TestWidgetFocusChangeListener focus_listener; + WidgetFocusManager::GetInstance()->AddFocusChangeListener(&focus_listener); + + // Create a top level widget. + Widget top_level_widget; + Widget::InitParams init_params = + CreateParams(Widget::InitParams::TYPE_WINDOW); + init_params.show_state = ui::SHOW_STATE_NORMAL; + gfx::Rect initial_bounds(0, 0, 500, 500); + init_params.bounds = initial_bounds; + init_params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; + top_level_widget.Init(std::move(init_params)); + ShowSync(&top_level_widget); + + ASSERT_FALSE(focus_listener.focus_changes().empty()); + EXPECT_EQ(top_level_widget.GetNativeView(), + focus_listener.focus_changes().back()); + + EXPECT_FALSE(top_level_widget.HasCapture()); + top_level_widget.SetCapture(nullptr); + EXPECT_TRUE(top_level_widget.HasCapture()); + + // Create a modal dialog. + ModalDialogDelegate* dialog_delegate = + new ModalDialogDelegate(ui::MODAL_TYPE_SYSTEM); + + Widget* modal_dialog_widget = views::DialogDelegate::CreateDialogWidget( + dialog_delegate, nullptr, top_level_widget.GetNativeView()); + modal_dialog_widget->SetBounds(gfx::Rect(100, 100, 200, 200)); + ShowSync(modal_dialog_widget); + + EXPECT_FALSE(top_level_widget.HasCapture()); + + modal_dialog_widget->CloseNow(); + top_level_widget.CloseNow(); + WidgetFocusManager::GetInstance()->RemoveFocusChangeListener(&focus_listener); +} + // Regression test for http://crbug.com/382421 (Linux-Aura issue). // TODO(pkotwicz): Make test pass on CrOS and Windows. // TODO(tapted): Investigate for toolkit-views on Mac http;//crbug.com/441064. @@ -1952,7 +1900,7 @@ class WidgetInputMethodInteractiveTest : public DesktopWidgetTestInteractive { // On Windows, Widget::Deactivate() works by activating the next topmost // window on the z-order stack. This only works if there is at least one // other window, so make sure that is the case. - deactivate_widget_ = CreateWidget(); + deactivate_widget_ = CreateTopLevelNativeWidget(); deactivate_widget_->Show(); #endif } @@ -1976,33 +1924,32 @@ class WidgetInputMethodInteractiveTest : public DesktopWidgetTestInteractive { #endif // Test input method focus changes affected by top window activaction. TEST_F(WidgetInputMethodInteractiveTest, MAYBE_Activation) { - Widget* widget = CreateWidget(); + WidgetAutoclosePtr widget(CreateTopLevelNativeWidget()); Textfield* textfield = new Textfield; widget->GetRootView()->AddChildView(textfield); textfield->RequestFocus(); - ShowSync(widget); + ShowSync(widget.get()); EXPECT_EQ(ui::TEXT_INPUT_TYPE_TEXT, widget->GetInputMethod()->GetTextInputType()); - DeactivateSync(widget); + DeactivateSync(widget.get()); EXPECT_EQ(ui::TEXT_INPUT_TYPE_NONE, widget->GetInputMethod()->GetTextInputType()); - widget->CloseNow(); } // Test input method focus changes affected by focus changes within 1 window. TEST_F(WidgetInputMethodInteractiveTest, OneWindow) { - Widget* widget = CreateWidget(); + WidgetAutoclosePtr widget(CreateTopLevelNativeWidget()); Textfield* textfield1 = new Textfield; Textfield* textfield2 = new Textfield; textfield2->SetTextInputType(ui::TEXT_INPUT_TYPE_PASSWORD); widget->GetRootView()->AddChildView(textfield1); widget->GetRootView()->AddChildView(textfield2); - ShowSync(widget); + ShowSync(widget.get()); textfield1->RequestFocus(); EXPECT_EQ(ui::TEXT_INPUT_TYPE_TEXT, @@ -2016,31 +1963,30 @@ TEST_F(WidgetInputMethodInteractiveTest, OneWindow) { // DNWA (which just activates the last active window) and involves the // AuraTestHelper which sets the input method as DummyInputMethod. #if BUILDFLAG(ENABLE_DESKTOP_AURA) || defined(OS_MACOSX) - DeactivateSync(widget); + DeactivateSync(widget.get()); EXPECT_EQ(ui::TEXT_INPUT_TYPE_NONE, widget->GetInputMethod()->GetTextInputType()); - ActivateSync(widget); + ActivateSync(widget.get()); EXPECT_EQ(ui::TEXT_INPUT_TYPE_PASSWORD, widget->GetInputMethod()->GetTextInputType()); - DeactivateSync(widget); + DeactivateSync(widget.get()); textfield1->RequestFocus(); - ActivateSync(widget); + ActivateSync(widget.get()); EXPECT_TRUE(widget->IsActive()); EXPECT_EQ(ui::TEXT_INPUT_TYPE_TEXT, widget->GetInputMethod()->GetTextInputType()); #endif - widget->CloseNow(); } // Test input method focus changes affected by focus changes cross 2 windows // which shares the same top window. TEST_F(WidgetInputMethodInteractiveTest, TwoWindows) { - Widget* parent = CreateWidget(); + WidgetAutoclosePtr parent(CreateTopLevelNativeWidget()); parent->SetBounds(gfx::Rect(100, 100, 100, 100)); - Widget* child = CreateChildNativeWidgetWithParent(parent); + Widget* child = CreateChildNativeWidgetWithParent(parent.get()); child->SetBounds(gfx::Rect(0, 0, 50, 50)); child->Show(); @@ -2049,7 +1995,7 @@ TEST_F(WidgetInputMethodInteractiveTest, TwoWindows) { textfield_parent->SetTextInputType(ui::TEXT_INPUT_TYPE_PASSWORD); parent->GetRootView()->AddChildView(textfield_parent); child->GetRootView()->AddChildView(textfield_child); - ShowSync(parent); + ShowSync(parent.get()); EXPECT_EQ(parent->GetInputMethod(), child->GetInputMethod()); @@ -2065,33 +2011,31 @@ TEST_F(WidgetInputMethodInteractiveTest, TwoWindows) { // DNWA (which just activates the last active window) and involves the // AuraTestHelper which sets the input method as DummyInputMethod. #if BUILDFLAG(ENABLE_DESKTOP_AURA) || defined(OS_MACOSX) - DeactivateSync(parent); + DeactivateSync(parent.get()); EXPECT_EQ(ui::TEXT_INPUT_TYPE_NONE, parent->GetInputMethod()->GetTextInputType()); - ActivateSync(parent); + ActivateSync(parent.get()); EXPECT_EQ(ui::TEXT_INPUT_TYPE_TEXT, parent->GetInputMethod()->GetTextInputType()); textfield_parent->RequestFocus(); - DeactivateSync(parent); + DeactivateSync(parent.get()); EXPECT_EQ(ui::TEXT_INPUT_TYPE_NONE, parent->GetInputMethod()->GetTextInputType()); - ActivateSync(parent); + ActivateSync(parent.get()); EXPECT_EQ(ui::TEXT_INPUT_TYPE_PASSWORD, parent->GetInputMethod()->GetTextInputType()); #endif - - parent->CloseNow(); } // Test input method focus changes affected by textfield's state changes. TEST_F(WidgetInputMethodInteractiveTest, TextField) { - Widget* widget = CreateWidget(); + WidgetAutoclosePtr widget(CreateTopLevelNativeWidget()); Textfield* textfield = new Textfield; widget->GetRootView()->AddChildView(textfield); - ShowSync(widget); + ShowSync(widget.get()); EXPECT_EQ(ui::TEXT_INPUT_TYPE_NONE, widget->GetInputMethod()->GetTextInputType()); @@ -2110,15 +2054,14 @@ TEST_F(WidgetInputMethodInteractiveTest, TextField) { textfield->SetReadOnly(true); EXPECT_EQ(ui::TEXT_INPUT_TYPE_NONE, widget->GetInputMethod()->GetTextInputType()); - widget->CloseNow(); } // Test input method should not work for accelerator. TEST_F(WidgetInputMethodInteractiveTest, AcceleratorInTextfield) { - Widget* widget = CreateWidget(); + WidgetAutoclosePtr widget(CreateTopLevelNativeWidget()); Textfield* textfield = new Textfield; widget->GetRootView()->AddChildView(textfield); - ShowSync(widget); + ShowSync(widget.get()); textfield->SetTextInputType(ui::TEXT_INPUT_TYPE_TEXT); textfield->RequestFocus(); @@ -2135,8 +2078,6 @@ TEST_F(WidgetInputMethodInteractiveTest, AcceleratorInTextfield) { ui::KeyEvent key_event2(key_event); widget->OnKeyEvent(&key_event2); EXPECT_FALSE(key_event2.stopped_propagation()); - - widget->CloseNow(); } } // namespace test diff --git a/chromium/ui/views/widget/widget_unittest.cc b/chromium/ui/views/widget/widget_unittest.cc index e932006a39a..469e9150908 100644 --- a/chromium/ui/views/widget/widget_unittest.cc +++ b/chromium/ui/views/widget/widget_unittest.cc @@ -343,79 +343,6 @@ class OwnershipTestWidget : public Widget { // TODO(sky): add coverage of ownership for the desktop variants. -// Widget owns its NativeWidget, part 1: NativeWidget is a platform-native -// widget. -TEST_F(WidgetOwnershipTest, Ownership_WidgetOwnsPlatformNativeWidget) { - OwnershipTestState state; - - auto widget = std::make_unique<OwnershipTestWidget>(&state); - Widget::InitParams params = CreateParamsForTestWidget(); - params.native_widget = CreatePlatformNativeWidgetImpl( - params, widget.get(), kStubCapture, &state.native_widget_deleted); - widget->Init(std::move(params)); - - // Now delete the Widget, which should delete the NativeWidget. - widget.reset(); - - EXPECT_TRUE(state.widget_deleted); - EXPECT_TRUE(state.native_widget_deleted); - - // TODO(beng): write test for this ownership scenario and the NativeWidget - // being deleted out from under the Widget. -} - -// Widget owns its NativeWidget, part 2: NativeWidget is a NativeWidget. -TEST_F(WidgetOwnershipTest, Ownership_WidgetOwnsViewsNativeWidget) { - OwnershipTestState state; - - auto widget = std::make_unique<OwnershipTestWidget>(&state); - Widget::InitParams params = CreateParamsForTestWidget(); - params.native_widget = CreatePlatformNativeWidgetImpl( - params, widget.get(), kStubCapture, &state.native_widget_deleted); - widget->Init(std::move(params)); - - // Now delete the Widget, which should delete the NativeWidget. - widget.reset(); - - EXPECT_TRUE(state.widget_deleted); - EXPECT_TRUE(state.native_widget_deleted); - - // TODO(beng): write test for this ownership scenario and the NativeWidget - // being deleted out from under the Widget. -} - -// Widget owns its NativeWidget, part 3: NativeWidget is a NativeWidget, -// destroy the parent view. -TEST_F(WidgetOwnershipTest, - Ownership_WidgetOwnsViewsNativeWidget_DestroyParentView) { - OwnershipTestState state; - - Widget* toplevel = CreateTopLevelPlatformWidget(); - - auto widget = std::make_unique<OwnershipTestWidget>(&state); - Widget::InitParams params = CreateParamsForTestWidget(); - params.parent = toplevel->GetNativeView(); - params.native_widget = CreatePlatformNativeWidgetImpl( - params, widget.get(), kStubCapture, &state.native_widget_deleted); - widget->Init(std::move(params)); - - // Now close the toplevel, which deletes the view hierarchy. - toplevel->CloseNow(); - - RunPendingMessages(); - - // This shouldn't delete the widget because it shouldn't be deleted - // from the native side. - EXPECT_FALSE(state.widget_deleted); - EXPECT_FALSE(state.native_widget_deleted); - - // Now delete it explicitly. - widget.reset(); - - EXPECT_TRUE(state.widget_deleted); - EXPECT_TRUE(state.native_widget_deleted); -} - // NativeWidget owns its Widget, part 1: NativeWidget is a platform-native // widget. TEST_F(WidgetOwnershipTest, Ownership_PlatformNativeWidgetOwnsWidget) { @@ -424,7 +351,7 @@ TEST_F(WidgetOwnershipTest, Ownership_PlatformNativeWidgetOwnsWidget) { Widget* widget = new OwnershipTestWidget(&state); Widget::InitParams params = CreateParams(Widget::InitParams::TYPE_POPUP); params.native_widget = CreatePlatformNativeWidgetImpl( - params, widget, kStubCapture, &state.native_widget_deleted); + widget, kStubCapture, &state.native_widget_deleted); widget->Init(std::move(params)); // Now destroy the native widget. @@ -444,7 +371,7 @@ TEST_F(WidgetOwnershipTest, Ownership_ViewsNativeWidgetOwnsWidget) { Widget::InitParams params = CreateParams(Widget::InitParams::TYPE_POPUP); params.parent = toplevel->GetNativeView(); params.native_widget = CreatePlatformNativeWidgetImpl( - params, widget, kStubCapture, &state.native_widget_deleted); + widget, kStubCapture, &state.native_widget_deleted); widget->Init(std::move(params)); // Now destroy the native widget. This is achieved by closing the toplevel. @@ -467,7 +394,7 @@ TEST_F(WidgetOwnershipTest, Widget* widget = new OwnershipTestWidget(&state); Widget::InitParams params = CreateParams(Widget::InitParams::TYPE_POPUP); params.native_widget = CreatePlatformNativeWidgetImpl( - params, widget, kStubCapture, &state.native_widget_deleted); + widget, kStubCapture, &state.native_widget_deleted); widget->Init(std::move(params)); // Now simulate a destroy of the platform native widget from the OS: @@ -489,7 +416,7 @@ TEST_F(WidgetOwnershipTest, Widget::InitParams params = CreateParams(Widget::InitParams::TYPE_POPUP); params.parent = toplevel->GetNativeView(); params.native_widget = CreatePlatformNativeWidgetImpl( - params, widget, kStubCapture, &state.native_widget_deleted); + widget, kStubCapture, &state.native_widget_deleted); widget->Init(std::move(params)); // Destroy the widget (achieved by closing the toplevel). @@ -514,7 +441,7 @@ TEST_F(WidgetOwnershipTest, Ownership_ViewsNativeWidgetOwnsWidget_Close) { Widget::InitParams params = CreateParams(Widget::InitParams::TYPE_POPUP); params.parent = toplevel->GetNativeView(); params.native_widget = CreatePlatformNativeWidgetImpl( - params, widget, kStubCapture, &state.native_widget_deleted); + widget, kStubCapture, &state.native_widget_deleted); widget->Init(std::move(params)); // Destroy the widget. @@ -529,26 +456,72 @@ TEST_F(WidgetOwnershipTest, Ownership_ViewsNativeWidgetOwnsWidget_Close) { EXPECT_TRUE(state.native_widget_deleted); } -// Widget owns its NativeWidget and has a WidgetDelegateView as its contents. -TEST_F(WidgetOwnershipTest, - Ownership_WidgetOwnsNativeWidgetWithWithWidgetDelegateView) { - OwnershipTestState state; +class WidgetOwnsNativeWidgetTest : public WidgetOwnershipTest { + public: + WidgetOwnsNativeWidgetTest() = default; + ~WidgetOwnsNativeWidgetTest() override = default; + + void TearDown() override { + EXPECT_TRUE(state_.widget_deleted); + EXPECT_TRUE(state_.native_widget_deleted); - WidgetDelegateView* delegate_view = new WidgetDelegateView; + WidgetOwnershipTest::TearDown(); + } + + OwnershipTestState* state() { return &state_; } - auto widget = std::make_unique<OwnershipTestWidget>(&state); + private: + OwnershipTestState state_; +}; + +// Widget owns its NativeWidget, part 1. +TEST_F(WidgetOwnsNativeWidgetTest, Ownership) { + auto widget = std::make_unique<OwnershipTestWidget>(state()); Widget::InitParams params = CreateParamsForTestWidget(); params.native_widget = CreatePlatformNativeWidgetImpl( - params, widget.get(), kStubCapture, &state.native_widget_deleted); - params.delegate = delegate_view; + widget.get(), kStubCapture, &state()->native_widget_deleted); widget->Init(std::move(params)); - widget->SetContentsView(delegate_view); - // Now delete the Widget. There should be no crash or use-after-free. + // Now delete the Widget, which should delete the NativeWidget. widget.reset(); - EXPECT_TRUE(state.widget_deleted); - EXPECT_TRUE(state.native_widget_deleted); + // TODO(beng): write test for this ownership scenario and the NativeWidget + // being deleted out from under the Widget. +} + +// Widget owns its NativeWidget, part 2: destroy the parent view. +TEST_F(WidgetOwnsNativeWidgetTest, DestroyParentView) { + Widget* toplevel = CreateTopLevelPlatformWidget(); + + auto widget = std::make_unique<OwnershipTestWidget>(state()); + Widget::InitParams params = CreateParamsForTestWidget(); + params.parent = toplevel->GetNativeView(); + params.native_widget = CreatePlatformNativeWidgetImpl( + widget.get(), kStubCapture, &state()->native_widget_deleted); + widget->Init(std::move(params)); + + // Now close the toplevel, which deletes the view hierarchy. + toplevel->CloseNow(); + + RunPendingMessages(); + + // This shouldn't delete the widget because it shouldn't be deleted + // from the native side. + EXPECT_FALSE(state()->widget_deleted); + EXPECT_FALSE(state()->native_widget_deleted); +} + +// Widget owns its NativeWidget, part 3: has a WidgetDelegateView as contents. +TEST_F(WidgetOwnsNativeWidgetTest, WidgetDelegateView) { + auto widget = std::make_unique<OwnershipTestWidget>(state()); + Widget::InitParams params = CreateParamsForTestWidget(); + params.native_widget = CreatePlatformNativeWidgetImpl( + widget.get(), kStubCapture, &state()->native_widget_deleted); + params.delegate = new WidgetDelegateView(); + widget->Init(std::move(params)); + + // Allow the Widget to go out of scope. There should be no crash or + // use-after-free. } //////////////////////////////////////////////////////////////////////////////// @@ -1259,37 +1232,6 @@ TEST_F(WidgetTest, KeyboardInputEvent) { EXPECT_FALSE(backspace_r.handled()); } -// Verifies bubbles result in a focus lost when shown. -// TODO(msw): this tests relies on focus, it needs to be in -// interactive_ui_tests. -TEST_F(DesktopWidgetTest, DISABLED_FocusChangesOnBubble) { - // Create a widget, show and activate it and focus the contents view. - View* contents_view = new View; - contents_view->SetFocusBehavior(View::FocusBehavior::ALWAYS); - std::unique_ptr<Widget> widget = CreateTestWidget(); - widget->SetContentsView(contents_view); - widget->Show(); - widget->Activate(); - contents_view->RequestFocus(); - EXPECT_TRUE(contents_view->HasFocus()); - - // Show a bubble. - BubbleDialogDelegateView* bubble_delegate_view = - new TestBubbleDialogDelegateView(contents_view); - bubble_delegate_view->SetFocusBehavior(View::FocusBehavior::ALWAYS); - BubbleDialogDelegateView::CreateBubble(bubble_delegate_view)->Show(); - bubble_delegate_view->RequestFocus(); - - // |contents_view_| should no longer have focus. - EXPECT_FALSE(contents_view->HasFocus()); - EXPECT_TRUE(bubble_delegate_view->HasFocus()); - - bubble_delegate_view->GetWidget()->CloseNow(); - - // Closing the bubble should result in focus going back to the contents view. - EXPECT_TRUE(contents_view->HasFocus()); -} - TEST_F(WidgetTest, BubbleControlsResetOnInit) { WidgetAutoclosePtr anchor(CreateTopLevelPlatformWidget()); anchor->Show(); @@ -1332,8 +1274,10 @@ TEST_F(DesktopWidgetTest, TestViewWidthAfterMinimizingWidget) { // paints are expected. class DesktopAuraTestValidPaintWidget : public Widget, public WidgetObserver { public: - DesktopAuraTestValidPaintWidget() { observer_.Add(this); } - + explicit DesktopAuraTestValidPaintWidget(Widget::InitParams init_params) + : Widget(std::move(init_params)) { + observer_.Add(this); + } ~DesktopAuraTestValidPaintWidget() override = default; bool ReadReceivedPaintAndReset() { @@ -1385,14 +1329,12 @@ class DesktopAuraPaintWidgetTest : public DesktopWidgetTest { std::unique_ptr<views::Widget> CreateTestWidget( views::Widget::InitParams::Type type = views::Widget::InitParams::TYPE_WINDOW_FRAMELESS) override { - Widget::InitParams params = CreateParamsForTestWidget(type); - auto widget = std::make_unique<DesktopAuraTestValidPaintWidget>(); + auto widget = std::make_unique<DesktopAuraTestValidPaintWidget>( + CreateParamsForTestWidget(type)); paint_widget_ = widget.get(); - widget->Init(std::move(params)); - View* contents_view = new View; + View* contents_view = widget->SetContentsView(std::make_unique<View>()); contents_view->SetFocusBehavior(View::FocusBehavior::ALWAYS); - widget->SetContentsView(contents_view); widget->Show(); widget->Activate(); @@ -2019,8 +1961,8 @@ class WidgetWindowTitleTest : public DesktopWidgetTest { CreateParams(Widget::InitParams::TYPE_WINDOW); if (!desktop_native_widget) { - init_params.native_widget = CreatePlatformNativeWidgetImpl( - init_params, widget.get(), kStubCapture, nullptr); + init_params.native_widget = + CreatePlatformNativeWidgetImpl(widget.get(), kStubCapture, nullptr); } widget->Init(std::move(init_params)); @@ -2066,7 +2008,8 @@ TEST_F(WidgetTest, WidgetDeleted_InOnMousePressed) { CreateParams(views::Widget::InitParams::TYPE_POPUP); widget->Init(std::move(params)); - widget->SetContentsView(new CloseWidgetView(ui::ET_MOUSE_PRESSED)); + widget->SetContentsView( + std::make_unique<CloseWidgetView>(ui::ET_MOUSE_PRESSED)); widget->SetSize(gfx::Size(100, 100)); widget->Show(); @@ -2091,7 +2034,8 @@ TEST_F(WidgetTest, WidgetDeleted_InDispatchGestureEvent) { CreateParams(views::Widget::InitParams::TYPE_POPUP); widget->Init(std::move(params)); - widget->SetContentsView(new CloseWidgetView(ui::ET_GESTURE_TAP_DOWN)); + widget->SetContentsView( + std::make_unique<CloseWidgetView>(ui::ET_GESTURE_TAP_DOWN)); widget->SetSize(gfx::Size(100, 100)); widget->Show(); @@ -2131,8 +2075,8 @@ bool RunGetNativeThemeFromDestructor(Widget::InitParams params, // Deletes itself when the Widget is destroyed. params.delegate = new GetNativeThemeFromDestructorView; if (!is_first_run) { - params.native_widget = CreatePlatformNativeWidgetImpl( - params, widget.get(), kStubCapture, nullptr); + params.native_widget = + CreatePlatformNativeWidgetImpl(widget.get(), kStubCapture, nullptr); needs_second_run = true; } widget->Init(std::move(params)); @@ -3041,8 +2985,8 @@ class WidgetChildDestructionTest : public DesktopWidgetTest { Widget::InitParams params = CreateParams(views::Widget::InitParams::TYPE_WINDOW); if (!top_level_has_desktop_native_widget_aura) { - params.native_widget = CreatePlatformNativeWidgetImpl( - params, top_level, kStubCapture, nullptr); + params.native_widget = + CreatePlatformNativeWidgetImpl(top_level, kStubCapture, nullptr); } top_level->Init(std::move(params)); top_level->GetRootView()->AddChildView( @@ -3054,8 +2998,8 @@ class WidgetChildDestructionTest : public DesktopWidgetTest { CreateParams(views::Widget::InitParams::TYPE_POPUP); child_params.parent = top_level->GetNativeView(); if (!child_has_desktop_native_widget_aura) { - child_params.native_widget = CreatePlatformNativeWidgetImpl( - child_params, child, kStubCapture, nullptr); + child_params.native_widget = + CreatePlatformNativeWidgetImpl(child, kStubCapture, nullptr); } child->Init(std::move(child_params)); child->GetRootView()->AddChildView( @@ -3874,48 +3818,31 @@ class ModalDialogDelegate : public DialogDelegateView { // remaining top-level windows should be re-enabled. TEST_F(DesktopWidgetTest, WindowModalOwnerDestroyedEnabledTest) { // top_level_widget owns owner_dialog_widget which owns owned_dialog_widget. - - // Create the top level widget. std::unique_ptr<Widget> top_level_widget = CreateTestWidget(); top_level_widget->Show(); // Create the owner modal dialog. - // owner_dialog_delegate instance will be destroyed when the dialog - // is destroyed. - ModalDialogDelegate* owner_dialog_delegate = - new ModalDialogDelegate(ui::MODAL_TYPE_WINDOW); - - Widget owner_dialog_widget; - Widget::InitParams init_params = - CreateParamsForTestWidget(Widget::InitParams::TYPE_WINDOW); - init_params.delegate = owner_dialog_delegate; - init_params.parent = top_level_widget->GetNativeView(); - init_params.native_widget = - new test::TestPlatformNativeWidget<DesktopNativeWidgetAura>( - &owner_dialog_widget, false, nullptr); - owner_dialog_widget.Init(std::move(init_params)); - - HWND owner_hwnd = HWNDForWidget(&owner_dialog_widget); - + const auto create_params = [this](Widget* widget, gfx::NativeView parent) { + Widget::InitParams init_params = + CreateParamsForTestWidget(Widget::InitParams::TYPE_WINDOW); + init_params.delegate = new ModalDialogDelegate(ui::MODAL_TYPE_WINDOW); + init_params.parent = parent; + init_params.native_widget = + new test::TestPlatformNativeWidget<DesktopNativeWidgetAura>( + widget, false, nullptr); + return init_params; + }; + Widget owner_dialog_widget( + create_params(&owner_dialog_widget, top_level_widget->GetNativeView())); owner_dialog_widget.Show(); + HWND owner_hwnd = HWNDForWidget(&owner_dialog_widget); // Create the owned modal dialog. - // As above, the owned_dialog_instance instance will be destroyed - // when the dialog is destroyed. - ModalDialogDelegate* owned_dialog_delegate = - new ModalDialogDelegate(ui::MODAL_TYPE_WINDOW); - - Widget owned_dialog_widget; - init_params.delegate = owned_dialog_delegate; - init_params.parent = owner_dialog_widget.GetNativeView(); - init_params.native_widget = - new test::TestPlatformNativeWidget<DesktopNativeWidgetAura>( - &owned_dialog_widget, false, nullptr); - owned_dialog_widget.Init(std::move(init_params)); - + Widget owned_dialog_widget( + create_params(&owned_dialog_widget, owner_dialog_widget.GetNativeView())); + owned_dialog_widget.Show(); HWND owned_hwnd = HWNDForWidget(&owned_dialog_widget); - owned_dialog_widget.Show(); RunPendingMessages(); HWND top_hwnd = HWNDForWidget(top_level_widget.get()); diff --git a/chromium/ui/views/win/fullscreen_handler.cc b/chromium/ui/views/win/fullscreen_handler.cc index 2f828618daf..8791362556f 100644 --- a/chromium/ui/views/win/fullscreen_handler.cc +++ b/chromium/ui/views/win/fullscreen_handler.cc @@ -6,7 +6,6 @@ #include <memory> -#include "base/logging.h" #include "base/win/win_util.h" #include "ui/base/win/shell.h" #include "ui/gfx/geometry/rect.h" diff --git a/chromium/ui/views/win/hwnd_message_handler.cc b/chromium/ui/views/win/hwnd_message_handler.cc index e18afe3c8c2..c981cfc6b0a 100644 --- a/chromium/ui/views/win/hwnd_message_handler.cc +++ b/chromium/ui/views/win/hwnd_message_handler.cc @@ -403,7 +403,6 @@ HWNDMessageHandler::HWNDMessageHandler(HWNDMessageHandlerDelegate* delegate, use_system_default_icon_(false), restored_enabled_(false), current_cursor_(nullptr), - previous_cursor_(nullptr), dpi_(0), called_enable_non_client_dpi_scaling_(false), active_mouse_tracking_flags_(0), @@ -864,13 +863,9 @@ bool HWNDMessageHandler::SetTitle(const base::string16& title) { } void HWNDMessageHandler::SetCursor(HCURSOR cursor) { - if (cursor) { - previous_cursor_ = ::SetCursor(cursor); - current_cursor_ = cursor; - } else if (previous_cursor_) { - ::SetCursor(previous_cursor_); - previous_cursor_ = nullptr; - } + TRACE_EVENT1("ui,input", "HWNDMessageHandler::SetCursor", "cursor", cursor); + ::SetCursor(cursor); + current_cursor_ = cursor; } void HWNDMessageHandler::FrameTypeChanged() { @@ -2386,18 +2381,42 @@ void HWNDMessageHandler::OnPaint(HDC dc) { } if (!IsRectEmpty(&ps.rcPaint)) { + HBRUSH brush = reinterpret_cast<HBRUSH>(GetStockObject(BLACK_BRUSH)); + if (HasChildRenderingWindow()) { // If there's a child window that's being rendered to then clear the // area outside it (as WS_CLIPCHILDREN is set) with transparent black. // Otherwise, other portions of the backing store for the window can // flicker opaque black. http://crbug.com/586454 - FillRect(ps.hdc, &ps.rcPaint, - reinterpret_cast<HBRUSH>(GetStockObject(BLACK_BRUSH))); + FillRect(ps.hdc, &ps.rcPaint, brush); + } else if (exposed_pixels_.height() > 0 || exposed_pixels_.width() > 0) { + // Fill in newly exposed window client area with black to ensure Windows + // doesn't put something else there (eg. copying existing pixels). This + // isn't needed if we've just cleared the whole client area outside the + // child window above. + RECT cr; + if (GetClientRect(hwnd(), &cr)) { + if (exposed_pixels_.height() > 0) { + DCHECK_GE(cr.bottom, exposed_pixels_.height()); + RECT rect = {cr.left, cr.bottom - exposed_pixels_.height(), cr.right, + cr.bottom}; + FillRect(ps.hdc, &rect, brush); + } + if (exposed_pixels_.width() > 0) { + DCHECK_GE(cr.right, exposed_pixels_.width()); + RECT rect = {cr.right - exposed_pixels_.width(), cr.top, cr.right, + cr.bottom - exposed_pixels_.height()}; + FillRect(ps.hdc, &rect, brush); + } + } } + delegate_->HandlePaintAccelerated(gfx::Rect(ps.rcPaint)); } + exposed_pixels_ = gfx::Size(); + EndPaint(hwnd(), &ps); } @@ -2815,6 +2834,10 @@ void HWNDMessageHandler::OnWindowPosChanging(WINDOWPOS* window_pos) { gfx::Size new_size = gfx::Size(window_pos->cx, window_pos->cy); if ((old_size != new_size && !(window_pos->flags & SWP_NOSIZE)) || window_pos->flags & SWP_FRAMECHANGED) { + // If the window is getting larger then fill the exposed area on the next + // WM_PAINT. + exposed_pixels_ = new_size - old_size; + delegate_->HandleWindowSizeChanging(); sent_window_size_changing_ = true; @@ -2827,6 +2850,9 @@ void HWNDMessageHandler::OnWindowPosChanging(WINDOWPOS* window_pos) { // resizing. See https://crbug.com/739724 if (is_translucent_) window_pos->flags |= SWP_NOCOPYBITS; + } else { + // The window size isn't changing so there are no exposed pixels. + exposed_pixels_ = gfx::Size(); } if (ScopedFullscreenVisibility::IsHiddenForFullscreen(hwnd())) { @@ -2867,10 +2893,13 @@ LRESULT HWNDMessageHandler::OnWindowSizingFinished(UINT message, // received after this message was posted. if (current_window_size_message_ != w_param) return 0; - delegate_->HandleWindowSizeUnchanged(); sent_window_size_changing_ = false; + // The window size didn't actually change, so nothing was exposed that needs + // to be filled black. + exposed_pixels_ = gfx::Size(); + return 0; } @@ -3126,9 +3155,8 @@ LRESULT HWNDMessageHandler::HandlePointerEventTypeTouch(UINT message, ui::TouchEvent event( event_type, touch_point, event_time, - ui::PointerDetails(ui::EventPointerType::POINTER_TYPE_TOUCH, - mapped_pointer_id, radius_x, radius_y, pressure, - rotation_angle), + ui::PointerDetails(ui::EventPointerType::kTouch, mapped_pointer_id, + radius_x, radius_y, pressure, rotation_angle), ui::GetModifiersFromKeyState()); event.latency()->AddLatencyNumberWithTimestamp( @@ -3282,9 +3310,8 @@ void HWNDMessageHandler::GenerateTouchEvent(ui::EventType event_type, size_t id, base::TimeTicks time_stamp, TouchEvents* touch_events) { - ui::TouchEvent event( - event_type, point, time_stamp, - ui::PointerDetails(ui::EventPointerType::POINTER_TYPE_TOUCH, id)); + ui::TouchEvent event(event_type, point, time_stamp, + ui::PointerDetails(ui::EventPointerType::kTouch, id)); event.set_flags(ui::GetModifiersFromKeyState()); diff --git a/chromium/ui/views/win/hwnd_message_handler.h b/chromium/ui/views/win/hwnd_message_handler.h index 7b56d58faff..a84076658b4 100644 --- a/chromium/ui/views/win/hwnd_message_handler.h +++ b/chromium/ui/views/win/hwnd_message_handler.h @@ -625,10 +625,6 @@ class VIEWS_EXPORT HWNDMessageHandler : public gfx::WindowImpl, // The current cursor. HCURSOR current_cursor_; - // The last cursor that was active before the current one was selected. Saved - // so that we can restore it. - HCURSOR previous_cursor_; - // The icon created from the bitmap image of the window icon. base::win::ScopedHICON window_icon_; @@ -798,6 +794,11 @@ class VIEWS_EXPORT HWNDMessageHandler : public gfx::WindowImpl, static base::LazyInstance<FullscreenWindowMonitorMap>::DestructorAtExit fullscreen_monitor_map_; + // How many pixels the window is expected to grow from OnWindowPosChanging(). + // Used to fill the newly exposed pixels black in OnPaint() before the + // browser compositor is able to redraw at the new window size. + gfx::Size exposed_pixels_; + // Populated if the cursor position is being mocked for testing purposes. base::Optional<gfx::Point> mock_cursor_position_; diff --git a/chromium/ui/views/win/pen_event_processor.cc b/chromium/ui/views/win/pen_event_processor.cc index 3ff82e59065..46ac6b2c8f7 100644 --- a/chromium/ui/views/win/pen_event_processor.cc +++ b/chromium/ui/views/win/pen_event_processor.cc @@ -4,6 +4,8 @@ #include "ui/views/win/pen_event_processor.h" +#include "base/check.h" +#include "base/notreached.h" #include "base/time/time.h" #include "ui/events/event_utils.h" @@ -40,16 +42,16 @@ std::unique_ptr<ui::Event> PenEventProcessor::GenerateEvent( // We are now creating a fake mouse event with pointer type of pen from // the WM_POINTER message and then setting up an associated pointer // details in the MouseEvent which contains the pen's information. - ui::EventPointerType input_type = ui::EventPointerType::POINTER_TYPE_PEN; + ui::EventPointerType input_type = ui::EventPointerType::kPen; // For the pointerup event, the penFlags is not set to PEN_FLAG_ERASER, so we // have to check if previously the pointer type is an eraser. if (pointer_pen_info.penFlags & PEN_FLAG_ERASER) { - input_type = ui::EventPointerType::POINTER_TYPE_ERASER; + input_type = ui::EventPointerType::kEraser; DCHECK(!eraser_pointer_id_ || *eraser_pointer_id_ == mapped_pointer_id); eraser_pointer_id_ = mapped_pointer_id; } else if (eraser_pointer_id_ && *eraser_pointer_id_ == mapped_pointer_id && (message == WM_POINTERUP || message == WM_NCPOINTERUP)) { - input_type = ui::EventPointerType::POINTER_TYPE_ERASER; + input_type = ui::EventPointerType::kEraser; eraser_pointer_id_.reset(); } diff --git a/chromium/ui/views/win/pen_event_processor_unittest.cc b/chromium/ui/views/win/pen_event_processor_unittest.cc index 211a735e062..652606182df 100644 --- a/chromium/ui/views/win/pen_event_processor_unittest.cc +++ b/chromium/ui/views/win/pen_event_processor_unittest.cc @@ -245,7 +245,7 @@ TEST(PenProcessorTest, PenEraserFlagDMEnabled) { ASSERT_TRUE(event); ASSERT_TRUE(event->IsTouchEvent()); EXPECT_EQ(ui::ET_TOUCH_PRESSED, event->AsTouchEvent()->type()); - EXPECT_EQ(ui::EventPointerType::POINTER_TYPE_ERASER, + EXPECT_EQ(ui::EventPointerType::kEraser, event->AsTouchEvent()->pointer_details().pointer_type); pen_info.pointerInfo.pointerFlags = POINTER_FLAG_UP; @@ -255,7 +255,7 @@ TEST(PenProcessorTest, PenEraserFlagDMEnabled) { ASSERT_TRUE(event); ASSERT_TRUE(event->IsTouchEvent()); EXPECT_EQ(ui::ET_TOUCH_RELEASED, event->AsTouchEvent()->type()); - EXPECT_EQ(ui::EventPointerType::POINTER_TYPE_ERASER, + EXPECT_EQ(ui::EventPointerType::kEraser, event->AsTouchEvent()->pointer_details().pointer_type); } diff --git a/chromium/ui/views/win/scoped_fullscreen_visibility.cc b/chromium/ui/views/win/scoped_fullscreen_visibility.cc index e3103243c64..4c77b7e7ee8 100644 --- a/chromium/ui/views/win/scoped_fullscreen_visibility.cc +++ b/chromium/ui/views/win/scoped_fullscreen_visibility.cc @@ -6,7 +6,7 @@ #include <utility> -#include "base/logging.h" +#include "base/check.h" namespace views { diff --git a/chromium/ui/views/window/caption_button_layout_constants.cc b/chromium/ui/views/window/caption_button_layout_constants.cc index a0738471c3f..3845999821f 100644 --- a/chromium/ui/views/window/caption_button_layout_constants.cc +++ b/chromium/ui/views/window/caption_button_layout_constants.cc @@ -4,7 +4,6 @@ #include "ui/views/window/caption_button_layout_constants.h" -#include "base/logging.h" #include "ui/base/pointer/touch_ui_controller.h" #include "ui/gfx/geometry/size.h" diff --git a/chromium/ui/views/window/client_view.cc b/chromium/ui/views/window/client_view.cc index c1b7d081893..13562905b1a 100644 --- a/chromium/ui/views/window/client_view.cc +++ b/chromium/ui/views/window/client_view.cc @@ -6,7 +6,7 @@ #include <memory> -#include "base/logging.h" +#include "base/check.h" #include "ui/accessibility/ax_enums.mojom.h" #include "ui/accessibility/ax_node_data.h" #include "ui/base/hit_test.h" diff --git a/chromium/ui/views/window/dialog_client_view.cc b/chromium/ui/views/window/dialog_client_view.cc index 7c683a51ae9..64e7e523b45 100644 --- a/chromium/ui/views/window/dialog_client_view.cc +++ b/chromium/ui/views/window/dialog_client_view.cc @@ -266,10 +266,8 @@ void DialogClientView::UpdateDialogButton(LabelButton** member, return; } - std::unique_ptr<LabelButton> button = - is_default ? MdTextButton::CreateSecondaryUiBlueButton(this, title) - : MdTextButton::CreateSecondaryUiButton(this, title); - + auto button = MdTextButton::Create(this, title); + button->SetProminent(is_default); button->SetIsDefault(is_default); button->SetEnabled(delegate->IsDialogButtonEnabled(type)); @@ -352,13 +350,13 @@ void DialogClientView::SetupLayout() { // into the layout. This simplifies min/max size calculations. column_set->AddPaddingColumn(kFixed, button_row_insets_.left()); column_set->AddColumn(GridLayout::FILL, GridLayout::FILL, kFixed, - GridLayout::USE_PREF, 0, 0); + GridLayout::ColumnSize::kUsePreferred, 0, 0); column_set->AddPaddingColumn(kStretchy, GetExtraViewSpacing()); column_set->AddColumn(GridLayout::FILL, GridLayout::FILL, kFixed, - GridLayout::USE_PREF, 0, 0); + GridLayout::ColumnSize::kUsePreferred, 0, 0); column_set->AddPaddingColumn(kFixed, button_spacing); column_set->AddColumn(GridLayout::FILL, GridLayout::FILL, kFixed, - GridLayout::USE_PREF, 0, 0); + GridLayout::ColumnSize::kUsePreferred, 0, 0); column_set->AddPaddingColumn(kFixed, button_row_insets_.right()); // Track which columns to link sizes under MD. diff --git a/chromium/ui/views/window/dialog_client_view_unittest.cc b/chromium/ui/views/window/dialog_client_view_unittest.cc index 390eac20ccd..921d7e999f8 100644 --- a/chromium/ui/views/window/dialog_client_view_unittest.cc +++ b/chromium/ui/views/window/dialog_client_view_unittest.cc @@ -50,6 +50,9 @@ class DialogClientViewTest : public test::WidgetTest, params.delegate = this; widget_->Init(std::move(params)); EXPECT_EQ(this, GetContentsView()); + layout_provider_ = std::make_unique<test::TestLayoutProvider>(); + layout_provider_->SetDistanceMetric(DISTANCE_BUTTON_MAX_LINKABLE_WIDTH, + 200); } void TearDown() override { @@ -90,6 +93,11 @@ class DialogClientViewTest : public test::WidgetTest, DialogModelChanged(); } + void SetDialogButtonLabel(ui::DialogButton button, const std::string& label) { + DialogDelegate::SetButtonLabel(button, base::UTF8ToUTF16(label)); + DialogModelChanged(); + } + // Sets the view to provide to DisownExtraView() and updates the dialog. This // can only be called a single time because DialogClientView caches the result // of DisownExtraView() and never calls it again. @@ -124,14 +132,34 @@ class DialogClientViewTest : public test::WidgetTest, DialogModelChanged(); } + views::Button* GetButtonByAccessibleName(views::View* root, + const base::string16& name) { + views::Button* button = Button::AsButton(root); + if (button && button->GetAccessibleName() == name) + return button; + for (auto* child : root->children()) { + button = GetButtonByAccessibleName(child, name); + if (button) + return button; + } + return nullptr; + } + + views::Button* GetButtonByAccessibleName(const std::string& label) { + return GetButtonByAccessibleName(widget_->GetRootView(), + base::UTF8ToUTF16(label)); + } + DialogClientView* client_view() { return static_cast<DialogClientView*>(widget_->client_view()); } Widget* widget() { return widget_; } + test::TestLayoutProvider* layout_provider() { return layout_provider_.get(); } private: // The dialog Widget. + std::unique_ptr<test::TestLayoutProvider> layout_provider_; Widget* widget_ = nullptr; gfx::Size preferred_size_; @@ -330,8 +358,6 @@ TEST_F(DialogClientViewTest, MinMaxPreferredSize) { // Ensure button widths are linked under MD. TEST_F(DialogClientViewTest, LinkedWidthDoesLink) { - test::TestLayoutProvider layout_provider; - layout_provider.SetDistanceMetric(DISTANCE_BUTTON_MAX_LINKABLE_WIDTH, 200); SetLongCancelLabel(); // Ensure there is no default button since getting a bold font can throw off @@ -361,13 +387,13 @@ TEST_F(DialogClientViewTest, LinkedWidthDoesLink) { EXPECT_EQ(cancel_button_width, client_view()->ok_button()->width()); // But not when the size of the cancel button exceeds the max linkable width. - layout_provider.SetDistanceMetric(DISTANCE_BUTTON_MAX_LINKABLE_WIDTH, 100); + layout_provider()->SetDistanceMetric(DISTANCE_BUTTON_MAX_LINKABLE_WIDTH, 100); EXPECT_GT(cancel_button_width, 100); DialogModelChanged(); CheckContentsIsSetToPreferredSize(); EXPECT_EQ(ok_button_only_width, client_view()->ok_button()->width()); - layout_provider.SetDistanceMetric(DISTANCE_BUTTON_MAX_LINKABLE_WIDTH, 200); + layout_provider()->SetDistanceMetric(DISTANCE_BUTTON_MAX_LINKABLE_WIDTH, 200); // The extra view should also match, if it's a matching button type. View* extra_button = @@ -377,8 +403,6 @@ TEST_F(DialogClientViewTest, LinkedWidthDoesLink) { } TEST_F(DialogClientViewTest, LinkedWidthDoesntLink) { - test::TestLayoutProvider layout_provider; - layout_provider.SetDistanceMetric(DISTANCE_BUTTON_MAX_LINKABLE_WIDTH, 200); SetLongCancelLabel(); // Ensure there is no default button since getting a bold font can throw off @@ -408,13 +432,13 @@ TEST_F(DialogClientViewTest, LinkedWidthDoesntLink) { EXPECT_EQ(cancel_button_width, client_view()->ok_button()->width()); // But not when the size of the cancel button exceeds the max linkable width. - layout_provider.SetDistanceMetric(DISTANCE_BUTTON_MAX_LINKABLE_WIDTH, 100); + layout_provider()->SetDistanceMetric(DISTANCE_BUTTON_MAX_LINKABLE_WIDTH, 100); EXPECT_GT(cancel_button_width, 100); DialogModelChanged(); CheckContentsIsSetToPreferredSize(); EXPECT_EQ(ok_button_only_width, client_view()->ok_button()->width()); - layout_provider.SetDistanceMetric(DISTANCE_BUTTON_MAX_LINKABLE_WIDTH, 200); + layout_provider()->SetDistanceMetric(DISTANCE_BUTTON_MAX_LINKABLE_WIDTH, 200); // Checkbox extends LabelButton, but it should not participate in linking. View* extra_button = @@ -542,4 +566,74 @@ TEST_F(DialogClientViewTest, IgnorePossiblyUnintendedClicks_RepeatedClicks) { EXPECT_TRUE(widget()->IsClosed()); } +TEST_F(DialogClientViewTest, ButtonLayoutWithExtra) { + // The dialog button row's layout should look like: + // | <inset> [extra] <flex-margin> [cancel] <margin> [ok] <inset> | + // Where: + // 1) The two insets are linkable + // 2) The ok & cancel buttons have their width linked + // 3) The extra button has its width linked to the other two + // 4) The margin should be invariant as the dialog changes width + // 5) The flex margin should change as the dialog changes width + // + // Note that cancel & ok may swap order depending on + // PlatformStyle::kIsOkButtonLeading; these invariants hold for either order. + SetDialogButtons(ui::DIALOG_BUTTON_OK | ui::DIALOG_BUTTON_CANCEL); + SetDialogButtonLabel(ui::DIALOG_BUTTON_OK, "ok"); + SetDialogButtonLabel(ui::DIALOG_BUTTON_CANCEL, "cancel"); + SetExtraView( + std::make_unique<LabelButton>(nullptr, base::UTF8ToUTF16("extra"))); + + widget()->Show(); + + Button* ok = GetButtonByAccessibleName("ok"); + Button* cancel = GetButtonByAccessibleName("cancel"); + Button* extra = GetButtonByAccessibleName("extra"); + + ASSERT_NE(ok, cancel); + ASSERT_NE(ok, extra); + ASSERT_NE(cancel, extra); + + client_view()->SizeToPreferredSize(); + client_view()->Layout(); + + auto bounds_left = [](View* v) { return v->GetBoundsInScreen().x(); }; + auto bounds_right = [](View* v) { return v->GetBoundsInScreen().right(); }; + + // (1): left inset == right inset (and they shouldn't be 0): + int left_inset = bounds_left(extra) - bounds_left(this); + int right_inset = + bounds_right(this) - std::max(bounds_right(ok), bounds_right(cancel)); + EXPECT_EQ(left_inset, right_inset); + EXPECT_GT(left_inset, 0); + + // (2) & (3): All three buttons have their widths linked: + EXPECT_EQ(ok->width(), cancel->width()); + EXPECT_EQ(ok->width(), extra->width()); + EXPECT_GT(ok->width(), 0); + + // (4): Margin between ok & cancel should be invariant as dialog width + // changes: + auto get_margin = [&]() { + return std::max(bounds_left(ok), bounds_left(cancel)) - + std::min(bounds_right(ok), bounds_right(cancel)); + }; + + // (5): Flex margin between ok/cancel and extra should vary with dialog width + // (it should absorb 100% of the change in width) + auto get_flex_margin = [&]() { + return std::min(bounds_left(ok), bounds_left(cancel)) - bounds_right(extra); + }; + + int old_margin = get_margin(); + int old_flex_margin = get_flex_margin(); + + SetSizeConstraints(gfx::Size(), gfx::Size(width() + 100, 0), gfx::Size()); + client_view()->SizeToPreferredSize(); + client_view()->Layout(); + + EXPECT_EQ(old_margin, get_margin()); + EXPECT_EQ(old_flex_margin + 100, get_flex_margin()); +} + } // namespace views diff --git a/chromium/ui/views/window/dialog_delegate.cc b/chromium/ui/views/window/dialog_delegate.cc index 16ee3828e40..6afc661f5a2 100644 --- a/chromium/ui/views/window/dialog_delegate.cc +++ b/chromium/ui/views/window/dialog_delegate.cc @@ -12,6 +12,7 @@ #include "build/build_config.h" #include "ui/accessibility/ax_enums.mojom.h" #include "ui/accessibility/ax_node_data.h" +#include "ui/accessibility/ax_role_properties.h" #include "ui/base/l10n/l10n_util.h" #include "ui/gfx/color_palette.h" #include "ui/strings/grit/ui_strings.h" @@ -42,6 +43,8 @@ DialogDelegate::Params::~Params() = default; // DialogDelegate: DialogDelegate::DialogDelegate() { + WidgetDelegate::RegisterWindowWillCloseCallback( + base::BindOnce(&DialogDelegate::WindowWillClose, base::Unretained(this))); UMA_HISTOGRAM_BOOLEAN("Dialog.DialogDelegate.Create", true); creation_time_ = base::TimeTicks::Now(); } @@ -135,7 +138,7 @@ base::string16 DialogDelegate::GetDialogButtonLabel( } bool DialogDelegate::IsDialogButtonEnabled(ui::DialogButton button) const { - return true; + return params_.enabled_buttons & button; } bool DialogDelegate::Cancel() { @@ -324,16 +327,35 @@ void DialogDelegate::DialogModelChanged() { } void DialogDelegate::SetDefaultButton(int button) { + if (params_.default_button == button) + return; params_.default_button = button; + DialogModelChanged(); } void DialogDelegate::SetButtons(int buttons) { + if (params_.buttons == buttons) + return; params_.buttons = buttons; + DialogModelChanged(); +} + +void DialogDelegate::SetButtonEnabled(ui::DialogButton button, bool enabled) { + if (!!(params_.enabled_buttons & button) == enabled) + return; + if (enabled) + params_.enabled_buttons |= button; + else + params_.enabled_buttons &= ~button; + DialogModelChanged(); } void DialogDelegate::SetButtonLabel(ui::DialogButton button, base::string16 label) { + if (params_.button_labels[button] == label) + return; params_.button_labels[button] = label; + DialogModelChanged(); } void DialogDelegate::SetAcceptCallback(base::OnceClosure callback) { @@ -430,8 +452,7 @@ View* DialogDelegateView::GetContentsView() { void DialogDelegateView::ViewHierarchyChanged( const ViewHierarchyChangedDetails& details) { if (details.is_add && details.child == this && GetWidget() && - (GetAccessibleWindowRole() == ax::mojom::Role::kAlert || - GetAccessibleWindowRole() == ax::mojom::Role::kAlertDialog)) { + ui::IsAlert(GetAccessibleWindowRole())) { NotifyAccessibilityEvent(ax::mojom::Event::kAlert, true); } } diff --git a/chromium/ui/views/window/dialog_delegate.h b/chromium/ui/views/window/dialog_delegate.h index 54ea673bb33..04bc5b4cdce 100644 --- a/chromium/ui/views/window/dialog_delegate.h +++ b/chromium/ui/views/window/dialog_delegate.h @@ -59,6 +59,11 @@ class VIEWS_EXPORT DialogDelegate : public WidgetDelegate { // Prefer to use this field (via SetButtonLabel) rather than override // GetDialogButtonLabel - see https://crbug.com/1011446 base::string16 button_labels[ui::DIALOG_BUTTON_LAST + 1]; + + // A bitmask of buttons (from ui::DialogButton) that are enabled in this + // dialog. It's legal for a button to be marked enabled that isn't present + // in |buttons| (see above). + int enabled_buttons = ui::DIALOG_BUTTON_OK | ui::DIALOG_BUTTON_CANCEL; }; DialogDelegate(); @@ -130,7 +135,6 @@ class VIEWS_EXPORT DialogDelegate : public WidgetDelegate { DialogDelegate* AsDialogDelegate() override; ClientView* CreateClientView(Widget* widget) override; NonClientFrameView* CreateNonClientFrameView(Widget* widget) override; - void WindowWillClose() override; static NonClientFrameView* CreateDialogFrameView(Widget* widget); @@ -169,7 +173,11 @@ class VIEWS_EXPORT DialogDelegate : public WidgetDelegate { void AddObserver(DialogObserver* observer); void RemoveObserver(DialogObserver* observer); - // Notifies observers when the result of the DialogModel overrides changes. + // Notifies DialogDelegate that the result of one of the virtual getter + // functions above has changed, which causes it to rebuild its layout. It is + // not necessary to call this unless you are overriding + // IsDialogButtonEnabled() or manually manipulating the dialog buttons. + // TODO(https://crbug.com/1011446): Make this private. void DialogModelChanged(); void set_use_round_corners(bool round) { params_.round_corners = round; } @@ -178,9 +186,12 @@ class VIEWS_EXPORT DialogDelegate : public WidgetDelegate { void set_use_custom_frame(bool use) { params_.custom_frame = use; } bool use_custom_frame() const { return params_.custom_frame; } + // These methods internally call DialogModelChanged() if needed, so it is not + // necessary to call DialogModelChanged() yourself after calling them. void SetDefaultButton(int button); void SetButtons(int buttons); void SetButtonLabel(ui::DialogButton button, base::string16 label); + void SetButtonEnabled(ui::DialogButton button, bool enabled); void SetAcceptCallback(base::OnceClosure callback); void SetCancelCallback(base::OnceClosure callback); void SetCloseCallback(base::OnceClosure callback); @@ -243,6 +254,10 @@ class VIEWS_EXPORT DialogDelegate : public WidgetDelegate { // dialogs use the same button row insets. void SetButtonRowInsets(const gfx::Insets& insets); + // Callback for WidgetDelegate when the window this dialog is hosted in is + // closing. Don't call this yourself. + void WindowWillClose(); + protected: ~DialogDelegate() override; diff --git a/chromium/ui/views/window/dialog_delegate_unittest.cc b/chromium/ui/views/window/dialog_delegate_unittest.cc index eeaf5cd23a8..6096431dc6b 100644 --- a/chromium/ui/views/window/dialog_delegate_unittest.cc +++ b/chromium/ui/views/window/dialog_delegate_unittest.cc @@ -427,6 +427,17 @@ TEST_F(DialogTest, UnfocusableInitialFocus) { dialog_widget->CloseNow(); } +TEST_F(DialogTest, ButtonEnableUpdatesState) { + test::WidgetTest::WidgetAutoclosePtr widget( + CreateDialogWidget(new DialogDelegateView)); + auto* dialog = static_cast<DialogDelegateView*>(widget->widget_delegate()); + + EXPECT_TRUE(dialog->GetOkButton()->GetEnabled()); + dialog->SetButtonEnabled(ui::DIALOG_BUTTON_OK, false); + dialog->DialogModelChanged(); + EXPECT_FALSE(dialog->GetOkButton()->GetEnabled()); +} + using DialogDelegateCloseTest = ViewsTestBase; TEST_F(DialogDelegateCloseTest, AnyCallbackInhibitsDefaultClose) { diff --git a/chromium/ui/views/window/window_resize_utils.cc b/chromium/ui/views/window/window_resize_utils.cc index 8ec1727320e..5578e3ac267 100644 --- a/chromium/ui/views/window/window_resize_utils.cc +++ b/chromium/ui/views/window/window_resize_utils.cc @@ -6,6 +6,7 @@ #include <algorithm> +#include "base/check_op.h" #include "ui/gfx/geometry/rect.h" #include "ui/gfx/geometry/size.h" |