diff options
author | Allan Sandfeld Jensen <allan.jensen@qt.io> | 2019-08-30 10:22:43 +0200 |
---|---|---|
committer | Allan Sandfeld Jensen <allan.jensen@qt.io> | 2019-08-30 12:36:28 +0000 |
commit | 271a6c3487a14599023a9106329505597638d793 (patch) | |
tree | e040d58ffc86c1480b79ca8528020ca9ec919bf8 /chromium/ui/views | |
parent | 7b2ffa587235a47d4094787d72f38102089f402a (diff) | |
download | qtwebengine-chromium-271a6c3487a14599023a9106329505597638d793.tar.gz |
BASELINE: Update Chromium to 77.0.3865.59
Change-Id: I1e89a5f3b009a9519a6705102ad65c92fe736f21
Reviewed-by: Michael BrĂ¼ning <michael.bruning@qt.io>
Diffstat (limited to 'chromium/ui/views')
382 files changed, 9110 insertions, 6342 deletions
diff --git a/chromium/ui/views/BUILD.gn b/chromium/ui/views/BUILD.gn index 081c13d1250..263f4a8b8ef 100644 --- a/chromium/ui/views/BUILD.gn +++ b/chromium/ui/views/BUILD.gn @@ -84,6 +84,9 @@ jumbo_component("views") { "animation/ink_drop_stub.h", "animation/ink_drop_util.h", "animation/installable_ink_drop.h", + "animation/installable_ink_drop_animator.h", + "animation/installable_ink_drop_config.h", + "animation/installable_ink_drop_painter.h", "animation/scroll_animator.h", "animation/square_ink_drop_ripple.h", "background.h", @@ -94,8 +97,7 @@ jumbo_component("views") { "bubble/info_bubble.h", "bubble/tooltip_icon.h", "button_drag_utils.h", - "cocoa/bridge_factory_host.h", - "cocoa/bridged_native_widget_host_impl.h", + "cocoa/native_widget_mac_ns_window_host.h", "color_chooser/color_chooser_listener.h", "color_chooser/color_chooser_view.h", "context_menu_controller.h", @@ -190,24 +192,23 @@ jumbo_component("views") { "drag_utils.h", "event_monitor.h", "event_monitor_mac.h", - "event_utils.h", "focus/external_focus_tracker.h", "focus/focus_manager.h", "focus/focus_manager_delegate.h", "focus/focus_manager_factory.h", "focus/focus_search.h", "focus/widget_focus_manager.h", + "input_event_activation_protector.h", "layout/box_layout.h", "layout/fill_layout.h", "layout/flex_layout.h", "layout/flex_layout_types.h", "layout/grid_layout.h", + "layout/interpolating_layout_manager.h", "layout/layout_manager.h", + "layout/layout_manager_base.h", "layout/layout_provider.h", - "linux_ui/device_scale_factor_observer.h", - "linux_ui/linux_ui.h", - "linux_ui/status_icon_linux.h", - "linux_ui/window_button_order_observer.h", + "layout/layout_types.h", "masked_targeter_delegate.h", "metadata/metadata_cache.h", "metadata/metadata_header_macros.h", @@ -306,6 +307,8 @@ jumbo_component("views") { "animation/ink_drop_stub.cc", "animation/ink_drop_util.cc", "animation/installable_ink_drop.cc", + "animation/installable_ink_drop_animator.cc", + "animation/installable_ink_drop_painter.cc", "animation/scroll_animator.cc", "animation/square_ink_drop_ripple.cc", "background.cc", @@ -398,23 +401,23 @@ jumbo_component("views") { "drag_utils.cc", "drag_utils_mac.mm", "event_monitor_mac.mm", - "event_utils.cc", "focus/external_focus_tracker.cc", "focus/focus_manager.cc", "focus/focus_manager_factory.cc", "focus/focus_search.cc", "focus/widget_focus_manager.cc", + "input_event_activation_protector.cc", "layout/box_layout.cc", "layout/fill_layout.cc", "layout/flex_layout.cc", "layout/flex_layout_types.cc", "layout/flex_layout_types_internal.cc", "layout/grid_layout.cc", + "layout/interpolating_layout_manager.cc", "layout/layout_manager.cc", + "layout/layout_manager_base.cc", "layout/layout_provider.cc", - "linux_ui/linux_ui.cc", - "linux_ui/status_icon_linux.cc", - "linux_ui/window_button_order_provider.cc", + "layout/layout_types.cc", "masked_targeter_delegate.cc", "metadata/metadata_cache.cc", "metadata/metadata_types.cc", @@ -479,10 +482,9 @@ jumbo_component("views") { # Internal sources. TODO(https://crbug.com/871123): Move more headers from # public into this list, along with the implementation file. sources += [ - "cocoa/bridge_factory_host.cc", - "cocoa/bridged_native_widget_host_impl.mm", "cocoa/drag_drop_client_mac.h", "cocoa/drag_drop_client_mac.mm", + "cocoa/native_widget_mac_ns_window_host.mm", "cocoa/text_input_host.h", "cocoa/text_input_host.mm", "cocoa/tooltip_manager_mac.h", @@ -545,23 +547,21 @@ jumbo_component("views") { "//ui/base/ime/linux", "//ui/shell_dialogs", ] - } else { - public -= [ + public += [ + "linux_ui/device_scale_factor_observer.h", "linux_ui/linux_ui.h", + "linux_ui/status_icon_linux.h", "linux_ui/window_button_order_observer.h", ] - sources -= [ + sources += [ "linux_ui/linux_ui.cc", + "linux_ui/status_icon_linux.cc", "linux_ui/window_button_order_provider.cc", ] } if (is_chromeos) { - public -= [ "linux_ui/status_icon_linux.h" ] - sources -= [ - "controls/menu/menu_config_linux.cc", - "linux_ui/status_icon_linux.cc", - ] + sources -= [ "controls/menu/menu_config_linux.cc" ] } if (is_win) { @@ -961,7 +961,10 @@ jumbo_source_set("test_support") { } } if (use_x11) { - deps += [ "//ui/gfx/x" ] + deps += [ + "//ui/events/x", + "//ui/gfx/x", + ] } if (ozone_platform_x11) { deps += [ "//ui/base/x" ] @@ -985,6 +988,7 @@ test("views_unittests") { "animation/ink_drop_impl_unittest.cc", "animation/ink_drop_ripple_unittest.cc", "animation/ink_drop_unittest.cc", + "animation/installable_ink_drop_animator_unittest.cc", "animation/installable_ink_drop_unittest.cc", "animation/square_ink_drop_ripple_unittest.cc", "border_unittest.cc", @@ -1007,6 +1011,7 @@ test("views_unittests") { "controls/editable_combobox/editable_combobox_unittest.cc", "controls/image_view_unittest.cc", "controls/label_unittest.cc", + "controls/menu/menu_closure_animation_mac_unittest.cc", "controls/menu/menu_controller_unittest.cc", "controls/menu/menu_item_view_unittest.cc", "controls/menu/menu_model_adapter_unittest.cc", @@ -1041,8 +1046,11 @@ test("views_unittests") { "focus/focus_traversal_unittest.cc", "layout/box_layout_unittest.cc", "layout/fill_layout_unittest.cc", + "layout/flex_layout_types_internal_unittest.cc", "layout/flex_layout_unittest.cc", "layout/grid_layout_unittest.cc", + "layout/interpolating_layout_manager_unittest.cc", + "layout/layout_manager_base_unittest.cc", "metadata/metadata_unittest.cc", "metadata/type_conversion_unittest.cc", "paint_info_unittest.cc", diff --git a/chromium/ui/views/OWNERS b/chromium/ui/views/OWNERS index a5aeea2cc4d..3da3cff33a4 100644 --- a/chromium/ui/views/OWNERS +++ b/chromium/ui/views/OWNERS @@ -1,10 +1,12 @@ # Prefer tapted@ for mac specific changes. +kylixrd@chromium.org msw@chromium.org pkasting@chromium.org -sadrul@chromium.org +robliao@chromium.org sky@chromium.org tapted@chromium.org +weili@chromium.org # Prefer ellyjones@ for any changes to these files. per-file *_mac.*=ellyjones@chromium.org diff --git a/chromium/ui/views/accessibility/ax_root_obj_wrapper.cc b/chromium/ui/views/accessibility/ax_root_obj_wrapper.cc index e03f06abf9b..c5bc7b0efa1 100644 --- a/chromium/ui/views/accessibility/ax_root_obj_wrapper.cc +++ b/chromium/ui/views/accessibility/ax_root_obj_wrapper.cc @@ -32,7 +32,7 @@ AXRootObjWrapper::~AXRootObjWrapper() { bool AXRootObjWrapper::HasChild(views::AXAuraObjWrapper* child) { std::vector<views::AXAuraObjWrapper*> children; GetChildren(&children); - return base::ContainsValue(children, child); + return base::Contains(children, child); } bool AXRootObjWrapper::IsIgnored() { diff --git a/chromium/ui/views/accessibility/ax_tree_source_views.cc b/chromium/ui/views/accessibility/ax_tree_source_views.cc index 1cb8e7cf6f7..9e52448ad75 100644 --- a/chromium/ui/views/accessibility/ax_tree_source_views.cc +++ b/chromium/ui/views/accessibility/ax_tree_source_views.cc @@ -99,8 +99,12 @@ AXAuraObjWrapper* AXTreeSourceViews::GetParent(AXAuraObjWrapper* node) const { return parent; } +bool AXTreeSourceViews::IsIgnored(AXAuraObjWrapper* node) const { + return node && node->IsIgnored(); +} + bool AXTreeSourceViews::IsValid(AXAuraObjWrapper* node) const { - return node && !node->IsIgnored(); + return node; } bool AXTreeSourceViews::IsEqual(AXAuraObjWrapper* node1, diff --git a/chromium/ui/views/accessibility/ax_tree_source_views.h b/chromium/ui/views/accessibility/ax_tree_source_views.h index 12d1f3b5ada..d7256c6d838 100644 --- a/chromium/ui/views/accessibility/ax_tree_source_views.h +++ b/chromium/ui/views/accessibility/ax_tree_source_views.h @@ -46,6 +46,7 @@ class VIEWS_EXPORT AXTreeSourceViews void GetChildren(AXAuraObjWrapper* node, std::vector<AXAuraObjWrapper*>* out_children) const override; AXAuraObjWrapper* GetParent(AXAuraObjWrapper* node) const override; + bool IsIgnored(AXAuraObjWrapper* node) const override; bool IsValid(AXAuraObjWrapper* node) const override; bool IsEqual(AXAuraObjWrapper* node1, AXAuraObjWrapper* node2) const override; AXAuraObjWrapper* GetNull() const override; diff --git a/chromium/ui/views/accessibility/ax_tree_source_views_unittest.cc b/chromium/ui/views/accessibility/ax_tree_source_views_unittest.cc index f94c0082a18..aeff646bcf7 100644 --- a/chromium/ui/views/accessibility/ax_tree_source_views_unittest.cc +++ b/chromium/ui/views/accessibility/ax_tree_source_views_unittest.cc @@ -151,7 +151,7 @@ TEST_F(AXTreeSourceViewsTest, IgnoredView) { AXAuraObjCache cache; TestAXTreeSourceViews tree(cache.GetOrCreate(widget_.get()), &cache); - EXPECT_FALSE(tree.IsValid(cache.GetOrCreate(ignored_view))); + EXPECT_TRUE(tree.IsValid(cache.GetOrCreate(ignored_view))); } } // namespace diff --git a/chromium/ui/views/accessibility/ax_virtual_view_unittest.cc b/chromium/ui/views/accessibility/ax_virtual_view_unittest.cc index cdb60abc79f..e7520c33976 100644 --- a/chromium/ui/views/accessibility/ax_virtual_view_unittest.cc +++ b/chromium/ui/views/accessibility/ax_virtual_view_unittest.cc @@ -343,5 +343,47 @@ TEST_F(AXVirtualViewTest, OverrideFocus) { button_accessibility.GetFocusedDescendant()); } +TEST_F(AXVirtualViewTest, Navigation) { + ASSERT_EQ(0, virtual_label_->GetChildCount()); + + AXVirtualView* virtual_child_1 = new AXVirtualView; + virtual_label_->AddChildView(base::WrapUnique(virtual_child_1)); + EXPECT_EQ(1, virtual_label_->GetChildCount()); + + AXVirtualView* virtual_child_2 = new AXVirtualView; + virtual_label_->AddChildView(base::WrapUnique(virtual_child_2)); + EXPECT_EQ(2, virtual_label_->GetChildCount()); + + AXVirtualView* virtual_child_3 = new AXVirtualView; + virtual_label_->AddChildView(base::WrapUnique(virtual_child_3)); + + AXVirtualView* virtual_child_4 = new AXVirtualView; + virtual_child_2->AddChildView(base::WrapUnique(virtual_child_4)); + + EXPECT_EQ(nullptr, virtual_label_->GetNextSibling()); + EXPECT_EQ(nullptr, virtual_label_->GetPreviousSibling()); + EXPECT_EQ(0, virtual_label_->GetIndexInParent()); + + EXPECT_EQ(virtual_child_2->GetNativeObject(), + virtual_child_1->GetNextSibling()); + EXPECT_EQ(nullptr, virtual_child_1->GetPreviousSibling()); + EXPECT_EQ(0, virtual_child_1->GetIndexInParent()); + + EXPECT_EQ(virtual_child_3->GetNativeObject(), + virtual_child_2->GetNextSibling()); + EXPECT_EQ(virtual_child_1->GetNativeObject(), + virtual_child_2->GetPreviousSibling()); + EXPECT_EQ(1, virtual_child_2->GetIndexInParent()); + + EXPECT_EQ(nullptr, virtual_child_3->GetNextSibling()); + EXPECT_EQ(virtual_child_2->GetNativeObject(), + virtual_child_3->GetPreviousSibling()); + EXPECT_EQ(2, virtual_child_3->GetIndexInParent()); + + EXPECT_EQ(nullptr, virtual_child_4->GetNextSibling()); + EXPECT_EQ(nullptr, virtual_child_4->GetPreviousSibling()); + EXPECT_EQ(0, virtual_child_4->GetIndexInParent()); +} + } // namespace test } // namespace views diff --git a/chromium/ui/views/accessibility/view_accessibility.cc b/chromium/ui/views/accessibility/view_accessibility.cc index aca46ef2d5c..4bc4f4067c3 100644 --- a/chromium/ui/views/accessibility/view_accessibility.cc +++ b/chromium/ui/views/accessibility/view_accessibility.cc @@ -189,6 +189,14 @@ void ViewAccessibility::GetAccessibleNodeData(ui::AXNodeData* data) const { data->AddStringAttribute(ax::mojom::StringAttribute::kClassName, view_->GetClassName()); + if (IsIgnored()) { + // Prevent screen readers from navigating to or speaking ignored nodes. + data->AddState(ax::mojom::State::kInvisible); + data->AddState(ax::mojom::State::kIgnored); + data->role = ax::mojom::Role::kIgnored; + return; + } + if (view_->IsAccessibilityFocusable()) data->AddState(ax::mojom::State::kFocusable); diff --git a/chromium/ui/views/accessibility/view_ax_platform_node_delegate.cc b/chromium/ui/views/accessibility/view_ax_platform_node_delegate.cc index 49769ae210c..fed95665862 100644 --- a/chromium/ui/views/accessibility/view_ax_platform_node_delegate.cc +++ b/chromium/ui/views/accessibility/view_ax_platform_node_delegate.cc @@ -399,10 +399,101 @@ base::string16 ViewAXPlatformNodeDelegate::GetAuthorUniqueId() const { return base::string16(); } +bool ViewAXPlatformNodeDelegate::IsMinimized() const { + Widget* widget = view()->GetWidget(); + return widget && widget->IsMinimized(); +} + const ui::AXUniqueId& ViewAXPlatformNodeDelegate::GetUniqueId() const { return ViewAccessibility::GetUniqueId(); } +bool ViewAXPlatformNodeDelegate::IsOrderedSetItem() const { + const ui::AXNodeData& data = GetData(); + return (view()->GetGroup() >= 0) || + (data.HasIntAttribute(ax::mojom::IntAttribute::kPosInSet) && + data.HasIntAttribute(ax::mojom::IntAttribute::kSetSize)); +} + +bool ViewAXPlatformNodeDelegate::IsOrderedSet() const { + return (view()->GetGroup() >= 0) || + GetData().HasIntAttribute(ax::mojom::IntAttribute::kSetSize); +} + +base::Optional<int> ViewAXPlatformNodeDelegate::GetPosInSet() const { + // Consider overridable attributes first. + const ui::AXNodeData& data = GetData(); + if (data.HasIntAttribute(ax::mojom::IntAttribute::kPosInSet)) + return data.GetIntAttribute(ax::mojom::IntAttribute::kPosInSet); + + std::vector<View*> views_in_group; + GetViewsInGroupForSet(&views_in_group); + if (views_in_group.empty()) + return base::nullopt; + // Check this is in views_in_group; it may be removed if it is ignored. + auto found_view = + std::find(views_in_group.begin(), views_in_group.end(), view()); + if (found_view == views_in_group.end()) + return base::nullopt; + + int posInSet = std::distance(views_in_group.begin(), found_view); + // posInSet is zero-based; users expect one-based, so increment. + return ++posInSet; +} + +base::Optional<int> ViewAXPlatformNodeDelegate::GetSetSize() const { + // Consider overridable attributes first. + const ui::AXNodeData& data = GetData(); + if (data.HasIntAttribute(ax::mojom::IntAttribute::kSetSize)) + return data.GetIntAttribute(ax::mojom::IntAttribute::kSetSize); + + std::vector<View*> views_in_group; + GetViewsInGroupForSet(&views_in_group); + if (views_in_group.empty()) + return base::nullopt; + // Check this is in views_in_group; it may be removed if it is ignored. + auto found_view = + std::find(views_in_group.begin(), views_in_group.end(), view()); + if (found_view == views_in_group.end()) + return base::nullopt; + + return views_in_group.size(); +} + +void ViewAXPlatformNodeDelegate::GetViewsInGroupForSet( + std::vector<View*>* views_in_group) const { + const int group_id = view()->GetGroup(); + if (group_id < 0) + return; + + View* view_to_check = view(); + // If this view has a parent, check from the parent, to make sure we catch any + // siblings. + if (view()->parent()) + view_to_check = view()->parent(); + view_to_check->GetViewsInGroup(group_id, views_in_group); + + // Remove any views that are ignored in the accessibility tree. + views_in_group->erase( + std::remove_if( + views_in_group->begin(), views_in_group->end(), + [](View* view) { + ViewAccessibility& view_accessibility = + view->GetViewAccessibility(); + bool is_ignored = view_accessibility.IsIgnored(); + // TODO Remove the ViewAXPlatformNodeDelegate::GetData() part of + // this lambda, once the temporary code in GetData() setting the + // role to kIgnored is moved to ViewAccessibility. + ViewAXPlatformNodeDelegate* ax_delegate = + static_cast<ViewAXPlatformNodeDelegate*>(&view_accessibility); + if (ax_delegate) + is_ignored = is_ignored || (ax_delegate->GetData().role == + ax::mojom::Role::kIgnored); + return is_ignored; + }), + views_in_group->end()); +} + ViewAXPlatformNodeDelegate::ChildWidgetsResult ViewAXPlatformNodeDelegate::GetChildWidgets() const { // Only attach child widgets to the root view. diff --git a/chromium/ui/views/accessibility/view_ax_platform_node_delegate.h b/chromium/ui/views/accessibility/view_ax_platform_node_delegate.h index bc16cfef9fd..d6fed0ddaea 100644 --- a/chromium/ui/views/accessibility/view_ax_platform_node_delegate.h +++ b/chromium/ui/views/accessibility/view_ax_platform_node_delegate.h @@ -62,13 +62,24 @@ class ViewAXPlatformNodeDelegate : public ViewAccessibility, bool ShouldIgnoreHoveredStateForTesting() override; bool IsOffscreen() const override; base::string16 GetAuthorUniqueId() const override; + bool IsMinimized() const override; // Also in |ViewAccessibility|. const ui::AXUniqueId& GetUniqueId() const override; + // Ordered-set-like and item-like nodes. + bool IsOrderedSetItem() const override; + bool IsOrderedSet() const override; + base::Optional<int> GetPosInSet() const override; + base::Optional<int> GetSetSize() const override; + protected: explicit ViewAXPlatformNodeDelegate(View* view); private: + // Uses Views::GetViewsInGroup to find nearby Views in the same group. + // Searches from the View's parent to include siblings within that group. + void GetViewsInGroupForSet(std::vector<View*>* views_in_group) const; + struct ChildWidgetsResult; ChildWidgetsResult GetChildWidgets() const; diff --git a/chromium/ui/views/accessibility/view_ax_platform_node_delegate_auralinux.cc b/chromium/ui/views/accessibility/view_ax_platform_node_delegate_auralinux.cc index aa9c6ef4c9c..cd191e5ffdd 100644 --- a/chromium/ui/views/accessibility/view_ax_platform_node_delegate_auralinux.cc +++ b/chromium/ui/views/accessibility/view_ax_platform_node_delegate_auralinux.cc @@ -16,6 +16,7 @@ #include "ui/accessibility/ax_node_data.h" #include "ui/accessibility/platform/ax_platform_node_auralinux.h" #include "ui/accessibility/platform/ax_platform_node_delegate_base.h" +#include "ui/aura/window.h" #include "ui/gfx/native_widget_types.h" #include "ui/views/view.h" #include "ui/views/views_delegate.h" @@ -33,7 +34,8 @@ namespace { // top-level widget to a vector so we can return the list of all top-level // windows as children of this application object. class AuraLinuxApplication : public ui::AXPlatformNodeDelegateBase, - public WidgetObserver { + public WidgetObserver, + public aura::WindowObserver { public: // Get the single instance of this class. static AuraLinuxApplication* GetInstance() { @@ -48,11 +50,16 @@ class AuraLinuxApplication : public ui::AXPlatformNodeDelegateBase, return; widget = widget->GetTopLevelWidget(); - if (base::ContainsValue(widgets_, widget)) + if (base::Contains(widgets_, widget)) return; widgets_.push_back(widget); widget->AddObserver(this); + + aura::Window* window = widget->GetNativeWindow(); + if (!window) + return; + window->AddObserver(this); } gfx::NativeViewAccessible GetNativeViewAccessible() { @@ -69,6 +76,20 @@ class AuraLinuxApplication : public ui::AXPlatformNodeDelegateBase, widgets_.erase(iter); } + void OnWindowVisibilityChanged(aura::Window* window, bool visible) override { + for (Widget* widget : widgets_) { + if (widget->GetNativeWindow() != window) + continue; + + View* root_view = widget->GetRootView(); + if (!root_view) + continue; + + root_view->NotifyAccessibilityEvent( + ax::mojom::Event::kWindowVisibilityChanged, true); + } + } + // ui::AXPlatformNodeDelegate: const ui::AXNodeData& GetData() const override { return data_; } diff --git a/chromium/ui/views/accessibility/view_ax_platform_node_delegate_mac.mm b/chromium/ui/views/accessibility/view_ax_platform_node_delegate_mac.mm index de71650a132..5a1d1f28924 100644 --- a/chromium/ui/views/accessibility/view_ax_platform_node_delegate_mac.mm +++ b/chromium/ui/views/accessibility/view_ax_platform_node_delegate_mac.mm @@ -6,7 +6,7 @@ #include <memory> -#include "ui/views/cocoa/bridged_native_widget_host_impl.h" +#include "ui/views/cocoa/native_widget_mac_ns_window_host.h" #include "ui/views/view.h" #include "ui/views/widget/widget.h" @@ -31,12 +31,12 @@ gfx::NativeViewAccessible ViewAXPlatformNodeDelegateMac::GetNSWindow() { if (!top_level_widget) return nil; - auto* bridge_host = BridgedNativeWidgetHostImpl::GetFromNativeWindow( + auto* window_host = NativeWidgetMacNSWindowHost::GetFromNativeWindow( top_level_widget->GetNativeWindow()); - if (!bridge_host) + if (!window_host) return nil; - return bridge_host->GetNativeViewAccessibleForNSWindow(); + return window_host->GetNativeViewAccessibleForNSWindow(); } gfx::NativeViewAccessible ViewAXPlatformNodeDelegateMac::GetParent() { @@ -47,12 +47,12 @@ gfx::NativeViewAccessible ViewAXPlatformNodeDelegateMac::GetParent() { if (!widget) return nil; - auto* bridge_host = BridgedNativeWidgetHostImpl::GetFromNativeWindow( + auto* window_host = NativeWidgetMacNSWindowHost::GetFromNativeWindow( view()->GetWidget()->GetNativeWindow()); - if (!bridge_host) + if (!window_host) return nil; - return bridge_host->GetNativeViewAccessibleForNSView(); + return window_host->GetNativeViewAccessibleForNSView(); } } // namespace views diff --git a/chromium/ui/views/accessibility/view_ax_platform_node_delegate_unittest.cc b/chromium/ui/views/accessibility/view_ax_platform_node_delegate_unittest.cc index 5671f5fb3b3..a0727e4bad4 100644 --- a/chromium/ui/views/accessibility/view_ax_platform_node_delegate_unittest.cc +++ b/chromium/ui/views/accessibility/view_ax_platform_node_delegate_unittest.cc @@ -78,6 +78,11 @@ class ViewAXPlatformNodeDelegateTest : public ViewsTestBase { &label_->GetViewAccessibility()); } + ViewAXPlatformNodeDelegate* view_accessibility(View* view) { + return static_cast<ViewAXPlatformNodeDelegate*>( + &view->GetViewAccessibility()); + } + bool SetFocused(ViewAXPlatformNodeDelegate* ax_delegate, bool focused) { ui::AXActionData data; data.action = @@ -85,6 +90,58 @@ class ViewAXPlatformNodeDelegateTest : public ViewsTestBase { return ax_delegate->AccessibilityPerformAction(data); } + // Sets up a more complicated structure of Views - one parent View with four + // child Views. + std::vector<View*> SetUpExtraViews() { + View* parent_view = new View(); + widget_->GetContentsView()->AddChildView(parent_view); + std::vector<View*> views{parent_view}; + + const int num_children = 4; + for (int i = 0; i < num_children; i++) { + View* child_view = new View(); + parent_view->AddChildView(child_view); + views.push_back(child_view); + } + return views; + } + + // Adds group id information to the first 5 values in |views|. If |views| is + // empty, populates it with one parent View and four child Views. It is + // assumed |views| is either empty or has at least 5 items. + void SetUpExtraViewsWithGroups(std::vector<View*>& views) { + // v[0] g1 + // | | | | + // v[1] g1 v[2] g1 v[3] g2 v[4] + if (views.empty()) + views = SetUpExtraViews(); + EXPECT_GE(views.size(), (size_t)5); + + views[0]->SetGroup(1); + views[1]->SetGroup(1); + views[2]->SetGroup(1); + views[3]->SetGroup(2); + // Skip views[4] - no group id. + } + + // Adds posInSet and setSize overrides to the first 5 values in |views|. If + // |views| is empty, populates it with one parent View and four child Views. + // It is assumed |views| is either empty or has at least 5 items. + void SetUpExtraViewsWithSetOverrides(std::vector<View*>& views) { + // v[0] p4 s4 + // | | | | + // v[1] p3 s4 v[2] p2 s4 v[3] p- s- v[4] p1 s4 + if (views.empty()) + views = SetUpExtraViews(); + EXPECT_GE(views.size(), (size_t)5); + + views[0]->GetViewAccessibility().OverridePosInSet(4, 4); + views[1]->GetViewAccessibility().OverridePosInSet(3, 4); + views[2]->GetViewAccessibility().OverridePosInSet(2, 4); + // Skip views[3] - no override. + views[4]->GetViewAccessibility().OverridePosInSet(1, 4); + } + protected: const int DEFAULT_VIEW_ID = 0; const int NON_DEFAULT_VIEW_ID = 1; @@ -185,6 +242,132 @@ TEST_F(ViewAXPlatformNodeDelegateTest, GetAuthorUniqueIdNonDefault) { button_accessibility()->GetAuthorUniqueId()); } +TEST_F(ViewAXPlatformNodeDelegateTest, IsOrderedSet) { + std::vector<View*> group_ids; + SetUpExtraViewsWithGroups(group_ids); + // Only last element has no group id. + EXPECT_TRUE(view_accessibility(group_ids[0])->IsOrderedSet()); + EXPECT_TRUE(view_accessibility(group_ids[1])->IsOrderedSet()); + EXPECT_TRUE(view_accessibility(group_ids[2])->IsOrderedSet()); + EXPECT_TRUE(view_accessibility(group_ids[3])->IsOrderedSet()); + EXPECT_FALSE(view_accessibility(group_ids[4])->IsOrderedSet()); + + EXPECT_TRUE(view_accessibility(group_ids[0])->IsOrderedSetItem()); + EXPECT_TRUE(view_accessibility(group_ids[1])->IsOrderedSetItem()); + EXPECT_TRUE(view_accessibility(group_ids[2])->IsOrderedSetItem()); + EXPECT_TRUE(view_accessibility(group_ids[3])->IsOrderedSetItem()); + EXPECT_FALSE(view_accessibility(group_ids[4])->IsOrderedSetItem()); + + std::vector<View*> overrides; + SetUpExtraViewsWithSetOverrides(overrides); + // Only overrides[3] has no override values for setSize/ posInSet. + EXPECT_TRUE(view_accessibility(overrides[0])->IsOrderedSet()); + EXPECT_TRUE(view_accessibility(overrides[1])->IsOrderedSet()); + EXPECT_TRUE(view_accessibility(overrides[2])->IsOrderedSet()); + EXPECT_FALSE(view_accessibility(overrides[3])->IsOrderedSet()); + EXPECT_TRUE(view_accessibility(overrides[4])->IsOrderedSet()); + + EXPECT_TRUE(view_accessibility(overrides[0])->IsOrderedSetItem()); + EXPECT_TRUE(view_accessibility(overrides[1])->IsOrderedSetItem()); + EXPECT_TRUE(view_accessibility(overrides[2])->IsOrderedSetItem()); + EXPECT_FALSE(view_accessibility(overrides[3])->IsOrderedSetItem()); + EXPECT_TRUE(view_accessibility(overrides[4])->IsOrderedSetItem()); +} + +TEST_F(ViewAXPlatformNodeDelegateTest, SetSizeAndPosition) { + // Test Views with group ids. + std::vector<View*> group_ids; + SetUpExtraViewsWithGroups(group_ids); + EXPECT_EQ(view_accessibility(group_ids[0])->GetSetSize(), 3); + EXPECT_EQ(view_accessibility(group_ids[0])->GetPosInSet(), 1); + EXPECT_EQ(view_accessibility(group_ids[1])->GetSetSize(), 3); + EXPECT_EQ(view_accessibility(group_ids[1])->GetPosInSet(), 2); + EXPECT_EQ(view_accessibility(group_ids[2])->GetSetSize(), 3); + EXPECT_EQ(view_accessibility(group_ids[2])->GetPosInSet(), 3); + + EXPECT_EQ(view_accessibility(group_ids[3])->GetSetSize(), 1); + EXPECT_EQ(view_accessibility(group_ids[3])->GetPosInSet(), 1); + + EXPECT_FALSE(view_accessibility(group_ids[4])->GetSetSize().has_value()); + EXPECT_FALSE(view_accessibility(group_ids[4])->GetPosInSet().has_value()); + + // Check if a View is ignored, it is not counted in SetSize or PosInSet + group_ids[1]->GetViewAccessibility().OverrideIsIgnored(true); + group_ids[2]->GetViewAccessibility().OverrideIsIgnored(true); + EXPECT_EQ(view_accessibility(group_ids[0])->GetSetSize(), 1); + EXPECT_EQ(view_accessibility(group_ids[0])->GetPosInSet(), 1); + EXPECT_FALSE(view_accessibility(group_ids[1])->GetSetSize().has_value()); + EXPECT_FALSE(view_accessibility(group_ids[1])->GetPosInSet().has_value()); + EXPECT_FALSE(view_accessibility(group_ids[2])->GetSetSize().has_value()); + EXPECT_FALSE(view_accessibility(group_ids[2])->GetPosInSet().has_value()); + group_ids[1]->GetViewAccessibility().OverrideIsIgnored(false); + group_ids[2]->GetViewAccessibility().OverrideIsIgnored(false); + + // Test Views with setSize/ posInSet override values set. + std::vector<View*> overrides; + SetUpExtraViewsWithSetOverrides(overrides); + EXPECT_EQ(view_accessibility(overrides[0])->GetSetSize(), 4); + EXPECT_EQ(view_accessibility(overrides[0])->GetPosInSet(), 4); + EXPECT_EQ(view_accessibility(overrides[1])->GetSetSize(), 4); + EXPECT_EQ(view_accessibility(overrides[1])->GetPosInSet(), 3); + EXPECT_EQ(view_accessibility(overrides[2])->GetSetSize(), 4); + EXPECT_EQ(view_accessibility(overrides[2])->GetPosInSet(), 2); + + EXPECT_FALSE(view_accessibility(overrides[3])->GetSetSize().has_value()); + EXPECT_FALSE(view_accessibility(overrides[3])->GetPosInSet().has_value()); + + EXPECT_EQ(view_accessibility(overrides[4])->GetSetSize(), 4); + EXPECT_EQ(view_accessibility(overrides[4])->GetPosInSet(), 1); + + // Test Views with both group ids and setSize/ posInSet override values set. + // Make sure the override values take precedence when both are set. + // Add setSize/ posInSet overrides to the Views with group ids. + SetUpExtraViewsWithSetOverrides(group_ids); + EXPECT_EQ(view_accessibility(group_ids[0])->GetSetSize(), 4); + EXPECT_EQ(view_accessibility(group_ids[0])->GetPosInSet(), 4); + EXPECT_EQ(view_accessibility(group_ids[1])->GetSetSize(), 4); + EXPECT_EQ(view_accessibility(group_ids[1])->GetPosInSet(), 3); + EXPECT_EQ(view_accessibility(group_ids[2])->GetSetSize(), 4); + EXPECT_EQ(view_accessibility(group_ids[2])->GetPosInSet(), 2); + + EXPECT_EQ(view_accessibility(group_ids[3])->GetSetSize(), 1); + EXPECT_EQ(view_accessibility(group_ids[3])->GetPosInSet(), 1); + + EXPECT_EQ(view_accessibility(group_ids[4])->GetSetSize(), 4); + EXPECT_EQ(view_accessibility(group_ids[4])->GetPosInSet(), 1); +} + +TEST_F(ViewAXPlatformNodeDelegateTest, Navigation) { + std::vector<View*> view_ids = SetUpExtraViews(); + + EXPECT_EQ(view_accessibility(view_ids[0])->GetNextSibling(), nullptr); + EXPECT_EQ(view_accessibility(view_ids[0])->GetPreviousSibling(), + view_accessibility(button_)->GetNativeObject()); + EXPECT_EQ(view_accessibility(view_ids[0])->GetIndexInParent(), 3); + + EXPECT_EQ(view_accessibility(view_ids[1])->GetNextSibling(), + view_accessibility(view_ids[2])->GetNativeObject()); + EXPECT_EQ(view_accessibility(view_ids[1])->GetPreviousSibling(), nullptr); + EXPECT_EQ(view_accessibility(view_ids[1])->GetIndexInParent(), 0); + + EXPECT_EQ(view_accessibility(view_ids[2])->GetNextSibling(), + view_accessibility(view_ids[3])->GetNativeObject()); + EXPECT_EQ(view_accessibility(view_ids[2])->GetPreviousSibling(), + view_accessibility(view_ids[1])->GetNativeObject()); + EXPECT_EQ(view_accessibility(view_ids[2])->GetIndexInParent(), 1); + + EXPECT_EQ(view_accessibility(view_ids[3])->GetNextSibling(), + view_accessibility(view_ids[4])->GetNativeObject()); + EXPECT_EQ(view_accessibility(view_ids[3])->GetPreviousSibling(), + view_accessibility(view_ids[2])->GetNativeObject()); + EXPECT_EQ(view_accessibility(view_ids[3])->GetIndexInParent(), 2); + + EXPECT_EQ(view_accessibility(view_ids[4])->GetNextSibling(), nullptr); + EXPECT_EQ(view_accessibility(view_ids[4])->GetPreviousSibling(), + view_accessibility(view_ids[3])->GetNativeObject()); + EXPECT_EQ(view_accessibility(view_ids[4])->GetIndexInParent(), 3); +} + #if defined(USE_AURA) class DerivedTestView : public View { public: diff --git a/chromium/ui/views/accessible_pane_view.cc b/chromium/ui/views/accessible_pane_view.cc index 2e2229db524..73b501c64e4 100644 --- a/chromium/ui/views/accessible_pane_view.cc +++ b/chromium/ui/views/accessible_pane_view.cc @@ -255,4 +255,8 @@ views::View* AccessiblePaneView::GetFocusTraversableParentView() { return nullptr; } +BEGIN_METADATA(AccessiblePaneView) +METADATA_PARENT_CLASS(View) +END_METADATA() + } // namespace views diff --git a/chromium/ui/views/accessible_pane_view.h b/chromium/ui/views/accessible_pane_view.h index d4d0c8aba14..1c70cd36de3 100644 --- a/chromium/ui/views/accessible_pane_view.h +++ b/chromium/ui/views/accessible_pane_view.h @@ -25,6 +25,8 @@ class VIEWS_EXPORT AccessiblePaneView : public View, public FocusChangeListener, public FocusTraversable { public: + METADATA_HEADER(AccessiblePaneView); + AccessiblePaneView(); ~AccessiblePaneView() override; diff --git a/chromium/ui/views/animation/animation_delegate_views.cc b/chromium/ui/views/animation/animation_delegate_views.cc index ce725286c52..660e42f28de 100644 --- a/chromium/ui/views/animation/animation_delegate_views.cc +++ b/chromium/ui/views/animation/animation_delegate_views.cc @@ -12,17 +12,27 @@ namespace views { AnimationDelegateViews::AnimationDelegateViews(View* view) : view_(view) { - scoped_observer_.Add(view); + if (view) + scoped_observer_.Add(view); } -AnimationDelegateViews::~AnimationDelegateViews() = default; +AnimationDelegateViews::~AnimationDelegateViews() { + // Reset the delegate so that we don't attempt to notify our observer from + // the destructor. + if (container_) + container_->set_observer(nullptr); +} void AnimationDelegateViews::AnimationContainerWasSet( gfx::AnimationContainer* container) { if (container_ == container) return; + if (container_) + container_->set_observer(nullptr); + container_ = container; + container_->set_observer(this); UpdateAnimationRunner(); } @@ -40,11 +50,22 @@ void AnimationDelegateViews::OnViewIsDeleting(View* observed_view) { UpdateAnimationRunner(); } +void AnimationDelegateViews::AnimationContainerShuttingDown( + gfx::AnimationContainer* container) { + container_ = nullptr; +} + void AnimationDelegateViews::UpdateAnimationRunner() { +#if defined(OS_CHROMEOS) + // TODO(crbug.com/969788): Re-enable this function with better ui::Compositor + // switching support. + return; +#endif // defined(OS_CHROMEOS) + if (!container_) return; - if (!view_ || !view_->GetWidget()) { + if (!view_ || !view_->GetWidget() || !view_->GetWidget()->GetCompositor()) { // TODO(https://crbug.com/960621): make sure the container has a correct // compositor-assisted runner. container_->SetAnimationRunner(nullptr); diff --git a/chromium/ui/views/animation/animation_delegate_views.h b/chromium/ui/views/animation/animation_delegate_views.h index 318d5585ca2..ee025be516f 100644 --- a/chromium/ui/views/animation/animation_delegate_views.h +++ b/chromium/ui/views/animation/animation_delegate_views.h @@ -8,6 +8,7 @@ #include <memory> #include "base/scoped_observer.h" +#include "ui/gfx/animation/animation_container_observer.h" #include "ui/gfx/animation/animation_delegate.h" #include "ui/views/view_observer.h" #include "ui/views/views_export.h" @@ -17,9 +18,11 @@ namespace views { class View; // Provides default implementaton to adapt CompositorAnimationRunner for -// Animation. -class VIEWS_EXPORT AnimationDelegateViews : public gfx::AnimationDelegate, - public ViewObserver { +// Animation. Falls back to the default animation runner when |view| is nullptr. +class VIEWS_EXPORT AnimationDelegateViews + : public gfx::AnimationDelegate, + public ViewObserver, + public gfx::AnimationContainerObserver { public: explicit AnimationDelegateViews(View* view); ~AnimationDelegateViews() override; @@ -32,6 +35,13 @@ class VIEWS_EXPORT AnimationDelegateViews : public gfx::AnimationDelegate, void OnViewRemovedFromWidget(View* observed_view) final; void OnViewIsDeleting(View* observed_view) final; + // gfx::AnimationContainerObserver: + void AnimationContainerProgressed( + gfx::AnimationContainer* container) override {} + void AnimationContainerEmpty(gfx::AnimationContainer* container) override {} + void AnimationContainerShuttingDown( + gfx::AnimationContainer* container) override; + gfx::AnimationContainer* container() { return container_; } private: diff --git a/chromium/ui/views/animation/bounds_animator.cc b/chromium/ui/views/animation/bounds_animator.cc index 90e1d3280ff..303c8ee65c8 100644 --- a/chromium/ui/views/animation/bounds_animator.cc +++ b/chromium/ui/views/animation/bounds_animator.cc @@ -18,14 +18,9 @@ BoundsAnimator::BoundsAnimator(View* parent) : AnimationDelegateViews(parent), parent_(parent), container_(new gfx::AnimationContainer()) { - container_->set_observer(this); } BoundsAnimator::~BoundsAnimator() { - // Reset the delegate so that we don't attempt to notify our observer from - // the destructor. - container_->set_observer(nullptr); - // Delete all the animations, but don't remove any child views. We assume the // view owns us and is going to be deleted anyway. for (auto& entry : data_) diff --git a/chromium/ui/views/animation/bounds_animator.h b/chromium/ui/views/animation/bounds_animator.h index ba268c91a29..8e11253f414 100644 --- a/chromium/ui/views/animation/bounds_animator.h +++ b/chromium/ui/views/animation/bounds_animator.h @@ -38,8 +38,7 @@ class View; // You can attach an AnimationDelegate to the individual animation for a view // by way of SetAnimationDelegate. Additionally you can attach an observer to // the BoundsAnimator that is notified when all animations are complete. -class VIEWS_EXPORT BoundsAnimator : public AnimationDelegateViews, - public gfx::AnimationContainerObserver { +class VIEWS_EXPORT BoundsAnimator : public AnimationDelegateViews { public: explicit BoundsAnimator(View* view); ~BoundsAnimator() override; @@ -158,8 +157,6 @@ class VIEWS_EXPORT BoundsAnimator : public AnimationDelegateViews, void AnimationProgressed(const gfx::Animation* animation) override; void AnimationEnded(const gfx::Animation* animation) override; void AnimationCanceled(const gfx::Animation* animation) override; - - // gfx::AnimationContainerObserver overrides. void AnimationContainerProgressed( gfx::AnimationContainer* container) override; void AnimationContainerEmpty(gfx::AnimationContainer* container) override; diff --git a/chromium/ui/views/animation/compositor_animation_runner.cc b/chromium/ui/views/animation/compositor_animation_runner.cc index 6ec26f29401..e5abea4d20b 100644 --- a/chromium/ui/views/animation/compositor_animation_runner.cc +++ b/chromium/ui/views/animation/compositor_animation_runner.cc @@ -65,6 +65,7 @@ void CompositorAnimationRunner::OnStart(base::TimeDelta min_interval) { if (!compositor_) return; + last_tick_ = base::TimeTicks::Now(); min_interval_ = min_interval; DCHECK(!compositor_->HasAnimationObserver(this)); compositor_->AddAnimationObserver(this); diff --git a/chromium/ui/views/animation/compositor_animation_runner_unittest.cc b/chromium/ui/views/animation/compositor_animation_runner_unittest.cc index ca380cfd204..78923d4f4df 100644 --- a/chromium/ui/views/animation/compositor_animation_runner_unittest.cc +++ b/chromium/ui/views/animation/compositor_animation_runner_unittest.cc @@ -15,7 +15,14 @@ namespace test { using CompositorAnimationRunnerTest = WidgetTest; -TEST_F(CompositorAnimationRunnerTest, BasicCoverageTest) { +// TODO(crbug.com/969788): Re-enable CompositorAnimationRunner with better +// ui::Compositor switching support. +#if defined(OS_CHROMEOS) +#define MAYBE_BasicCoverageTest DISABLED_BasicCoverageTest +#else +#define MAYBE_BasicCoverageTest BasicCoverageTest +#endif +TEST_F(CompositorAnimationRunnerTest, MAYBE_BasicCoverageTest) { WidgetAutoclosePtr widget(CreateTopLevelPlatformWidget()); widget->Show(); diff --git a/chromium/ui/views/animation/ink_drop_host_view.cc b/chromium/ui/views/animation/ink_drop_host_view.cc index 64a14517067..5f8a9ac4f16 100644 --- a/chromium/ui/views/animation/ink_drop_host_view.cc +++ b/chromium/ui/views/animation/ink_drop_host_view.cc @@ -105,7 +105,7 @@ void InkDropHostView::SetInkDropMode(InkDropMode ink_drop_mode) { void InkDropHostView::AnimateInkDrop(InkDropState state, const ui::LocatedEvent* event) { - ink_drop_event_handler_.AnimateInkDrop(state, event); + GetEventHandler()->AnimateInkDrop(state, event); } std::unique_ptr<InkDropImpl> InkDropHostView::CreateDefaultInkDropImpl() { @@ -170,9 +170,8 @@ InkDrop* InkDropHostView::GetInkDrop() { } gfx::Point InkDropHostView::GetInkDropCenterBasedOnLastEvent() const { - return ink_drop_event_handler_.GetLastRippleTriggeringEvent() - ? ink_drop_event_handler_.GetLastRippleTriggeringEvent() - ->location() + return GetEventHandler()->GetLastRippleTriggeringEvent() + ? GetEventHandler()->GetLastRippleTriggeringEvent()->location() : GetMirroredRect(GetContentsBounds()).CenterPoint(); } @@ -195,4 +194,19 @@ gfx::Size InkDropHostView::CalculateLargeInkDropSize( return gfx::ScaleToCeiledSize(gfx::Size(small_size), kLargeInkDropScale); } +const InkDropEventHandler* InkDropHostView::GetEventHandler() const { + if (ink_drop_event_handler_override_) + return ink_drop_event_handler_override_; + return &ink_drop_event_handler_; +} + +InkDropEventHandler* InkDropHostView::GetEventHandler() { + return const_cast<InkDropEventHandler*>( + const_cast<const InkDropHostView*>(this)->GetEventHandler()); +} + +BEGIN_METADATA(InkDropHostView) +METADATA_PARENT_CLASS(View) +END_METADATA() + } // namespace views diff --git a/chromium/ui/views/animation/ink_drop_host_view.h b/chromium/ui/views/animation/ink_drop_host_view.h index 30879738568..c410d9fb249 100644 --- a/chromium/ui/views/animation/ink_drop_host_view.h +++ b/chromium/ui/views/animation/ink_drop_host_view.h @@ -38,6 +38,8 @@ class InkDropHostViewTestApi; // A view that provides InkDropHost functionality. class VIEWS_EXPORT InkDropHostView : public View { public: + METADATA_HEADER(InkDropHostView); + // Used in SetInkDropMode() to specify whether the ink drop effect is enabled // or not for the view. In case of having an ink drop, it also specifies // whether the default event handler for the ink drop should be installed or @@ -111,6 +113,16 @@ class VIEWS_EXPORT InkDropHostView : public View { return ink_drop_large_corner_radius_; } + // Allows InstallableInkDrop to override our InkDropEventHandler + // instance. + // + // TODO(crbug.com/931964): Remove this, either by finishing refactor or by + // giving up. + void set_ink_drop_event_handler_override( + InkDropEventHandler* ink_drop_event_handler_override) { + ink_drop_event_handler_override_ = ink_drop_event_handler_override; + } + // Animates |ink_drop_| to the desired |ink_drop_state|. Caches |event| as the // last_ripple_triggering_event(). // @@ -204,6 +216,9 @@ class VIEWS_EXPORT InkDropHostView : public View { InkDropHostView* const host_view_; }; + const InkDropEventHandler* GetEventHandler() const; + InkDropEventHandler* GetEventHandler(); + // Defines what type of |ink_drop_| to create. InkDropMode ink_drop_mode_ = InkDropMode::OFF; @@ -215,6 +230,8 @@ class VIEWS_EXPORT InkDropHostView : public View { InkDropHostViewEventHandlerDelegate ink_drop_event_handler_delegate_; InkDropEventHandler ink_drop_event_handler_; + InkDropEventHandler* ink_drop_event_handler_override_ = nullptr; + float ink_drop_visible_opacity_ = 0.175f; // TODO(pbos): Audit call sites to make sure highlight opacity is either diff --git a/chromium/ui/views/animation/installable_ink_drop.cc b/chromium/ui/views/animation/installable_ink_drop.cc index 8e826ee4129..edfd31a63bc 100644 --- a/chromium/ui/views/animation/installable_ink_drop.cc +++ b/chromium/ui/views/animation/installable_ink_drop.cc @@ -4,25 +4,119 @@ #include "ui/views/animation/installable_ink_drop.h" +#include <algorithm> + #include "base/logging.h" +#include "base/memory/ptr_util.h" +#include "cc/paint/paint_flags.h" +#include "third_party/skia/include/core/SkColor.h" +#include "third_party/skia/include/core/SkPath.h" +#include "ui/compositor/layer.h" +#include "ui/compositor/paint_context.h" +#include "ui/compositor/paint_recorder.h" +#include "ui/gfx/animation/animation_container.h" +#include "ui/gfx/canvas.h" +#include "ui/gfx/color_palette.h" +#include "ui/gfx/geometry/rect.h" +#include "ui/gfx/geometry/rect_f.h" +#include "ui/gfx/geometry/size.h" +#include "ui/gfx/skia_util.h" +#include "ui/views/animation/compositor_animation_runner.h" +#include "ui/views/animation/ink_drop_host_view.h" +#include "ui/views/view.h" +#include "ui/views/view_class_properties.h" +#include "ui/views/widget/widget.h" namespace views { +namespace { + +InstallableInkDropConfig GetPlaceholderInstallableInkDropConfig() { + InstallableInkDropConfig config{0}; + config.base_color = gfx::kPlaceholderColor; + config.ripple_opacity = 1.0f; + config.highlight_opacity = 1.0f; + return config; +} + +} // namespace + const base::Feature kInstallableInkDropFeature{ "InstallableInkDrop", base::FEATURE_DISABLED_BY_DEFAULT}; -InstallableInkDrop::InstallableInkDrop() = default; +InstallableInkDrop::InstallableInkDrop(View* view) + : view_(view), + config_(GetPlaceholderInstallableInkDropConfig()), + layer_(std::make_unique<ui::Layer>()), + event_handler_(view_, this), + painter_(&config_, &visual_state_), + animation_container_(base::MakeRefCounted<gfx::AnimationContainer>()), + animator_(layer_->size(), + &visual_state_, + animation_container_.get(), + base::Bind(&InstallableInkDrop::SchedulePaint, + base::Unretained(this))) { + // Catch if |view_| is destroyed out from under us. + if (DCHECK_IS_ON()) + view_->AddObserver(this); + + layer_->set_delegate(this); + layer_->SetFillsBoundsOpaquely(false); + layer_->SetFillsBoundsCompletely(false); + view_->AddLayerBeneathView(layer_.get()); + + // AddLayerBeneathView() changes the location of |layer_| so this must be done + // after. + layer_->SetBounds(gfx::Rect(view_->size()) + + layer_->bounds().OffsetFromOrigin()); + layer_->SchedulePaint(gfx::Rect(layer_->size())); + + if (view_->GetWidget()) { + // Using CompositorAnimationRunner keeps our animation updates in sync with + // compositor frames and avoids jank. + animation_container_->SetAnimationRunner( + std::make_unique<CompositorAnimationRunner>( + view_->GetWidget()->GetCompositor())); + } +} -InstallableInkDrop::~InstallableInkDrop() = default; +InstallableInkDrop::InstallableInkDrop(InkDropHostView* ink_drop_host_view) + : InstallableInkDrop(static_cast<View*>(ink_drop_host_view)) { + // To get all events, we must override InkDropHostView's event handler. + ink_drop_host_view->set_ink_drop_event_handler_override(&event_handler_); + ink_drop_host_view_ = ink_drop_host_view; +} -void InstallableInkDrop::HostSizeChanged(const gfx::Size& new_size) {} +InstallableInkDrop::~InstallableInkDrop() { + view_->RemoveLayerBeneathView(layer_.get()); + if (ink_drop_host_view_) + ink_drop_host_view_->set_ink_drop_event_handler_override(nullptr); + if (DCHECK_IS_ON()) + view_->RemoveObserver(this); +} + +void InstallableInkDrop::SetConfig(InstallableInkDropConfig config) { + config_ = config; + SchedulePaint(); +} + +void InstallableInkDrop::HostSizeChanged(const gfx::Size& new_size) { + layer_->SetBounds(gfx::Rect(new_size) + layer_->bounds().OffsetFromOrigin()); + layer_->SchedulePaint(gfx::Rect(layer_->size())); + animator_.SetSize(layer_->size()); +} InkDropState InstallableInkDrop::GetTargetInkDropState() const { - return current_state_; + return animator_.target_state(); } void InstallableInkDrop::AnimateToState(InkDropState ink_drop_state) { - current_state_ = ink_drop_state; + const gfx::Point ripple_center = + event_handler_.GetLastRippleTriggeringEvent() + ? event_handler_.GetLastRippleTriggeringEvent()->location() + : view_->GetMirroredRect(view_->GetLocalBounds()).CenterPoint(); + animator_.SetLastEventLocation(ripple_center); + animator_.AnimateToState(ink_drop_state); } void InstallableInkDrop::SetHoverHighlightFadeDurationMs(int duration_ms) { @@ -34,19 +128,27 @@ void InstallableInkDrop::UseDefaultHoverHighlightFadeDuration() { } void InstallableInkDrop::SnapToActivated() { - NOTREACHED(); + // TODO(crbug.com/933384): do this without animation. + animator_.AnimateToState(InkDropState::ACTIVATED); } void InstallableInkDrop::SnapToHidden() { - NOTREACHED(); + // TODO(crbug.com/933384): do this without animation. + animator_.AnimateToState(InkDropState::HIDDEN); } -void InstallableInkDrop::SetHovered(bool is_hovered) {} +void InstallableInkDrop::SetHovered(bool is_hovered) { + is_hovered_ = is_hovered; + UpdateAnimatorHighlight(); +} -void InstallableInkDrop::SetFocused(bool is_focused) {} +void InstallableInkDrop::SetFocused(bool is_focused) { + is_focused_ = is_focused; + UpdateAnimatorHighlight(); +} bool InstallableInkDrop::IsHighlightFadingInOrVisible() const { - return false; + return is_hovered_ || is_focused_; } void InstallableInkDrop::SetShowHighlightOnHover(bool show_highlight_on_hover) { @@ -57,4 +159,61 @@ void InstallableInkDrop::SetShowHighlightOnFocus(bool show_highlight_on_focus) { NOTREACHED(); } +InkDrop* InstallableInkDrop::GetInkDrop() { + return this; +} + +bool InstallableInkDrop::HasInkDrop() const { + return true; +} + +bool InstallableInkDrop::SupportsGestureEvents() const { + return true; +} + +void InstallableInkDrop::OnViewIsDeleting(View* observed_view) { + DCHECK_EQ(view_, observed_view); + NOTREACHED() << "|this| needs to outlive the view it's installed on"; +} + +void InstallableInkDrop::OnPaintLayer(const ui::PaintContext& context) { + DCHECK_EQ(view_->size(), layer_->size()); + + ui::PaintRecorder paint_recorder(context, layer_->size()); + gfx::Canvas* canvas = paint_recorder.canvas(); + canvas->ClipPath(GetHighlightPathForView(view_), true); + + painter_.Paint(canvas, view_->size()); +} + +void InstallableInkDrop::OnDeviceScaleFactorChanged( + float old_device_scale_factor, + float new_device_scale_factor) {} + +// static +SkPath InstallableInkDrop::GetHighlightPathForView(const View* view) { + // Use the provided highlight path if there is one. + const SkPath* const path_property = view->GetProperty(kHighlightPathKey); + if (path_property) + return *path_property; + + // Otherwise, construct a default path. This will be pill shaped, or circular + // when |view| is square. + SkPath path; + + const float radius = + std::min(view->size().width(), view->size().height()) / 2.0f; + path.addRoundRect(gfx::RectToSkRect(view->GetLocalBounds()), radius, radius); + + return path; +} + +void InstallableInkDrop::SchedulePaint() { + layer_->SchedulePaint(gfx::Rect(layer_->size())); +} + +void InstallableInkDrop::UpdateAnimatorHighlight() { + animator_.AnimateHighlight(is_hovered_ || is_focused_); +} + } // namespace views diff --git a/chromium/ui/views/animation/installable_ink_drop.h b/chromium/ui/views/animation/installable_ink_drop.h index 0670195ea20..a61c8944a57 100644 --- a/chromium/ui/views/animation/installable_ink_drop.h +++ b/chromium/ui/views/animation/installable_ink_drop.h @@ -5,25 +5,68 @@ #ifndef UI_VIEWS_ANIMATION_INSTALLABLE_INK_DROP_H_ #define UI_VIEWS_ANIMATION_INSTALLABLE_INK_DROP_H_ +#include <memory> + #include "base/feature_list.h" -#include "ui/gfx/geometry/size.h" +#include "base/memory/scoped_refptr.h" +#include "ui/compositor/layer_delegate.h" #include "ui/views/animation/ink_drop.h" +#include "ui/views/animation/ink_drop_event_handler.h" #include "ui/views/animation/ink_drop_state.h" +#include "ui/views/animation/installable_ink_drop_animator.h" +#include "ui/views/animation/installable_ink_drop_config.h" +#include "ui/views/animation/installable_ink_drop_painter.h" +#include "ui/views/view_observer.h" #include "ui/views/views_export.h" +class SkPath; + +namespace gfx { +class AnimationContainer; +class Size; +} // namespace gfx + +namespace ui { +class Layer; +class PaintContext; +} // namespace ui + namespace views { +class InkDropHostView; +class View; + extern const VIEWS_EXPORT base::Feature kInstallableInkDropFeature; // Stub for future InkDrop implementation that will be installable on any View // without needing InkDropHostView. This is currently non-functional and fails // on some method calls. TODO(crbug.com/931964): implement the necessary parts // of the API and remove the rest from the InkDrop interface. -class VIEWS_EXPORT InstallableInkDrop : public InkDrop { +class VIEWS_EXPORT InstallableInkDrop : public InkDrop, + public InkDropEventHandler::Delegate, + public ui::LayerDelegate, + public ViewObserver { public: - InstallableInkDrop(); + // Create ink drop for |view|. Note that |view| must live longer than us. + explicit InstallableInkDrop(View* view); + + // Overload for working within the InkDropHostView hierarchy. Similar to + // above, |ink_drop_host_view| must outlive us. + // + // TODO(crbug.com/931964): Remove this. + explicit InstallableInkDrop(InkDropHostView* ink_drop_host_view); + + InstallableInkDrop(const InstallableInkDrop&) = delete; + InstallableInkDrop(InstallableInkDrop&&) = delete; + ~InstallableInkDrop() override; + void SetConfig(InstallableInkDropConfig config); + InstallableInkDropConfig config() const { return config_; } + + // Should only be used for inspecting properties of the layer in tests. + const ui::Layer* layer_for_testing() const { return layer_.get(); } + // InkDrop: void HostSizeChanged(const gfx::Size& new_size) override; InkDropState GetTargetInkDropState() const override; @@ -38,8 +81,62 @@ class VIEWS_EXPORT InstallableInkDrop : public InkDrop { void SetShowHighlightOnHover(bool show_highlight_on_hover) override; void SetShowHighlightOnFocus(bool show_highlight_on_focus) override; + // InkDropEventHandler::Delegate: + InkDrop* GetInkDrop() override; + bool HasInkDrop() const override; + bool SupportsGestureEvents() const override; + + // ViewObserver: + void OnViewIsDeleting(View* observed_view) override; + + // ui::LayerDelegate: + void OnPaintLayer(const ui::PaintContext& context) override; + void OnDeviceScaleFactorChanged(float old_device_scale_factor, + float new_device_scale_factor) override; + + // Gets the path that the ink drop fills in for the highlight. This uses + // |kHighlightPathKey| if provided but falls back to a pill-shaped path. + static SkPath GetHighlightPathForView(const View* view); + private: - InkDropState current_state_ = InkDropState::HIDDEN; + void SchedulePaint(); + void UpdateAnimatorHighlight(); + + // The view this ink drop is showing for. |layer_| is added to the layer + // hierarchy that |view_| belongs to. We track events on |view_| to update our + // visual state. + View* const view_; + + // If we were installed on an InkDropHostView, this will be non-null. We store + // this to to remove our InkDropEventHandler override. + InkDropHostView* ink_drop_host_view_ = nullptr; + + // Contains the colors and opacities used to paint. + InstallableInkDropConfig config_; + + // The layer we paint to. + std::unique_ptr<ui::Layer> layer_; + + // Observes |view_| and updates our visual state accordingly. + InkDropEventHandler event_handler_; + + // Completely describes the current visual state of the ink drop, including + // progress of animations. + InstallableInkDropPainter::State visual_state_; + + // Handles painting |visual_state_| on request. + InstallableInkDropPainter painter_; + + // Used to synchronize the hover and activation animations within this ink + // drop. Since we use |views::CompositorAnimationRunner|, this also + // synchronizes them with compositor frames. + scoped_refptr<gfx::AnimationContainer> animation_container_; + + // Manages our animations and maniuplates |visual_state_| for us. + InstallableInkDropAnimator animator_; + + bool is_hovered_ = false; + bool is_focused_ = false; }; } // namespace views diff --git a/chromium/ui/views/animation/installable_ink_drop_animator.cc b/chromium/ui/views/animation/installable_ink_drop_animator.cc new file mode 100644 index 00000000000..0445cdf8d96 --- /dev/null +++ b/chromium/ui/views/animation/installable_ink_drop_animator.cc @@ -0,0 +1,257 @@ +// 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. + +#include "ui/views/animation/installable_ink_drop_animator.h" + +#include <algorithm> + +#include "base/bind.h" +#include "base/logging.h" +#include "base/trace_event/trace_event.h" +#include "ui/gfx/animation/tween.h" +#include "ui/gfx/geometry/point_f.h" +#include "ui/gfx/geometry/rect_conversions.h" +#include "ui/gfx/geometry/rect_f.h" +#include "ui/gfx/geometry/size_f.h" +#include "ui/gfx/geometry/vector2d_f.h" +#include "ui/views/animation/installable_ink_drop_painter.h" + +namespace views { + +// static +constexpr base::TimeDelta InstallableInkDropAnimator::kAnimationDuration; + +InstallableInkDropAnimator::InstallableInkDropAnimator( + gfx::Size size, + InstallableInkDropPainter::State* visual_state, + gfx::AnimationContainer* animation_container, + base::RepeatingClosure repaint_callback) + : size_(size), + visual_state_(visual_state), + repaint_callback_(repaint_callback), + highlight_animation_(this), + flood_fill_animation_(this), + fade_out_animation_(this) { + highlight_animation_.SetContainer(animation_container); + highlight_animation_.SetSlideDuration(kAnimationDuration.InMilliseconds()); + + flood_fill_animation_.SetContainer(animation_container); + flood_fill_animation_.SetDuration(kAnimationDuration); + + fade_out_animation_.SetContainer(animation_container); + fade_out_animation_.SetDuration(kAnimationDuration); +} + +InstallableInkDropAnimator::~InstallableInkDropAnimator() = default; + +void InstallableInkDropAnimator::SetSize(gfx::Size size) { + size_ = size; +} + +void InstallableInkDropAnimator::SetLastEventLocation( + gfx::Point last_event_location) { + last_event_location_ = last_event_location; +} + +void InstallableInkDropAnimator::AnimateToState(InkDropState target_state) { + VerifyAnimationState(); + + const InkDropState last_state = target_state_; + + TRACE_EVENT2("views", "InstallableInkDropAnimator::AnimateToState", + "target_state", target_state, "last_state", last_state); + + switch (target_state) { + case InkDropState::HIDDEN: + case InkDropState::DEACTIVATED: + // If we weren't animating to a visible state, we shouldn't do anything. + if (last_state == InkDropState::HIDDEN || + last_state == InkDropState::DEACTIVATED) { + break; + } + // If we were flood-filling, jump to the end of that animation. + if (flood_fill_animation_.is_animating()) { + flood_fill_animation_.End(); + } + // If we weren't already fading out, start fading out. Otherwise, just + // continue fading out. + if (!fade_out_animation_.is_animating()) + fade_out_animation_.Start(); + break; + + case InkDropState::ACTION_PENDING: + if (last_state != InkDropState::HIDDEN) { + // Snap to hidden state by stopping all animations. + flood_fill_animation_.Stop(); + fade_out_animation_.Stop(); + } + flood_fill_animation_.Start(); + break; + + case InkDropState::ACTION_TRIGGERED: + if (last_state == InkDropState::HIDDEN) { + // Start the flood fill. On this animation's end, we will start the fade + // out animation in AnimationEnded(). + flood_fill_animation_.Start(); + } else if (last_state == InkDropState::ACTION_PENDING && + !flood_fill_animation_.is_animating()) { + // If we were done animating to ACTION_PENDING, we must start the fade + // out animation here. + fade_out_animation_.Start(); + // If we were in ACTION_PENDING but weren't done animating, we will + // start the fade out animation in AnimationEnded(). + } else if (last_state != InkDropState::ACTION_PENDING) { + // Any other state transitions are invalid. + NOTREACHED() << "Transition from " << ToString(last_state) + << " to ACTION_TRIGGERED is invalid."; + } + break; + + case InkDropState::ALTERNATE_ACTION_PENDING: + case InkDropState::ALTERNATE_ACTION_TRIGGERED: + // TODO(crbug.com/933384): handle these. + NOTREACHED() << "target_state = " << ToString(target_state); + break; + + case InkDropState::ACTIVATED: + if (fade_out_animation_.is_animating()) { + // If we were fading out of ACTIVATED or ACTION_TRIGGERED, finish the + // animation to reset to HIDDEN state. + fade_out_animation_.End(); + } + + // Now simply start the flood fill animation again. + flood_fill_animation_.Start(); + break; + } + + target_state_ = target_state; + VerifyAnimationState(); + repaint_callback_.Run(); +} + +void InstallableInkDropAnimator::AnimateHighlight(bool fade_in) { + if (fade_in) { + highlight_animation_.Show(); + } else { + highlight_animation_.Hide(); + } +} + +void InstallableInkDropAnimator::VerifyAnimationState() const { +#if DCHECK_IS_ON() + switch (target_state_) { + case InkDropState::HIDDEN: + case InkDropState::DEACTIVATED: + // We can only be fading out or completely invisible. So, we cannot be + // flood-filling. + DCHECK(!flood_fill_animation_.is_animating()); + break; + case InkDropState::ACTION_PENDING: + case InkDropState::ACTIVATED: + // These states flood-fill then stay visible. + DCHECK(!fade_out_animation_.is_animating()); + if (!flood_fill_animation_.is_animating()) + DCHECK_EQ(1.0f, visual_state_->flood_fill_progress); + break; + case InkDropState::ACTION_TRIGGERED: + // We should always be animating during this state. Once animations are + // done, we automatically transition to HIDDEN. Make sure exactly one of + // our animations are running. + DCHECK_NE(flood_fill_animation_.is_animating(), + fade_out_animation_.is_animating()); + break; + case InkDropState::ALTERNATE_ACTION_PENDING: + case InkDropState::ALTERNATE_ACTION_TRIGGERED: + // TODO(crbug.com/933384): handle these. + NOTREACHED() << "target_state = " << ToString(target_state_); + break; + } +#endif // DCHECK_IS_ON() +} + +void InstallableInkDropAnimator::AnimationEnded( + const gfx::Animation* animation) { + TRACE_EVENT0("views", "InstallableInkDropAnimator::AnimationEnded"); + + if (animation == &flood_fill_animation_) { + switch (target_state_) { + case InkDropState::ACTION_PENDING: + case InkDropState::ACTIVATED: + // The ink drop stays filled in these states so nothing needs to be + // done. + break; + + case InkDropState::ACTION_TRIGGERED: + // After filling, the ink drop should fade out. + fade_out_animation_.Start(); + break; + + case InkDropState::ALTERNATE_ACTION_PENDING: + case InkDropState::ALTERNATE_ACTION_TRIGGERED: + // TODO(crbug.com/933384): handle these. + NOTREACHED() << "target_state_ = " << ToString(target_state_); + break; + + case InkDropState::HIDDEN: + case InkDropState::DEACTIVATED: + // The flood fill animation should never run in these states. + NOTREACHED() << "target_state_ = " << ToString(target_state_); + break; + } + + // The ink drop is now fully activated. + visual_state_->flood_fill_progress = 1.0f; + repaint_callback_.Run(); + } else if (animation == &fade_out_animation_) { + switch (target_state_) { + case InkDropState::HIDDEN: + // Nothing to do. + break; + + case InkDropState::ACTION_TRIGGERED: + case InkDropState::DEACTIVATED: + // These states transition to HIDDEN when they're done animating. + target_state_ = InkDropState::HIDDEN; + break; + + case InkDropState::ACTION_PENDING: + case InkDropState::ACTIVATED: + // The fade out animation should never run in these states. + NOTREACHED() << "target_state_ = " << ToString(target_state_); + break; + + case InkDropState::ALTERNATE_ACTION_PENDING: + case InkDropState::ALTERNATE_ACTION_TRIGGERED: + // TODO(crbug.com/933384): handle these. + NOTREACHED() << "target_state_ = " << ToString(target_state_); + break; + } + + // The ink drop fill is fully invisible. + visual_state_->flood_fill_progress = 0.0f; + repaint_callback_.Run(); + } +} + +void InstallableInkDropAnimator::AnimationProgressed( + const gfx::Animation* animation) { + TRACE_EVENT0("views", "InstallableInkDropAnimator::AnimationProgressed"); + + if (animation == &highlight_animation_) { + visual_state_->highlighted_ratio = highlight_animation_.GetCurrentValue(); + } else if (animation == &flood_fill_animation_) { + visual_state_->flood_fill_center = gfx::PointF(last_event_location_); + visual_state_->flood_fill_progress = gfx::Tween::CalculateValue( + gfx::Tween::EASE_IN_OUT, flood_fill_animation_.GetCurrentValue()); + } else if (animation == &fade_out_animation_) { + // Do nothing for now. + } else { + NOTREACHED(); + } + + repaint_callback_.Run(); +} + +} // namespace views diff --git a/chromium/ui/views/animation/installable_ink_drop_animator.h b/chromium/ui/views/animation/installable_ink_drop_animator.h new file mode 100644 index 00000000000..b6bd89644f8 --- /dev/null +++ b/chromium/ui/views/animation/installable_ink_drop_animator.h @@ -0,0 +1,90 @@ +// 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. + +#ifndef UI_VIEWS_ANIMATION_INSTALLABLE_INK_DROP_ANIMATOR_H_ +#define UI_VIEWS_ANIMATION_INSTALLABLE_INK_DROP_ANIMATOR_H_ + +#include "base/callback.h" +#include "base/optional.h" +#include "base/time/time.h" +#include "base/timer/timer.h" +#include "ui/gfx/animation/animation_delegate.h" +#include "ui/gfx/animation/linear_animation.h" +#include "ui/gfx/animation/slide_animation.h" +#include "ui/gfx/geometry/point.h" +#include "ui/gfx/geometry/size.h" +#include "ui/views/animation/ink_drop_state.h" +#include "ui/views/animation/installable_ink_drop_painter.h" + +namespace gfx { +class AnimationContainer; +} + +namespace views { + +// Manages animating the ink drop's visual state. This class is essentially a +// state machine, using the current and target InkDropStates to affect the +// InstallableInkDropPainter passed in. The animations are currently minimal. +class VIEWS_EXPORT InstallableInkDropAnimator : public gfx::AnimationDelegate { + public: + // Placeholder duration used for all animations. TODO(crbug.com/933384): + // remove this and replace it with separate durations for different + // animations. + static constexpr base::TimeDelta kAnimationDuration = + base::TimeDelta::FromMilliseconds(500); + + // We use a shared gfx::AnimationContainer for our animations to allow them to + // update in sync. It is passed in at construction for two reasons: it allows + // |views::CompositorAnimationRunner| to be used for more efficient and less + // janky animations, and it enables easier unit testing. + explicit InstallableInkDropAnimator( + gfx::Size size, + InstallableInkDropPainter::State* visual_state, + gfx::AnimationContainer* animation_container, + base::RepeatingClosure repaint_callback); + ~InstallableInkDropAnimator() override; + + void SetSize(gfx::Size size); + void SetLastEventLocation(gfx::Point last_event_location); + + // Set the target state and animate to it. + void AnimateToState(InkDropState target_state); + + // Animates the hover highlight in or out. Animates in if |fade_in| is true, + // and out otherwise. + void AnimateHighlight(bool fade_in); + + InkDropState target_state() const { return target_state_; } + + private: + // Checks that the states of our animations make sense given + // |target_state_|. DCHECKs if something is wrong. + void VerifyAnimationState() const; + + // gfx::AnimationDelegate: + void AnimationEnded(const gfx::Animation* animation) override; + void AnimationProgressed(const gfx::Animation* animation) override; + + gfx::Size size_; + + // The visual state we are controlling. + InstallableInkDropPainter::State* const visual_state_; + + // Called when |visual_state_| changes so the user can repaint. + base::RepeatingClosure repaint_callback_; + + InkDropState target_state_ = InkDropState::HIDDEN; + + // Used to animate the painter's highlight value in and out. + gfx::SlideAnimation highlight_animation_; + + gfx::LinearAnimation flood_fill_animation_; + gfx::LinearAnimation fade_out_animation_; + + gfx::Point last_event_location_; +}; + +} // namespace views + +#endif // UI_VIEWS_ANIMATION_INSTALLABLE_INK_DROP_ANIMATOR_H_ diff --git a/chromium/ui/views/animation/installable_ink_drop_animator_unittest.cc b/chromium/ui/views/animation/installable_ink_drop_animator_unittest.cc new file mode 100644 index 00000000000..d11209e9842 --- /dev/null +++ b/chromium/ui/views/animation/installable_ink_drop_animator_unittest.cc @@ -0,0 +1,267 @@ +// 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. + +#include "ui/views/animation/installable_ink_drop_animator.h" + +#include "base/bind_helpers.h" +#include "base/memory/scoped_refptr.h" +#include "base/run_loop.h" +#include "base/test/scoped_task_environment.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "ui/gfx/animation/animation_test_api.h" +#include "ui/gfx/geometry/point.h" +#include "ui/gfx/geometry/point_conversions.h" +#include "ui/gfx/geometry/point_f.h" +#include "ui/gfx/geometry/rect_f.h" +#include "ui/gfx/geometry/size.h" +#include "ui/views/animation/ink_drop_state.h" +#include "ui/views/animation/installable_ink_drop_painter.h" + +namespace views { + +namespace { + +class InstallableInkDropAnimatorTest : public ::testing::Test { + public: + InstallableInkDropAnimatorTest() + : animation_container_(base::MakeRefCounted<gfx::AnimationContainer>()), + animation_tester_(animation_container_.get()), + callback_( + base::Bind([](bool* callback_called) { *callback_called = true; }, + &callback_called_)) {} + + protected: + base::test::ScopedTaskEnvironment scoped_task_environment_; + + scoped_refptr<gfx::AnimationContainer> animation_container_; + gfx::AnimationContainerTestApi animation_tester_; + + InstallableInkDropPainter::State visual_state_; + + bool callback_called_ = false; + base::RepeatingClosure callback_; +}; + +} // namespace + +TEST_F(InstallableInkDropAnimatorTest, UpdatesTargetState) { + InstallableInkDropAnimator animator(gfx::Size(2, 2), &visual_state_, + animation_container_.get(), + base::DoNothing()); + EXPECT_EQ(InkDropState::HIDDEN, animator.target_state()); + + animator.AnimateToState(InkDropState::ACTIVATED); + EXPECT_EQ(InkDropState::ACTIVATED, animator.target_state()); +} + +TEST_F(InstallableInkDropAnimatorTest, AnimateToTriggeredFromHidden) { + InstallableInkDropAnimator animator(gfx::Size(10, 10), &visual_state_, + animation_container_.get(), callback_); + EXPECT_EQ(0.0f, visual_state_.flood_fill_progress); + + animator.SetLastEventLocation(gfx::Point(5, 5)); + animator.AnimateToState(InkDropState::ACTION_TRIGGERED); + EXPECT_EQ(InkDropState::ACTION_TRIGGERED, animator.target_state()); + EXPECT_GT(1.0f, visual_state_.flood_fill_progress); + + callback_called_ = false; + animation_tester_.IncrementTime( + InstallableInkDropAnimator::kAnimationDuration); + base::RunLoop().RunUntilIdle(); + EXPECT_EQ(InkDropState::ACTION_TRIGGERED, animator.target_state()); + EXPECT_EQ(1.0f, visual_state_.flood_fill_progress); + EXPECT_TRUE(callback_called_); + + callback_called_ = false; + animation_tester_.IncrementTime( + InstallableInkDropAnimator::kAnimationDuration); + base::RunLoop().RunUntilIdle(); + EXPECT_EQ(InkDropState::HIDDEN, animator.target_state()); + EXPECT_EQ(0.0f, visual_state_.flood_fill_progress); + EXPECT_TRUE(callback_called_); +} + +TEST_F(InstallableInkDropAnimatorTest, + AnimateToPendingThenTriggeredFromHidden) { + InstallableInkDropAnimator animator(gfx::Size(10, 10), &visual_state_, + animation_container_.get(), callback_); + + animator.SetLastEventLocation(gfx::Point(5, 5)); + animator.AnimateToState(InkDropState::ACTION_PENDING); + EXPECT_EQ(InkDropState::ACTION_PENDING, animator.target_state()); + EXPECT_GT(1.0f, visual_state_.flood_fill_progress); + + callback_called_ = false; + animation_tester_.IncrementTime( + InstallableInkDropAnimator::kAnimationDuration); + base::RunLoop().RunUntilIdle(); + EXPECT_EQ(InkDropState::ACTION_PENDING, animator.target_state()); + EXPECT_EQ(1.0f, visual_state_.flood_fill_progress); + EXPECT_TRUE(callback_called_); + + // The animation should be finished now and the visual state should *not* + // change; ACTION_PENDING lasts indefinitely. + animation_tester_.IncrementTime( + InstallableInkDropAnimator::kAnimationDuration); + base::RunLoop().RunUntilIdle(); + EXPECT_EQ(InkDropState::ACTION_PENDING, animator.target_state()); + EXPECT_EQ(1.0f, visual_state_.flood_fill_progress); + + // Animating to ACTION_TRIGGERED from ACTION_PENDING should not repeat the + // flood-fill animation. Instead, it should just fade out. + animator.AnimateToState(InkDropState::ACTION_TRIGGERED); + EXPECT_EQ(InkDropState::ACTION_TRIGGERED, animator.target_state()); + EXPECT_EQ(1.0f, visual_state_.flood_fill_progress); + + animation_tester_.IncrementTime( + InstallableInkDropAnimator::kAnimationDuration); + base::RunLoop().RunUntilIdle(); + EXPECT_EQ(InkDropState::HIDDEN, animator.target_state()); + EXPECT_EQ(0.0f, visual_state_.flood_fill_progress); +} + +TEST_F(InstallableInkDropAnimatorTest, + AnimateToPendingWhileAnimatingToTriggered) { + const base::TimeDelta kHalfAnimationDuration = + InstallableInkDropAnimator::kAnimationDuration / 2; + const base::TimeDelta kRemainingAnimationDuration = + InstallableInkDropAnimator::kAnimationDuration - kHalfAnimationDuration; + + InstallableInkDropAnimator animator(gfx::Size(10, 10), &visual_state_, + animation_container_.get(), callback_); + + animator.SetLastEventLocation(gfx::Point(5, 5)); + animator.AnimateToState(InkDropState::ACTION_PENDING); + EXPECT_EQ(InkDropState::ACTION_PENDING, animator.target_state()); + EXPECT_GT(1.0f, visual_state_.flood_fill_progress); + + callback_called_ = false; + animation_tester_.IncrementTime(kHalfAnimationDuration); + base::RunLoop().RunUntilIdle(); + EXPECT_EQ(InkDropState::ACTION_PENDING, animator.target_state()); + EXPECT_LT(0.0f, visual_state_.flood_fill_progress); + EXPECT_GT(1.0f, visual_state_.flood_fill_progress); + EXPECT_TRUE(callback_called_); + + // Switching to ACTION_TRIGGERED should not restart the animation. Instead, it + // should just add a transition to HIDDEN after the flood-fill is done. + animator.AnimateToState(InkDropState::ACTION_TRIGGERED); + EXPECT_EQ(InkDropState::ACTION_TRIGGERED, animator.target_state()); + EXPECT_LT(0.0f, visual_state_.flood_fill_progress); + EXPECT_GT(1.0f, visual_state_.flood_fill_progress); + + callback_called_ = false; + animation_tester_.IncrementTime(kRemainingAnimationDuration); + base::RunLoop().RunUntilIdle(); + EXPECT_EQ(InkDropState::ACTION_TRIGGERED, animator.target_state()); + EXPECT_EQ(1.0f, visual_state_.flood_fill_progress); + EXPECT_TRUE(callback_called_); +} + +TEST_F(InstallableInkDropAnimatorTest, AnimateToActivatedThenDeactivated) { + InstallableInkDropAnimator animator(gfx::Size(10, 10), &visual_state_, + animation_container_.get(), callback_); + + animator.SetLastEventLocation(gfx::Point(5, 5)); + animator.AnimateToState(InkDropState::ACTIVATED); + EXPECT_EQ(InkDropState::ACTIVATED, animator.target_state()); + EXPECT_GT(1.0f, visual_state_.flood_fill_progress); + + callback_called_ = false; + animation_tester_.IncrementTime( + InstallableInkDropAnimator::kAnimationDuration); + base::RunLoop().RunUntilIdle(); + EXPECT_EQ(InkDropState::ACTIVATED, animator.target_state()); + EXPECT_EQ(1.0f, visual_state_.flood_fill_progress); + EXPECT_TRUE(callback_called_); + + // The state should stay the same indefinitely. + animation_tester_.IncrementTime( + InstallableInkDropAnimator::kAnimationDuration); + base::RunLoop().RunUntilIdle(); + EXPECT_EQ(InkDropState::ACTIVATED, animator.target_state()); + EXPECT_EQ(1.0f, visual_state_.flood_fill_progress); + + // Animating to DEACTIVATED should fade out and transition to HIDDEN. + animator.AnimateToState(InkDropState::DEACTIVATED); + EXPECT_EQ(InkDropState::DEACTIVATED, animator.target_state()); + EXPECT_EQ(1.0f, visual_state_.flood_fill_progress); + + callback_called_ = false; + animation_tester_.IncrementTime( + InstallableInkDropAnimator::kAnimationDuration); + base::RunLoop().RunUntilIdle(); + EXPECT_EQ(InkDropState::HIDDEN, animator.target_state()); + EXPECT_EQ(0.0f, visual_state_.flood_fill_progress); +} + +TEST_F(InstallableInkDropAnimatorTest, + FloodFillAnimationExpandsFromEventLocation) { + constexpr gfx::Point kEventLocation(3, 7); + + // Split |InstallableInkDrop::kAnimationDuration| into three chunks. + const base::TimeDelta kFirstDuration = + InstallableInkDropAnimator::kAnimationDuration / 3; + const base::TimeDelta kSecondDuration = kFirstDuration; + const base::TimeDelta kLastDuration = + InstallableInkDropAnimator::kAnimationDuration - kFirstDuration - + kSecondDuration; + + InstallableInkDropAnimator animator(gfx::Size(10, 10), &visual_state_, + animation_container_.get(), callback_); + + animator.SetLastEventLocation(kEventLocation); + animator.AnimateToState(InkDropState::ACTIVATED); + + callback_called_ = false; + animation_tester_.IncrementTime(kFirstDuration); + base::RunLoop().RunUntilIdle(); + EXPECT_TRUE(callback_called_); + EXPECT_LT(0.0f, visual_state_.flood_fill_progress); + EXPECT_GT(1.0f, visual_state_.flood_fill_progress); + + const float first_progress = visual_state_.flood_fill_progress; + + callback_called_ = false; + animation_tester_.IncrementTime(kSecondDuration); + base::RunLoop().RunUntilIdle(); + EXPECT_TRUE(callback_called_); + EXPECT_LT(first_progress, visual_state_.flood_fill_progress); + EXPECT_GT(1.0f, visual_state_.flood_fill_progress); + + callback_called_ = false; + animation_tester_.IncrementTime(kLastDuration); + base::RunLoop().RunUntilIdle(); + EXPECT_TRUE(callback_called_); + EXPECT_EQ(1.0f, visual_state_.flood_fill_progress); +} + +TEST_F(InstallableInkDropAnimatorTest, HighlightAnimationFadesInAndOut) { + InstallableInkDropAnimator animator(gfx::Size(2, 2), &visual_state_, + animation_container_.get(), callback_); + EXPECT_EQ(0.0f, visual_state_.highlighted_ratio); + EXPECT_FALSE(callback_called_); + + animator.AnimateHighlight(true); + EXPECT_EQ(0.0f, visual_state_.highlighted_ratio); + + callback_called_ = false; + animation_tester_.IncrementTime( + InstallableInkDropAnimator::kAnimationDuration); + base::RunLoop().RunUntilIdle(); + EXPECT_EQ(1.0f, visual_state_.highlighted_ratio); + EXPECT_TRUE(callback_called_); + + animator.AnimateHighlight(false); + EXPECT_EQ(1.0f, visual_state_.highlighted_ratio); + + callback_called_ = false; + animation_tester_.IncrementTime( + InstallableInkDropAnimator::kAnimationDuration); + base::RunLoop().RunUntilIdle(); + EXPECT_EQ(0.0f, visual_state_.highlighted_ratio); + EXPECT_TRUE(callback_called_); +} + +} // namespace views diff --git a/chromium/ui/views/animation/installable_ink_drop_config.h b/chromium/ui/views/animation/installable_ink_drop_config.h new file mode 100644 index 00000000000..8e19ae13ce0 --- /dev/null +++ b/chromium/ui/views/animation/installable_ink_drop_config.h @@ -0,0 +1,23 @@ +// 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. + +#ifndef UI_VIEWS_ANIMATION_INSTALLABLE_INK_DROP_CONFIG_H_ +#define UI_VIEWS_ANIMATION_INSTALLABLE_INK_DROP_CONFIG_H_ + +#include "third_party/skia/include/core/SkColor.h" + +namespace views { + +struct InstallableInkDropConfig { + // The color of ink drop effects, modulated by opacity. + SkColor base_color; + // The opacity to paint |base_color| at for a fully-visible ripple. + float ripple_opacity; + // The opacity to paint |base_color| at for a fully-visible hover highlight. + float highlight_opacity; +}; + +} // namespace views + +#endif // UI_VIEWS_ANIMATION_INSTALLABLE_INK_DROP_CONFIG_H_ diff --git a/chromium/ui/views/animation/installable_ink_drop_painter.cc b/chromium/ui/views/animation/installable_ink_drop_painter.cc new file mode 100644 index 00000000000..8ae4abe39e1 --- /dev/null +++ b/chromium/ui/views/animation/installable_ink_drop_painter.cc @@ -0,0 +1,68 @@ +// 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. + +#include "ui/views/animation/installable_ink_drop_painter.h" + +#include "base/trace_event/trace_event.h" +#include "cc/paint/paint_flags.h" +#include "ui/gfx/animation/tween.h" +#include "ui/gfx/canvas.h" +#include "ui/gfx/geometry/point.h" +#include "ui/gfx/geometry/point_f.h" +#include "ui/gfx/geometry/rect.h" +#include "ui/gfx/geometry/rect_f.h" +#include "ui/gfx/geometry/size.h" +#include "ui/gfx/geometry/vector2d_f.h" +#include "ui/views/animation/installable_ink_drop_config.h" + +namespace views { + +InstallableInkDropPainter::State::State() = default; +InstallableInkDropPainter::State::~State() = default; + +gfx::Size InstallableInkDropPainter::GetMinimumSize() const { + return gfx::Size(); +} + +void InstallableInkDropPainter::Paint(gfx::Canvas* canvas, + const gfx::Size& size) { + TRACE_EVENT0("views", "InstallableInkDropPainter::Paint"); + + DCHECK_GE(state_->flood_fill_progress, 0.0f); + DCHECK_LE(state_->flood_fill_progress, 1.0f); + DCHECK_GE(state_->highlighted_ratio, 0.0f); + DCHECK_LE(state_->highlighted_ratio, 1.0f); + + if (state_->highlighted_ratio > 0.0f) { + canvas->FillRect( + gfx::Rect(size), + SkColorSetA(config_->base_color, config_->highlight_opacity * + state_->highlighted_ratio * + SK_AlphaOPAQUE)); + } + + // If fully filled, we can draw the activated color more efficiently as a + // rectangle. + if (state_->flood_fill_progress == 1.0f) { + canvas->FillRect(gfx::Rect(size), + SkColorSetA(config_->base_color, + config_->ripple_opacity * SK_AlphaOPAQUE)); + } else if (state_->flood_fill_progress > 0.0f) { + // We interpolate between a circle of radius 2 and a circle whose radius is + // the diagonal of |size|. + const float min_radius = 2.0f; + const float max_radius = + gfx::Vector2dF(size.width(), size.height()).Length(); + const float cur_radius = gfx::Tween::FloatValueBetween( + state_->flood_fill_progress, min_radius, max_radius); + + cc::PaintFlags flags; + flags.setStyle(cc::PaintFlags::kFill_Style); + flags.setColor(SkColorSetA(config_->base_color, + config_->ripple_opacity * SK_AlphaOPAQUE)); + canvas->DrawCircle(state_->flood_fill_center, cur_radius, flags); + } +} + +} // namespace views diff --git a/chromium/ui/views/animation/installable_ink_drop_painter.h b/chromium/ui/views/animation/installable_ink_drop_painter.h new file mode 100644 index 00000000000..2ac1dcf0fc0 --- /dev/null +++ b/chromium/ui/views/animation/installable_ink_drop_painter.h @@ -0,0 +1,53 @@ +// 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. + +#ifndef UI_VIEWS_ANIMATION_INSTALLABLE_INK_DROP_PAINTER_H_ +#define UI_VIEWS_ANIMATION_INSTALLABLE_INK_DROP_PAINTER_H_ + +#include "base/optional.h" +#include "ui/gfx/geometry/rect_f.h" +#include "ui/views/painter.h" + +namespace views { + +struct InstallableInkDropConfig; + +// Holds the current visual state of the installable ink drop and handles +// painting it. The |Painter::Paint()| implementation draws a rectangular ink +// drop of the given size; the user should set a clip path via +// |gfx::Canvas::ClipPath()| to control the shape. +class VIEWS_EXPORT InstallableInkDropPainter : public Painter { + public: + struct VIEWS_EXPORT State { + State(); + ~State(); + + gfx::PointF flood_fill_center; + float flood_fill_progress = 0.0f; + float highlighted_ratio = 0.0f; + }; + + // Pointer arguments must outlive |this|. + InstallableInkDropPainter(const InstallableInkDropConfig* config, + const State* state) + : config_(config), state_(state) {} + ~InstallableInkDropPainter() override = default; + + // Painter: + gfx::Size GetMinimumSize() const override; + void Paint(gfx::Canvas* canvas, const gfx::Size& size) override; + + private: + // Contains the colors and opacities we use to paint, given the current state. + // This isn't modified inside this class, but it can be modified by our user. + const InstallableInkDropConfig* const config_; + + // The current visual state. This isn't modified inside this class, but it can + // be modified by our user. + const State* const state_; +}; + +} // namespace views + +#endif // UI_VIEWS_ANIMATION_INSTALLABLE_INK_DROP_PAINTER_H_ diff --git a/chromium/ui/views/animation/installable_ink_drop_unittest.cc b/chromium/ui/views/animation/installable_ink_drop_unittest.cc index d75acaee15f..b6d27e2e9a4 100644 --- a/chromium/ui/views/animation/installable_ink_drop_unittest.cc +++ b/chromium/ui/views/animation/installable_ink_drop_unittest.cc @@ -4,20 +4,70 @@ #include "ui/views/animation/installable_ink_drop.h" +#include <memory> + +#include "base/test/scoped_task_environment.h" #include "testing/gtest/include/gtest/gtest.h" +#include "third_party/skia/include/core/SkPath.h" +#include "ui/gfx/geometry/rect.h" #include "ui/views/animation/ink_drop_state.h" +#include "ui/views/view.h" +#include "ui/views/view_class_properties.h" namespace views { -TEST(InstallableInkDropTest, UpdatesState) { - InstallableInkDrop installable_ink_drop; +class InstallableInkDropTest : public ::testing::Test { + protected: + void SetUp() override { + // Ink drop layers get installed as siblings to their host view's + // layer. Hence, there needs to be a root view with a layer above them. + root_view_.SetPaintToLayer(); + } + + View* root_view() { return &root_view_; } + + private: + base::test::ScopedTaskEnvironment scoped_task_environment_; + + View root_view_; +}; + +TEST_F(InstallableInkDropTest, LayerIsAddedAndRemoved) { + View* view = root_view()->AddChildView(std::make_unique<View>()); + view->SetPaintToLayer(); + EXPECT_EQ(1, static_cast<int>(root_view()->layer()->children().size())); + + { + InstallableInkDrop ink_drop(view); + EXPECT_EQ(2, static_cast<int>(root_view()->layer()->children().size())); + } + + EXPECT_EQ(1, static_cast<int>(root_view()->layer()->children().size())); +} + +TEST_F(InstallableInkDropTest, LayerSizeTracksViewSize) { + View* view = root_view()->AddChildView(std::make_unique<View>()); + view->SetBoundsRect(gfx::Rect(0, 0, 10, 10)); + + InstallableInkDrop ink_drop(view); + EXPECT_EQ(view->size(), ink_drop.layer_for_testing()->size()); + + view->SetBoundsRect(gfx::Rect(0, 0, 20, 15)); + EXPECT_EQ(view->size(), ink_drop.layer_for_testing()->size()); + + view->SetBoundsRect(gfx::Rect(10, 10, 30, 30)); + EXPECT_EQ(view->size(), ink_drop.layer_for_testing()->size()); +} + +TEST_F(InstallableInkDropTest, UpdatesState) { + View* view = root_view()->AddChildView(std::make_unique<View>()); + InstallableInkDrop ink_drop(view); // Initial state should be HIDDEN. - EXPECT_EQ(installable_ink_drop.GetTargetInkDropState(), InkDropState::HIDDEN); + EXPECT_EQ(ink_drop.GetTargetInkDropState(), InkDropState::HIDDEN); - installable_ink_drop.AnimateToState(InkDropState::ACTIVATED); - EXPECT_EQ(installable_ink_drop.GetTargetInkDropState(), - InkDropState::ACTIVATED); + ink_drop.AnimateToState(InkDropState::ACTIVATED); + EXPECT_EQ(ink_drop.GetTargetInkDropState(), InkDropState::ACTIVATED); } } // namespace views diff --git a/chromium/ui/views/bubble/bubble_dialog_delegate_view.cc b/chromium/ui/views/bubble/bubble_dialog_delegate_view.cc index ee6f0497efb..161f0b5fd49 100644 --- a/chromium/ui/views/bubble/bubble_dialog_delegate_view.cc +++ b/chromium/ui/views/bubble/bubble_dialog_delegate_view.cc @@ -33,6 +33,9 @@ namespace views { +// static +bool BubbleDialogDelegateView::devtools_dismiss_override_ = false; + namespace { // Override base functionality of Widget to give bubble dialogs access to the @@ -116,10 +119,6 @@ Widget* CreateBubbleWidget(BubbleDialogDelegateView* bubble) { } // namespace -// static -const char BubbleDialogDelegateView::kViewClassName[] = - "BubbleDialogDelegateView"; - BubbleDialogDelegateView::~BubbleDialogDelegateView() { if (GetWidget()) GetWidget()->RemoveObserver(this); @@ -181,10 +180,6 @@ NonClientFrameView* BubbleDialogDelegateView::CreateNonClientFrameView( return frame; } -const char* BubbleDialogDelegateView::GetClassName() const { - return kViewClassName; -} - bool BubbleDialogDelegateView::AcceleratorPressed( const ui::Accelerator& accelerator) { if (accelerator.key_code() == ui::VKEY_DOWN || @@ -230,6 +225,9 @@ void BubbleDialogDelegateView::OnWidgetVisibilityChanged(Widget* widget, void BubbleDialogDelegateView::OnWidgetActivationChanged(Widget* widget, bool active) { + if (devtools_dismiss_override_) + return; + #if defined(OS_MACOSX) // Install |mac_bubble_closer_| the first time the widget becomes active. if (widget == GetWidget() && active && !mac_bubble_closer_) { @@ -273,6 +271,15 @@ void BubbleDialogDelegateView::SetHighlightedButton( } void BubbleDialogDelegateView::SetArrow(BubbleBorder::Arrow arrow) { + SetArrowWithoutResizing(arrow); + // If SetArrow() is called before CreateWidget(), there's no need to update + // the BubbleFrameView. + if (GetBubbleFrameView()) + SizeToContents(); +} + +void BubbleDialogDelegateView::SetArrowWithoutResizing( + BubbleBorder::Arrow arrow) { if (base::i18n::IsRTL()) arrow = BubbleBorder::horizontal_mirror(arrow); if (arrow_ == arrow) @@ -281,10 +288,8 @@ void BubbleDialogDelegateView::SetArrow(BubbleBorder::Arrow arrow) { // If SetArrow() is called before CreateWidget(), there's no need to update // the BubbleFrameView. - if (GetBubbleFrameView()) { + if (GetBubbleFrameView()) GetBubbleFrameView()->SetArrow(arrow); - SizeToContents(); - } } gfx::Rect BubbleDialogDelegateView::GetAnchorRect() const { @@ -517,4 +522,8 @@ void BubbleDialogDelegateView::UpdateHighlightedButton(bool highlighted) { button->SetHighlighted(highlighted); } +BEGIN_METADATA(BubbleDialogDelegateView) +METADATA_PARENT_CLASS(DialogDelegateView) +END_METADATA() + } // namespace views diff --git a/chromium/ui/views/bubble/bubble_dialog_delegate_view.h b/chromium/ui/views/bubble/bubble_dialog_delegate_view.h index bb495e34edb..77288a2077c 100644 --- a/chromium/ui/views/bubble/bubble_dialog_delegate_view.h +++ b/chromium/ui/views/bubble/bubble_dialog_delegate_view.h @@ -30,6 +30,10 @@ namespace ui { class Accelerator; } // namespace ui +namespace ui_devtools { +class PageAgentViews; +} + namespace views { class BubbleFrameView; @@ -39,8 +43,7 @@ class Button; class VIEWS_EXPORT BubbleDialogDelegateView : public DialogDelegateView, public WidgetObserver { public: - // Internal class name. - static const char kViewClassName[]; + METADATA_HEADER(BubbleDialogDelegateView); enum class CloseReason { DEACTIVATION, @@ -58,7 +61,6 @@ class VIEWS_EXPORT BubbleDialogDelegateView : public DialogDelegateView, bool ShouldShowCloseButton() const override; ClientView* CreateClientView(Widget* widget) override; NonClientFrameView* CreateNonClientFrameView(Widget* widget) override; - const char* GetClassName() const override; bool AcceleratorPressed(const ui::Accelerator& accelerator) override; // WidgetObserver: @@ -81,9 +83,19 @@ class VIEWS_EXPORT BubbleDialogDelegateView : public DialogDelegateView, // The anchor rect is used in the absence of an assigned anchor view. const gfx::Rect& anchor_rect() const { return anchor_rect_; } - // Set the desired arrow for the bubble. The arrow will be mirrored for RTL. + // Set the desired arrow for the bubble and updates the bubble's bounds + // accordingly. The arrow will be mirrored for RTL. void SetArrow(BubbleBorder::Arrow arrow); + // Sets the arrow without recaluclating or updating bounds. This could be used + // proceeding another function call which also sets bounds, so that bounds are + // not set multiple times in a row. When animating bounds changes, setting + // bounds twice in a row can make the widget position jump. + // TODO(crbug.com/982880) It would be good to be able to re-target the + // animation rather than expet callers to use SetArrowWithoutResizing if they + // are also changing the anchor rect, or similar. + void SetArrowWithoutResizing(BubbleBorder::Arrow arrow); + BubbleBorder::Shadow GetShadow() const; void set_shadow(BubbleBorder::Shadow shadow) { shadow_ = shadow; } @@ -184,6 +196,7 @@ class VIEWS_EXPORT BubbleDialogDelegateView : public DialogDelegateView, private: friend class BubbleBorderDelegate; friend class BubbleWindowTargeter; + friend class ui_devtools::PageAgentViews; FRIEND_TEST_ALL_PREFIXES(BubbleDelegateTest, CreateDelegate); FRIEND_TEST_ALL_PREFIXES(BubbleDelegateTest, NonClientHitTest); @@ -202,6 +215,10 @@ class VIEWS_EXPORT BubbleDialogDelegateView : public DialogDelegateView, // provide different highlight effects. virtual void UpdateHighlightedButton(bool highlighted); + // Set from UI DevTools to prevent bubbles from closing in + // OnWidgetActivationChanged(). + static bool devtools_dismiss_override_; + // A flag controlling bubble closure on deactivation. bool close_on_deactivate_; diff --git a/chromium/ui/views/bubble/bubble_frame_view.cc b/chromium/ui/views/bubble/bubble_frame_view.cc index 419abab9192..1508e41f366 100644 --- a/chromium/ui/views/bubble/bubble_frame_view.cc +++ b/chromium/ui/views/bubble/bubble_frame_view.cc @@ -28,7 +28,6 @@ #include "ui/views/controls/button/image_button.h" #include "ui/views/controls/button/image_button_factory.h" #include "ui/views/controls/image_view.h" -#include "ui/views/event_utils.h" #include "ui/views/layout/box_layout.h" #include "ui/views/layout/layout_provider.h" #include "ui/views/paint_info.h" @@ -104,7 +103,7 @@ std::unique_ptr<Label> BubbleFrameView::CreateDefaultTitleLabel( const base::string16& title_text) { auto title = std::make_unique<Label>(title_text, style::CONTEXT_DIALOG_TITLE); title->SetHorizontalAlignment(gfx::ALIGN_LEFT); - title->set_collapse_when_hidden(true); + title->SetCollapseWhenHidden(true); title->SetMultiLine(true); return title; } @@ -404,9 +403,7 @@ void BubbleFrameView::ViewHierarchyChanged( void BubbleFrameView::VisibilityChanged(View* starting_from, bool is_visible) { NonClientFrameView::VisibilityChanged(starting_from, is_visible); - - if (is_visible) - view_shown_time_stamp_ = base::TimeTicks::Now(); + input_protector_.VisibilityChanged(is_visible); } void BubbleFrameView::OnPaint(gfx::Canvas* canvas) { @@ -426,7 +423,7 @@ void BubbleFrameView::PaintChildren(const PaintInfo& paint_info) { } void BubbleFrameView::ButtonPressed(Button* sender, const ui::Event& event) { - if (IsPossiblyUnintendedInteraction(view_shown_time_stamp_, event)) + if (input_protector_.IsPossiblyUnintendedInteraction(event)) return; if (sender == close_) { @@ -446,15 +443,17 @@ void BubbleFrameView::SetBubbleBorder(std::unique_ptr<BubbleBorder> border) { SetBackground(std::make_unique<views::BubbleBackground>(bubble_border_)); } -void BubbleFrameView::SetFootnoteView(View* view) { - if (!view) +void BubbleFrameView::SetFootnoteView(std::unique_ptr<View> view) { + if (!view) { + delete footnote_container_; + footnote_container_ = nullptr; return; + } DCHECK(!footnote_container_); int radius = bubble_border_ ? bubble_border_->corner_radius() : 0; - footnote_container_ = - new FootnoteContainerView(footnote_margins_, view, radius); - AddChildView(footnote_container_); + footnote_container_ = AddChildView(std::make_unique<FootnoteContainerView>( + footnote_margins_, std::move(view), radius)); } void BubbleFrameView::SetCornerRadius(int radius) { @@ -512,7 +511,7 @@ gfx::Rect BubbleFrameView::GetUpdatedWindowBounds( } void BubbleFrameView::ResetViewShownTimeStampForTesting() { - view_shown_time_stamp_ = base::TimeTicks(); + input_protector_.ResetForTesting(); } gfx::Rect BubbleFrameView::GetAvailableScreenBounds( diff --git a/chromium/ui/views/bubble/bubble_frame_view.h b/chromium/ui/views/bubble/bubble_frame_view.h index a3b91eb0840..45eb1ef8413 100644 --- a/chromium/ui/views/bubble/bubble_frame_view.h +++ b/chromium/ui/views/bubble/bubble_frame_view.h @@ -16,6 +16,7 @@ #include "ui/views/bubble/bubble_border.h" #include "ui/views/controls/button/button.h" #include "ui/views/controls/label.h" +#include "ui/views/input_event_activation_protector.h" #include "ui/views/window/non_client_view.h" namespace views { @@ -88,7 +89,7 @@ class VIEWS_EXPORT BubbleFrameView : public NonClientFrameView, gfx::Insets content_margins() const { return content_margins_; } - void SetFootnoteView(View* view); + void SetFootnoteView(std::unique_ptr<View> view); void set_footnote_margins(const gfx::Insets& footnote_margins) { footnote_margins_ = footnote_margins; } @@ -216,9 +217,6 @@ class VIEWS_EXPORT BubbleFrameView : public NonClientFrameView, // A view to contain the footnote view, if it exists. FootnoteContainerView* footnote_container_; - // Time when view has been shown. - base::TimeTicks view_shown_time_stamp_; - // Set preference for how the arrow will be adjusted if the window is outside // the available bounds. PreferredArrowAdjustment preferred_arrow_adjustment_ = @@ -228,6 +226,8 @@ class VIEWS_EXPORT BubbleFrameView : public NonClientFrameView, // hover). bool hit_test_transparent_ = false; + InputEventActivationProtector input_protector_; + DISALLOW_COPY_AND_ASSIGN(BubbleFrameView); }; diff --git a/chromium/ui/views/bubble/bubble_frame_view_unittest.cc b/chromium/ui/views/bubble/bubble_frame_view_unittest.cc index 82b0779727c..d4b6ed70794 100644 --- a/chromium/ui/views/bubble/bubble_frame_view_unittest.cc +++ b/chromium/ui/views/bubble/bubble_frame_view_unittest.cc @@ -180,22 +180,22 @@ TEST_F(BubbleFrameViewTest, GetBoundsForClientViewWithClose) { TEST_F(BubbleFrameViewTest, RemoveFootnoteView) { TestBubbleFrameView frame(this); EXPECT_EQ(nullptr, frame.footnote_container_); - View* footnote_dummy_view = new StaticSizedView(gfx::Size(200, 200)); - frame.SetFootnoteView(footnote_dummy_view); + auto footnote = std::make_unique<StaticSizedView>(gfx::Size(200, 200)); + View* footnote_dummy_view = footnote.get(); + frame.SetFootnoteView(std::move(footnote)); EXPECT_EQ(footnote_dummy_view->parent(), frame.footnote_container_); - View* container_view = footnote_dummy_view->parent(); - delete footnote_dummy_view; - footnote_dummy_view = nullptr; - EXPECT_FALSE(container_view->GetVisible()); + frame.SetFootnoteView(nullptr); EXPECT_EQ(nullptr, frame.footnote_container_); } TEST_F(BubbleFrameViewTest, FootnoteContainerViewShouldMatchVisibilityOfFirstChild) { TestBubbleFrameView frame(this); - View* footnote_dummy_view = new StaticSizedView(gfx::Size(200, 200)); - footnote_dummy_view->SetVisible(false); - frame.SetFootnoteView(footnote_dummy_view); + std::unique_ptr<View> footnote = + std::make_unique<StaticSizedView>(gfx::Size(200, 200)); + footnote->SetVisible(false); + View* footnote_dummy_view = footnote.get(); + frame.SetFootnoteView(std::move(footnote)); View* footnote_container_view = footnote_dummy_view->parent(); EXPECT_FALSE(footnote_container_view->GetVisible()); footnote_dummy_view->SetVisible(true); @@ -807,19 +807,21 @@ TEST_F(BubbleFrameViewTest, GetPreferredSizeWithFootnote) { constexpr int kFootnoteHeight = 20; const gfx::Size no_footnote_size = frame.GetPreferredSize(); - View* footnote = new StaticSizedView(gfx::Size(10, kFootnoteHeight)); + std::unique_ptr<View> footnote = + std::make_unique<StaticSizedView>(gfx::Size(10, kFootnoteHeight)); footnote->SetVisible(false); - frame.SetFootnoteView(footnote); + View* footnote_dummy_view = footnote.get(); + frame.SetFootnoteView(std::move(footnote)); EXPECT_EQ(no_footnote_size, frame.GetPreferredSize()); // No change. - footnote->SetVisible(true); + footnote_dummy_view->SetVisible(true); gfx::Size with_footnote_size = no_footnote_size; constexpr int kFootnoteTopBorderThickness = 1; with_footnote_size.Enlarge(0, kFootnoteHeight + kFootnoteTopBorderThickness + frame.content_margins().height()); EXPECT_EQ(with_footnote_size, frame.GetPreferredSize()); - footnote->SetVisible(false); + footnote_dummy_view->SetVisible(false); EXPECT_EQ(no_footnote_size, frame.GetPreferredSize()); } @@ -1106,8 +1108,8 @@ TEST_F(BubbleFrameViewTest, NoElideTitle) { // Sanity check: Title labels default to multiline and elide tail. Either of // which result in the Layout system making the title and resulting dialog // very narrow. - EXPECT_EQ(gfx::ELIDE_TAIL, title_label->elide_behavior()); - EXPECT_TRUE(title_label->multi_line()); + EXPECT_EQ(gfx::ELIDE_TAIL, title_label->GetElideBehavior()); + EXPECT_TRUE(title_label->GetMultiLine()); EXPECT_GT(empty_bubble_width, title_label->size().width()); EXPECT_EQ(empty_bubble_width, bubble->GetClientAreaBoundsInScreen().width()); diff --git a/chromium/ui/views/bubble/footnote_container_view.cc b/chromium/ui/views/bubble/footnote_container_view.cc index 4277f0329c0..33a01690829 100644 --- a/chromium/ui/views/bubble/footnote_container_view.cc +++ b/chromium/ui/views/bubble/footnote_container_view.cc @@ -49,26 +49,27 @@ class HalfRoundedRectBackground : public Background { } // namespace FootnoteContainerView::FootnoteContainerView(const gfx::Insets& margins, - View* child_view, + std::unique_ptr<View> child_view, float corner_radius) { - SetLayoutManager( - std::make_unique<BoxLayout>(BoxLayout::kVertical, margins, 0)); + SetLayoutManager(std::make_unique<BoxLayout>( + BoxLayout::Orientation::kVertical, margins, 0)); SetCornerRadius(corner_radius); - SetBorder(CreateSolidSidedBorder(1, 0, 0, 0, - GetNativeTheme()->SystemDarkModeEnabled() - ? gfx::kGoogleGrey900 - : gfx::kGoogleGrey200)); - AddChildView(child_view); - SetVisible(child_view->GetVisible()); + ResetBorder(); + auto* child_view_ptr = AddChildView(std::move(child_view)); + SetVisible(child_view_ptr->GetVisible()); } FootnoteContainerView::~FootnoteContainerView() = default; void FootnoteContainerView::SetCornerRadius(float corner_radius) { - SkColor background_color = GetNativeTheme()->GetSystemColor( - ui::NativeTheme::kColorId_BubbleFooterBackground); - SetBackground(std::make_unique<HalfRoundedRectBackground>(background_color, - corner_radius)); + corner_radius_ = corner_radius; + ResetBackground(); +} + +void FootnoteContainerView::OnThemeChanged() { + View::OnThemeChanged(); + ResetBorder(); + ResetBackground(); } void FootnoteContainerView::ChildVisibilityChanged(View* child) { @@ -76,4 +77,22 @@ void FootnoteContainerView::ChildVisibilityChanged(View* child) { SetVisible(child->GetVisible()); } +void FootnoteContainerView::ResetBackground() { + SkColor background_color = GetNativeTheme()->GetSystemColor( + ui::NativeTheme::kColorId_BubbleFooterBackground); + SetBackground(std::make_unique<HalfRoundedRectBackground>(background_color, + corner_radius_)); +} + +void FootnoteContainerView::ResetBorder() { + SetBorder(CreateSolidSidedBorder(1, 0, 0, 0, + GetNativeTheme()->SystemDarkModeEnabled() + ? gfx::kGoogleGrey900 + : gfx::kGoogleGrey200)); +} + +BEGIN_METADATA(FootnoteContainerView) +METADATA_PARENT_CLASS(View) +END_METADATA() + } // namespace views diff --git a/chromium/ui/views/bubble/footnote_container_view.h b/chromium/ui/views/bubble/footnote_container_view.h index 2b4bea9235f..d49778e064c 100644 --- a/chromium/ui/views/bubble/footnote_container_view.h +++ b/chromium/ui/views/bubble/footnote_container_view.h @@ -13,17 +13,25 @@ namespace views { // background with rounded corners at the bottom. class FootnoteContainerView : public View { public: + METADATA_HEADER(FootnoteContainerView); + FootnoteContainerView(const gfx::Insets& margins, - View* child_view, + std::unique_ptr<View> child_view, float corner_radius); ~FootnoteContainerView() override; void SetCornerRadius(float corner_radius); // View: + void OnThemeChanged() override; void ChildVisibilityChanged(View* child) override; private: + void ResetBackground(); + void ResetBorder(); + + float corner_radius_; + DISALLOW_IMPLICIT_CONSTRUCTORS(FootnoteContainerView); }; diff --git a/chromium/ui/views/bubble/info_bubble.cc b/chromium/ui/views/bubble/info_bubble.cc index bce25293961..aeaa6c71352 100644 --- a/chromium/ui/views/bubble/info_bubble.cc +++ b/chromium/ui/views/bubble/info_bubble.cc @@ -125,4 +125,8 @@ void InfoBubble::UpdatePosition() { } } +BEGIN_METADATA(InfoBubble) +METADATA_PARENT_CLASS(BubbleDialogDelegateView) +END_METADATA() + } // namespace views diff --git a/chromium/ui/views/bubble/info_bubble.h b/chromium/ui/views/bubble/info_bubble.h index c1f092d10d1..7d607e76fdf 100644 --- a/chromium/ui/views/bubble/info_bubble.h +++ b/chromium/ui/views/bubble/info_bubble.h @@ -17,6 +17,8 @@ class InfoBubbleFrame; // Class to create and manage an information bubble for errors or tooltips. class InfoBubble : public BubbleDialogDelegateView { public: + METADATA_HEADER(InfoBubble); + InfoBubble(View* anchor, const base::string16& message); ~InfoBubble() override; diff --git a/chromium/ui/views/bubble/tooltip_icon.cc b/chromium/ui/views/bubble/tooltip_icon.cc index 0ff1e6bd3b3..289e92115b8 100644 --- a/chromium/ui/views/bubble/tooltip_icon.cc +++ b/chromium/ui/views/bubble/tooltip_icon.cc @@ -28,10 +28,6 @@ TooltipIcon::~TooltipIcon() { HideBubble(); } -const char* TooltipIcon::GetClassName() const { - return "TooltipIcon"; -} - void TooltipIcon::OnMouseEntered(const ui::MouseEvent& event) { mouse_inside_ = true; show_timer_.Start(FROM_HERE, base::TimeDelta::FromMilliseconds(150), this, @@ -113,4 +109,8 @@ void TooltipIcon::OnWidgetDestroyed(Widget* widget) { bubble_ = nullptr; } +BEGIN_METADATA(TooltipIcon) +METADATA_PARENT_CLASS(ImageView) +END_METADATA() + } // namespace views diff --git a/chromium/ui/views/bubble/tooltip_icon.h b/chromium/ui/views/bubble/tooltip_icon.h index 97dbe043b45..cb10e596539 100644 --- a/chromium/ui/views/bubble/tooltip_icon.h +++ b/chromium/ui/views/bubble/tooltip_icon.h @@ -25,12 +25,13 @@ class VIEWS_EXPORT TooltipIcon : public ImageView, public MouseWatcherListener, public WidgetObserver { public: + METADATA_HEADER(TooltipIcon); + explicit TooltipIcon(const base::string16& tooltip, int tooltip_icon_size = 16); ~TooltipIcon() override; // ImageView: - const char* GetClassName() const override; void OnMouseEntered(const ui::MouseEvent& event) override; void OnMouseExited(const ui::MouseEvent& event) override; bool OnMousePressed(const ui::MouseEvent& event) override; diff --git a/chromium/ui/views/cocoa/bridge_factory_host.cc b/chromium/ui/views/cocoa/bridge_factory_host.cc deleted file mode 100644 index 20c2e163c85..00000000000 --- a/chromium/ui/views/cocoa/bridge_factory_host.cc +++ /dev/null @@ -1,33 +0,0 @@ -// Copyright 2018 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "ui/views/cocoa/bridge_factory_host.h" - -#include "mojo/public/cpp/bindings/interface_request.h" - -namespace views { - -BridgeFactoryHost::BridgeFactoryHost( - remote_cocoa::mojom::BridgeFactoryAssociatedRequest* request) { - *request = mojo::MakeRequest(&bridge_factory_ptr_); -} - -BridgeFactoryHost::~BridgeFactoryHost() { - for (Observer& obs : observers_) - obs.OnBridgeFactoryHostDestroying(this); -} - -remote_cocoa::mojom::BridgeFactory* BridgeFactoryHost::GetFactory() { - return bridge_factory_ptr_.get(); -} - -void BridgeFactoryHost::AddObserver(Observer* observer) { - observers_.AddObserver(observer); -} - -void BridgeFactoryHost::RemoveObserver(const Observer* observer) { - observers_.RemoveObserver(observer); -} - -} // namespace views diff --git a/chromium/ui/views/cocoa/bridge_factory_host.h b/chromium/ui/views/cocoa/bridge_factory_host.h deleted file mode 100644 index 82a77a8cdd5..00000000000 --- a/chromium/ui/views/cocoa/bridge_factory_host.h +++ /dev/null @@ -1,41 +0,0 @@ -// Copyright 2018 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef UI_VIEWS_COCOA_BRIDGE_FACTORY_HOST_H_ -#define UI_VIEWS_COCOA_BRIDGE_FACTORY_HOST_H_ - -#include "base/observer_list.h" -#include "base/observer_list_types.h" -#include "components/remote_cocoa/common/bridge_factory.mojom.h" -#include "ui/views/views_export.h" - -namespace views { - -class VIEWS_EXPORT BridgeFactoryHost { - public: - class Observer : public base::CheckedObserver { - public: - virtual void OnBridgeFactoryHostDestroying(BridgeFactoryHost* host) = 0; - - protected: - ~Observer() override {} - }; - - BridgeFactoryHost( - remote_cocoa::mojom::BridgeFactoryAssociatedRequest* request); - ~BridgeFactoryHost(); - - remote_cocoa::mojom::BridgeFactory* GetFactory(); - - void AddObserver(Observer* observer); - void RemoveObserver(const Observer* observer); - - private: - remote_cocoa::mojom::BridgeFactoryAssociatedPtr bridge_factory_ptr_; - base::ObserverList<Observer> observers_; -}; - -} // namespace views - -#endif // UI_VIEWS_COCOA_BRIDGE_FACTORY_HOST_H_ diff --git a/chromium/ui/views/cocoa/bridged_native_widget_interactive_uitest.mm b/chromium/ui/views/cocoa/bridged_native_widget_interactive_uitest.mm index 20ee22947de..736f61161be 100644 --- a/chromium/ui/views/cocoa/bridged_native_widget_interactive_uitest.mm +++ b/chromium/ui/views/cocoa/bridged_native_widget_interactive_uitest.mm @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#import "components/remote_cocoa/app_shim/bridged_native_widget_impl.h" +#import "components/remote_cocoa/app_shim/native_widget_ns_window_bridge.h" #import <Cocoa/Cocoa.h> @@ -15,7 +15,7 @@ #include "ui/base/test/ui_controls.h" #import "ui/base/test/windowed_nsnotification_observer.h" #import "ui/events/test/cocoa_test_event_utils.h" -#include "ui/views/cocoa/bridged_native_widget_host_impl.h" +#include "ui/views/cocoa/native_widget_mac_ns_window_host.h" #include "ui/views/test/widget_test.h" #include "ui/views/widget/native_widget_mac.h" #include "ui/views/window/native_frame_view.h" @@ -251,13 +251,13 @@ void WaitForEvent(NSUInteger mask) { } // namespace // This is used to inject test versions of NativeFrameView and -// BridgedNativeWidgetImpl. +// NativeWidgetNSWindowBridge. class HitTestNativeWidgetMac : public NativeWidgetMac { public: HitTestNativeWidgetMac(internal::NativeWidgetDelegate* delegate, NativeFrameView* native_frame_view) : NativeWidgetMac(delegate), native_frame_view_(native_frame_view) { - bridge_host_ = std::make_unique<BridgedNativeWidgetHostImpl>(this); + ns_window_host_ = std::make_unique<NativeWidgetMacNSWindowHost>(this); } // internal::NativeWidgetPrivate: diff --git a/chromium/ui/views/cocoa/bridged_native_widget_unittest.mm b/chromium/ui/views/cocoa/bridged_native_widget_unittest.mm index 8a871ad6526..d925401a44b 100644 --- a/chromium/ui/views/cocoa/bridged_native_widget_unittest.mm +++ b/chromium/ui/views/cocoa/bridged_native_widget_unittest.mm @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#import "components/remote_cocoa/app_shim/bridged_native_widget_impl.h" +#import "components/remote_cocoa/app_shim/native_widget_ns_window_bridge.h" #import <Cocoa/Cocoa.h> #include <objc/runtime.h> @@ -29,7 +29,7 @@ #import "ui/base/test/cocoa_helper.h" #include "ui/events/test/cocoa_test_event_utils.h" #import "ui/gfx/mac/coordinate_conversion.h" -#import "ui/views/cocoa/bridged_native_widget_host_impl.h" +#import "ui/views/cocoa/native_widget_mac_ns_window_host.h" #import "ui/views/cocoa/text_input_host.h" #include "ui/views/controls/textfield/textfield.h" #include "ui/views/controls/textfield/textfield_controller.h" @@ -295,13 +295,13 @@ NSTextInputContext* g_fake_current_input_context = nullptr; namespace views { namespace test { -// Provides the |parent| argument to construct a BridgedNativeWidgetImpl. +// Provides the |parent| argument to construct a NativeWidgetNSWindowBridge. class MockNativeWidgetMac : public NativeWidgetMac { public: explicit MockNativeWidgetMac(internal::NativeWidgetDelegate* delegate) : NativeWidgetMac(delegate) {} - using NativeWidgetMac::bridge_impl; - using NativeWidgetMac::bridge_host; + using NativeWidgetMac::GetInProcessNSWindowBridge; + using NativeWidgetMac::GetNSWindowHost; // internal::NativeWidgetPrivate: void InitNativeWidget(const Widget::InitParams& params) override { @@ -313,19 +313,19 @@ class MockNativeWidgetMac : public NativeWidgetMac { styleMask:NSBorderlessWindowMask backing:NSBackingStoreBuffered defer:NO]); - bridge_host()->CreateLocalBridge(window); + GetNSWindowHost()->CreateInProcessNSWindowBridge(window); if (auto* parent = - BridgedNativeWidgetHostImpl::GetFromNativeView(params.parent)) { - bridge_host()->SetParent(parent); + NativeWidgetMacNSWindowHost::GetFromNativeView(params.parent)) { + GetNSWindowHost()->SetParent(parent); } - bridge_host()->InitWindow(params); + GetNSWindowHost()->InitWindow(params); // Usually the bridge gets initialized here. It is skipped to run extra // checks in tests, and so that a second window isn't created. delegate()->OnNativeWidgetCreated(); // To allow events to dispatch to a view, it needs a way to get focus. - bridge_host()->SetFocusManager(GetWidget()->GetFocusManager()); + GetNSWindowHost()->SetFocusManager(GetWidget()->GetFocusManager()); } void ReorderNativeViews() override { @@ -336,7 +336,8 @@ class MockNativeWidgetMac : public NativeWidgetMac { DISALLOW_COPY_AND_ASSIGN(MockNativeWidgetMac); }; -// Helper test base to construct a BridgedNativeWidgetImpl with a valid parent. +// Helper test base to construct a NativeWidgetNSWindowBridge with a valid +// parent. class BridgedNativeWidgetTestBase : public ui::CocoaTest { public: struct SkipInitialization {}; @@ -348,11 +349,11 @@ class BridgedNativeWidgetTestBase : public ui::CocoaTest { explicit BridgedNativeWidgetTestBase(SkipInitialization tag) : native_widget_mac_(nullptr) {} - BridgedNativeWidgetImpl* bridge() { - return native_widget_mac_->bridge_impl(); + remote_cocoa::NativeWidgetNSWindowBridge* bridge() { + return native_widget_mac_->GetInProcessNSWindowBridge(); } - BridgedNativeWidgetHostImpl* bridge_host() { - return native_widget_mac_->bridge_host(); + NativeWidgetMacNSWindowHost* GetNSWindowHost() { + return native_widget_mac_->GetNSWindowHost(); } // Generate an autoreleased KeyDown NSEvent* in |widget_| for pressing the @@ -404,8 +405,8 @@ class BridgedNativeWidgetTestBase : public ui::CocoaTest { } NSWindow* bridge_window() const { - if (native_widget_mac_->bridge_impl()) - return native_widget_mac_->bridge_impl()->ns_window(); + if (auto* bridge = native_widget_mac_->GetInProcessNSWindowBridge()) + return bridge->ns_window(); return nil; } @@ -550,7 +551,7 @@ Textfield* BridgedNativeWidgetTest::InstallTextField( // schedules a task to flash the cursor, so this requires |message_loop_|. textfield->RequestFocus(); - bridge_host()->text_input_host()->SetTextInputClient(textfield); + GetNSWindowHost()->text_input_host()->SetTextInputClient(textfield); // Initialize the dummy text view. Initializing this with NSZeroRect causes // weird NSTextView behavior on OSX 10.9. @@ -632,8 +633,8 @@ void BridgedNativeWidgetTest::SetUp() { // The delegate should exist before setting the root view. EXPECT_TRUE([window delegate]); - bridge_host()->SetRootView(view_.get()); - bridge()->CreateContentView(bridge_host()->GetRootViewNSViewId(), + GetNSWindowHost()->SetRootView(view_.get()); + bridge()->CreateContentView(GetNSWindowHost()->GetRootViewNSViewId(), view_->bounds()); ns_view_ = bridge()->ns_view(); @@ -646,8 +647,8 @@ void BridgedNativeWidgetTest::TearDown() { // Clear kill buffer so that no state persists between tests. TextfieldModel::ClearKillBuffer(); - if (bridge_host()) { - bridge_host()->SetRootView(nullptr); + if (GetNSWindowHost()) { + GetNSWindowHost()->SetRootView(nullptr); bridge()->DestroyContentView(); } view_.reset(); @@ -849,7 +850,7 @@ TEST_F(BridgedNativeWidgetTest, ViewSizeTracksWindow) { } TEST_F(BridgedNativeWidgetTest, GetInputMethodShouldNotReturnNull) { - EXPECT_TRUE(bridge_host()->GetInputMethod()); + EXPECT_TRUE(GetNSWindowHost()->GetInputMethod()); } // A simpler test harness for testing initialization flows. @@ -873,7 +874,7 @@ class BridgedNativeWidgetInitTest : public BridgedNativeWidgetTestBase { DISALLOW_COPY_AND_ASSIGN(BridgedNativeWidgetInitTest); }; -// Test that BridgedNativeWidgetImpl remains sane if Init() is never called. +// Test that NativeWidgetNSWindowBridge remains sane if Init() is never called. TEST_F(BridgedNativeWidgetInitTest, InitNotCalled) { // Don't use a Widget* as the delegate. ~Widget() checks for Widget:: // |native_widget_destroyed_| being set to true. That can only happen with a @@ -883,7 +884,7 @@ TEST_F(BridgedNativeWidgetInitTest, InitNotCalled) { new MockNativeWidgetMac(nullptr)); native_widget_mac_ = native_widget.get(); EXPECT_FALSE(bridge()); - EXPECT_FALSE(bridge_host()->GetLocalNSWindow()); + EXPECT_FALSE(GetNSWindowHost()->GetInProcessNSWindow()); } // Tests the shadow type given in InitParams. @@ -930,7 +931,7 @@ TEST_F(BridgedNativeWidgetTest, InputContext) { EXPECT_FALSE([ns_view_ inputContext]); InstallTextField(test_string, ui::TEXT_INPUT_TYPE_TEXT); EXPECT_TRUE([ns_view_ inputContext]); - bridge_host()->text_input_host()->SetTextInputClient(nullptr); + GetNSWindowHost()->text_input_host()->SetTextInputClient(nullptr); EXPECT_FALSE([ns_view_ inputContext]); InstallTextField(test_string, ui::TEXT_INPUT_TYPE_NONE); EXPECT_FALSE([ns_view_ inputContext]); @@ -1339,7 +1340,7 @@ TEST_F(BridgedNativeWidgetTest, TextInput_DeleteCommands) { // Test that we don't crash during an action message even if the TextInputClient // is nil. Regression test for crbug.com/615745. TEST_F(BridgedNativeWidgetTest, NilTextInputClient) { - bridge_host()->text_input_host()->SetTextInputClient(nullptr); + GetNSWindowHost()->text_input_host()->SetTextInputClient(nullptr); NSMutableArray* selectors = [NSMutableArray array]; [selectors addObjectsFromArray:kMoveActions]; [selectors addObjectsFromArray:kSelectActions]; @@ -1751,7 +1752,7 @@ TEST_F(BridgedNativeWidgetTest, TextInput_RecursiveUpdateWindows) { bool saw_update_windows = false; base::RepeatingClosure update_windows_closure = base::BindRepeating( [](bool* saw_update_windows, BridgedContentView* view, - BridgedNativeWidgetHostImpl* host, Textfield* textfield) { + NativeWidgetMacNSWindowHost* host, Textfield* textfield) { // Ensure updateWindows is not invoked recursively. EXPECT_FALSE(*saw_update_windows); *saw_update_windows = true; @@ -1777,11 +1778,11 @@ TEST_F(BridgedNativeWidgetTest, TextInput_RecursiveUpdateWindows) { // Now, the |textfield| set above should have been set again. EXPECT_TRUE(g_fake_current_input_context); }, - &saw_update_windows, ns_view_, bridge_host(), textfield); + &saw_update_windows, ns_view_, GetNSWindowHost(), textfield); SetHandleKeyEventCallback(base::BindRepeating( [](int* saw_return_count, BridgedContentView* view, - BridgedNativeWidgetHostImpl* host, Textfield* textfield, + NativeWidgetMacNSWindowHost* host, Textfield* textfield, const ui::KeyEvent& event) { if (event.key_code() == ui::VKEY_RETURN) { *saw_return_count += 1; @@ -1791,7 +1792,7 @@ TEST_F(BridgedNativeWidgetTest, TextInput_RecursiveUpdateWindows) { } return false; }, - &vkey_return_count, ns_view_, bridge_host())); + &vkey_return_count, ns_view_, GetNSWindowHost())); // Starting text (just insert it). [ns_view_ insertText:@"ă…‚" replacementRange:NSMakeRange(NSNotFound, 0)]; @@ -1880,8 +1881,8 @@ TEST_F(BridgedNativeWidgetSimulateFullscreenTest, FailToEnterAndExit) { object:window]; // On a failure, Cocoa starts by sending an unexpected *exit* fullscreen, and - // BridgedNativeWidgetImpl will think it's just a delayed transition and try - // to go back into fullscreen but get ignored by Cocoa. + // NativeWidgetNSWindowBridge will think it's just a delayed transition and + // try to go back into fullscreen but get ignored by Cocoa. EXPECT_EQ(0, [window ignoredToggleFullScreenCount]); EXPECT_TRUE(bridge()->target_fullscreen_state()); [center postNotificationName:NSWindowDidExitFullScreenNotification diff --git a/chromium/ui/views/cocoa/drag_drop_client_mac.h b/chromium/ui/views/cocoa/drag_drop_client_mac.h index 450a2edb705..608e73a075c 100644 --- a/chromium/ui/views/cocoa/drag_drop_client_mac.h +++ b/chromium/ui/views/cocoa/drag_drop_client_mac.h @@ -19,14 +19,17 @@ namespace gfx { class Point; -} +} // namespace gfx + +namespace remote_cocoa { +class NativeWidgetNSWindowBridge; +} // namespace remote_cocoa namespace views { namespace test { class DragDropClientMacTest; -} +} // namespace test -class BridgedNativeWidgetImpl; class View; // Implements drag and drop on MacViews. This class acts as a bridge between @@ -34,13 +37,14 @@ class View; // DesktopDragDropClientAuraX11. class VIEWS_EXPORT DragDropClientMac : public remote_cocoa::DragDropClient { public: - DragDropClientMac(BridgedNativeWidgetImpl* bridge, View* root_view); + DragDropClientMac(remote_cocoa::NativeWidgetNSWindowBridge* bridge, + View* root_view); ~DragDropClientMac() override; // Initiates a drag and drop session. Returns the drag operation that was // applied at the end of the drag drop session. void StartDragAndDrop(View* view, - const ui::OSExchangeData& data, + std::unique_ptr<ui::OSExchangeData> data, int operation, ui::DragDropTypes::DragEventSource source); @@ -69,7 +73,7 @@ class VIEWS_EXPORT DragDropClientMac : public remote_cocoa::DragDropClient { int last_operation_ = 0; // The bridge between the content view and the drag drop client. - BridgedNativeWidgetImpl* bridge_; // Weak. Owns |this|. + remote_cocoa::NativeWidgetNSWindowBridge* bridge_; // Weak. Owns |this|. // The closure for the drag and drop's run loop. base::OnceClosure quit_closure_; diff --git a/chromium/ui/views/cocoa/drag_drop_client_mac.mm b/chromium/ui/views/cocoa/drag_drop_client_mac.mm index ac1c1b5bdf0..16ed882e3e9 100644 --- a/chromium/ui/views/cocoa/drag_drop_client_mac.mm +++ b/chromium/ui/views/cocoa/drag_drop_client_mac.mm @@ -8,7 +8,7 @@ #include "base/run_loop.h" #include "base/strings/sys_string_conversions.h" #import "components/remote_cocoa/app_shim/bridged_content_view.h" -#import "components/remote_cocoa/app_shim/bridged_native_widget_impl.h" +#import "components/remote_cocoa/app_shim/native_widget_ns_window_bridge.h" #import "ui/base/dragdrop/os_exchange_data_provider_mac.h" #include "ui/gfx/image/image_skia_util_mac.h" #include "ui/views/drag_utils.h" @@ -16,8 +16,9 @@ namespace views { -DragDropClientMac::DragDropClientMac(BridgedNativeWidgetImpl* bridge, - View* root_view) +DragDropClientMac::DragDropClientMac( + remote_cocoa::NativeWidgetNSWindowBridge* bridge, + View* root_view) : drop_helper_(root_view), bridge_(bridge) { DCHECK(bridge); } @@ -26,12 +27,10 @@ DragDropClientMac::~DragDropClientMac() {} void DragDropClientMac::StartDragAndDrop( View* view, - const ui::OSExchangeData& data, + std::unique_ptr<ui::OSExchangeData> data, int operation, ui::DragDropTypes::DragEventSource source) { - // TODO(avi): Why must this data be cloned? - exchange_data_ = - std::make_unique<ui::OSExchangeData>(data.provider().Clone()); + exchange_data_ = std::move(data); source_operation_ = operation; is_drag_source_ = true; diff --git a/chromium/ui/views/cocoa/drag_drop_client_mac_unittest.mm b/chromium/ui/views/cocoa/drag_drop_client_mac_unittest.mm index e19072b03c3..10ad4b6d0ca 100644 --- a/chromium/ui/views/cocoa/drag_drop_client_mac_unittest.mm +++ b/chromium/ui/views/cocoa/drag_drop_client_mac_unittest.mm @@ -12,10 +12,10 @@ #include "base/mac/sdk_forward_declarations.h" #include "base/strings/utf_string_conversions.h" #include "base/threading/thread_task_runner_handle.h" -#import "components/remote_cocoa/app_shim/bridged_native_widget_impl.h" +#import "components/remote_cocoa/app_shim/native_widget_ns_window_bridge.h" #import "ui/base/clipboard/clipboard_util_mac.h" #include "ui/gfx/image/image_unittest_util.h" -#import "ui/views/cocoa/bridged_native_widget_host_impl.h" +#import "ui/views/cocoa/native_widget_mac_ns_window_host.h" #include "ui/views/test/widget_test.h" #include "ui/views/view.h" #include "ui/views/widget/native_widget_mac.h" @@ -158,7 +158,7 @@ class DragDropClientMacTest : public WidgetTest { DragDropClientMacTest() : widget_(new Widget) {} DragDropClientMac* drag_drop_client() { - return bridge_host_->drag_drop_client(); + return ns_window_host_->drag_drop_client(); } NSDragOperation DragUpdate(NSPasteboard* pasteboard) { @@ -189,9 +189,9 @@ class DragDropClientMacTest : public WidgetTest { gfx::Rect bounds(0, 0, 100, 100); widget_->SetBounds(bounds); - bridge_host_ = BridgedNativeWidgetHostImpl::GetFromNativeWindow( + ns_window_host_ = NativeWidgetMacNSWindowHost::GetFromNativeWindow( widget_->GetNativeWindow()); - bridge_ = bridge_host_->bridge_impl(); + bridge_ = ns_window_host_->GetInProcessNSWindowBridge(); widget_->Show(); target_ = new DragDropView(); @@ -209,8 +209,8 @@ class DragDropClientMacTest : public WidgetTest { protected: Widget* widget_ = nullptr; - BridgedNativeWidgetImpl* bridge_ = nullptr; - BridgedNativeWidgetHostImpl* bridge_host_ = nullptr; + remote_cocoa::NativeWidgetNSWindowBridge* bridge_ = nullptr; + NativeWidgetMacNSWindowHost* ns_window_host_ = nullptr; DragDropView* target_ = nullptr; base::scoped_nsobject<MockDraggingInfo> dragging_info_; @@ -242,15 +242,15 @@ TEST_F(DragDropClientMacTest, ReleaseCapture) { // since the runloop will exit before the system has any opportunity to // capture anything. bridge_->AcquireCapture(); - EXPECT_TRUE(bridge_host_->IsMouseCaptureActive()); + EXPECT_TRUE(ns_window_host_->IsMouseCaptureActive()); // Create the drop data - OSExchangeData data; + std::unique_ptr<OSExchangeData> data(std::make_unique<OSExchangeData>()); const base::string16& text = ASCIIToUTF16("text"); - data.SetString(text); - data.provider().SetDragImage(gfx::test::CreateImageSkia(100, 100), - gfx::Vector2d()); - SetData(data); + data->SetString(text); + data->provider().SetDragImage(gfx::test::CreateImageSkia(100, 100), + gfx::Vector2d()); + SetData(*data.get()); // There's no way to cleanly stop NSDraggingSession inside unit tests, so just // don't start it at all. @@ -265,10 +265,10 @@ TEST_F(DragDropClientMacTest, ReleaseCapture) { // It will call ReleaseCapture(). drag_drop_client()->StartDragAndDrop( - target_, data, 0, ui::DragDropTypes::DRAG_EVENT_SOURCE_MOUSE); + target_, std::move(data), 0, ui::DragDropTypes::DRAG_EVENT_SOURCE_MOUSE); // The capture should be released. - EXPECT_FALSE(bridge_host_->IsMouseCaptureActive()); + EXPECT_FALSE(ns_window_host_->IsMouseCaptureActive()); } // Tests if the drag and drop target rejects the dropped data with the diff --git a/chromium/ui/views/cocoa/bridged_native_widget_host_impl.h b/chromium/ui/views/cocoa/native_widget_mac_ns_window_host.h index 75f3dd1ec82..528ec883d22 100644 --- a/chromium/ui/views/cocoa/bridged_native_widget_host_impl.h +++ b/chromium/ui/views/cocoa/native_widget_mac_ns_window_host.h @@ -2,25 +2,25 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef UI_VIEWS_COCOA_BRIDGED_NATIVE_WIDGET_HOST_IMPL_H_ -#define UI_VIEWS_COCOA_BRIDGED_NATIVE_WIDGET_HOST_IMPL_H_ +#ifndef UI_VIEWS_COCOA_NATIVE_WIDGET_MAC_NS_WINDOW_HOST_H_ +#define UI_VIEWS_COCOA_NATIVE_WIDGET_MAC_NS_WINDOW_HOST_H_ #include <memory> #include <vector> #include "base/mac/scoped_nsobject.h" #include "base/macros.h" -#include "components/remote_cocoa/app_shim/bridged_native_widget_host_helper.h" +#include "components/remote_cocoa/app_shim/native_widget_ns_window_host_helper.h" #include "components/remote_cocoa/app_shim/ns_view_ids.h" -#include "components/remote_cocoa/common/bridged_native_widget.mojom.h" -#include "components/remote_cocoa/common/bridged_native_widget_host.mojom.h" +#include "components/remote_cocoa/browser/application_host.h" +#include "components/remote_cocoa/common/native_widget_ns_window.mojom.h" +#include "components/remote_cocoa/common/native_widget_ns_window_host.mojom.h" #include "mojo/public/cpp/bindings/associated_binding.h" #include "ui/accelerated_widget_mac/accelerated_widget_mac.h" -#include "ui/accelerated_widget_mac/display_link_mac.h" #include "ui/base/cocoa/accessibility_focus_overrider.h" #include "ui/base/ime/input_method_delegate.h" #include "ui/compositor/layer_owner.h" -#include "ui/views/cocoa/bridge_factory_host.h" +#include "ui/display/mac/display_link_mac.h" #include "ui/views/cocoa/drag_drop_client_mac.h" #include "ui/views/focus/focus_manager.h" #include "ui/views/views_export.h" @@ -31,23 +31,27 @@ @class NSAccessibilityRemoteUIElement; @class NSView; +namespace remote_cocoa { +class NativeWidgetNSWindowBridge; +class ScopedNativeWindowMapping; +} // namespace remote_cocoa + namespace ui { class RecyclableCompositorMac; } // namespace ui namespace views { -class BridgedNativeWidgetImpl; class NativeWidgetMac; class TextInputHost; // The portion of NativeWidgetMac that lives in the browser process. This -// communicates to the BridgedNativeWidgetImpl, which interacts with the Cocoa -// APIs, and which may live in an app shim process. -class VIEWS_EXPORT BridgedNativeWidgetHostImpl - : public remote_cocoa::BridgedNativeWidgetHostHelper, - public BridgeFactoryHost::Observer, - public remote_cocoa::mojom::BridgedNativeWidgetHost, +// communicates to the NativeWidgetNSWindowBridge, which interacts with the +// Cocoa APIs, and which may live in an app shim process. +class VIEWS_EXPORT NativeWidgetMacNSWindowHost + : public remote_cocoa::NativeWidgetNSWindowHostHelper, + public remote_cocoa::ApplicationHost::Observer, + public remote_cocoa::mojom::NativeWidgetNSWindowHost, public DialogObserver, public FocusChangeListener, public ui::internal::InputMethodDelegate, @@ -58,34 +62,34 @@ class VIEWS_EXPORT BridgedNativeWidgetHostImpl public: // Retrieves the bridge host associated with the given NativeWindow. Returns // null if the supplied handle has no associated Widget. - static BridgedNativeWidgetHostImpl* GetFromNativeWindow( + static NativeWidgetMacNSWindowHost* GetFromNativeWindow( gfx::NativeWindow window); - static BridgedNativeWidgetHostImpl* GetFromNativeView(gfx::NativeView view); + static NativeWidgetMacNSWindowHost* GetFromNativeView(gfx::NativeView view); // Unique integer id handles are used to bridge between the - // BridgedNativeWidgetHostImpl in one process and the BridgedNativeWidgetHost + // NativeWidgetMacNSWindowHost in one process and the NativeWidgetNSWindowHost // potentially in another. - static BridgedNativeWidgetHostImpl* GetFromId( + static NativeWidgetMacNSWindowHost* GetFromId( uint64_t bridged_native_widget_id); uint64_t bridged_native_widget_id() const { return widget_id_; } // Creates one side of the bridge. |owner| must not be NULL. - explicit BridgedNativeWidgetHostImpl(NativeWidgetMac* owner); - ~BridgedNativeWidgetHostImpl() override; + explicit NativeWidgetMacNSWindowHost(NativeWidgetMac* owner); + ~NativeWidgetMacNSWindowHost() override; // The NativeWidgetMac that owns |this|. views::NativeWidgetMac* native_widget_mac() const { return native_widget_mac_; } - BridgedNativeWidgetHostImpl* parent() const { return parent_; } - std::vector<BridgedNativeWidgetHostImpl*> children() const { + NativeWidgetMacNSWindowHost* parent() const { return parent_; } + std::vector<NativeWidgetMacNSWindowHost*> children() const { return children_; } // The bridge factory that was used to create the true NSWindow for this // widget. This is nullptr for in-process windows. - BridgeFactoryHost* bridge_factory_host() const { - return bridge_factory_host_; + remote_cocoa::ApplicationHost* application_host() const { + return application_host_; } TextInputHost* text_input_host() const { return text_input_host_.get(); } @@ -93,7 +97,7 @@ class VIEWS_EXPORT BridgedNativeWidgetHostImpl // A NSWindow that is guaranteed to exist in this process. If the bridge // object for this host is in this process, then this points to the bridge's // NSWindow. Otherwise, it mirrors the id and bounds of the child window. - NativeWidgetMacNSWindow* GetLocalNSWindow() const; + NativeWidgetMacNSWindow* GetInProcessNSWindow() const; // Return the accessibility object for the content NSView. gfx::NativeViewAccessible GetNativeViewAccessibleForNSView() const; @@ -102,13 +106,16 @@ class VIEWS_EXPORT BridgedNativeWidgetHostImpl gfx::NativeViewAccessible GetNativeViewAccessibleForNSWindow() const; // The mojo interface through which to communicate with the underlying - // NSWindow and NSView. - remote_cocoa::mojom::BridgedNativeWidget* bridge() const; + // NSWindow and NSView. This points to either |remote_ns_window_ptr_| or + // |in_process_ns_window_bridge_|. + remote_cocoa::mojom::NativeWidgetNSWindow* GetNSWindowMojo() const; - // Direct access to the BridgedNativeWidgetImpl that this is hosting. + // Direct access to the NativeWidgetNSWindowBridge that this is hosting. // TODO(ccameron): Remove all accesses to this member, and replace them // with methods that may be sent across processes. - BridgedNativeWidgetImpl* bridge_impl() const { return bridge_impl_.get(); } + remote_cocoa::NativeWidgetNSWindowBridge* GetInProcessNSWindowBridge() const { + return in_process_ns_window_bridge_.get(); + } TooltipManager* tooltip_manager() { return tooltip_manager_.get(); } @@ -117,11 +124,12 @@ class VIEWS_EXPORT BridgedNativeWidgetHostImpl } // Create and set the bridge object to be in this process. - void CreateLocalBridge(base::scoped_nsobject<NativeWidgetMacNSWindow> window); + void CreateInProcessNSWindowBridge( + base::scoped_nsobject<NativeWidgetMacNSWindow> window); // Create and set the bridge object to be potentially in another process. - void CreateRemoteBridge( - BridgeFactoryHost* bridge_factory_host, + void CreateRemoteNSWindow( + remote_cocoa::ApplicationHost* application_host, remote_cocoa::mojom::CreateWindowParamsPtr window_create_params); void InitWindow(const Widget::InitParams& params); @@ -191,7 +199,7 @@ class VIEWS_EXPORT BridgedNativeWidgetHostImpl // Set |parent_| and update the old and new parents' |children_|. It is valid // to set |new_parent| to nullptr. Propagate this to the BridgedNativeWidget. - void SetParent(BridgedNativeWidgetHostImpl* new_parent); + void SetParent(NativeWidgetMacNSWindowHost* new_parent); // Properties set and queried by views. Not actually native. void SetNativeWindowProperty(const char* name, void* value); @@ -220,12 +228,12 @@ class VIEWS_EXPORT BridgedNativeWidgetHostImpl void RankNSViewsRecursive(View* view, std::map<NSView*, int>* rank) const; // If we are accessing the BridgedNativeWidget through mojo, then - // |local_window_| is not the true window that is resized. This function - // updates the frame of |local_window_| to keep it in sync for any native - // calls that may use it (e.g, for context menu positioning). + // |in_process_ns_window_| is not the true window that is resized. This + // function updates the frame of |in_process_ns_window_| to keep it in sync + // for any native calls that may use it (e.g, for context menu positioning). void UpdateLocalWindowFrame(const gfx::Rect& frame); - // BridgedNativeWidgetHostHelper: + // NativeWidgetNSWindowHostHelper: id GetNativeViewAccessible() override; void DispatchKeyEvent(ui::KeyEvent* event) override; bool DispatchKeyEventToMenuController(ui::KeyEvent* event) override; @@ -236,10 +244,11 @@ class VIEWS_EXPORT BridgedNativeWidgetHostImpl remote_cocoa::DragDropClient* GetDragDropClient() override; ui::TextInputClient* GetTextInputClient() override; - // BridgeFactoryHost::Observer: - void OnBridgeFactoryHostDestroying(BridgeFactoryHost* host) override; + // remote_cocoa::ApplicationHost::Observer: + void OnApplicationHostDestroying( + remote_cocoa::ApplicationHost* host) override; - // remote_cocoa::mojom::BridgedNativeWidgetHost: + // remote_cocoa::mojom::NativeWidgetNSWindowHost: void OnVisibilityChanged(bool visible) override; void OnWindowNativeThemeChanged() override; void OnViewSizeChanged(const gfx::Size& new_size) override; @@ -306,7 +315,7 @@ class VIEWS_EXPORT BridgedNativeWidgetHostImpl bool require_priority_handler, bool* was_handled) override; - // remote_cocoa::mojom::BridgedNativeWidgetHost, synchronous callbacks: + // remote_cocoa::mojom::NativeWidgetNSWindowHost, synchronous callbacks: void GetSheetOffsetY(GetSheetOffsetYCallback callback) override; void DispatchKeyEventRemote(std::unique_ptr<ui::Event> event, DispatchKeyEventRemoteCallback callback) override; @@ -355,9 +364,7 @@ class VIEWS_EXPORT BridgedNativeWidgetHostImpl void OnDidChangeFocus(View* focused_before, View* focused_now) override; // ui::internal::InputMethodDelegate: - ui::EventDispatchDetails DispatchKeyEventPostIME( - ui::KeyEvent* key, - DispatchKeyEventPostIMECallback callback) override; + ui::EventDispatchDetails DispatchKeyEventPostIME(ui::KeyEvent* key) override; // ui::AccessibilityFocusOverrider::Client: id GetAccessibilityFocusedUIElement() override; @@ -375,13 +382,18 @@ class VIEWS_EXPORT BridgedNativeWidgetHostImpl const uint64_t widget_id_; views::NativeWidgetMac* const native_widget_mac_; // Weak. Owns |this_|. + // Structure used to look up this structure's interfaces from its + // gfx::NativeWindow. + std::unique_ptr<remote_cocoa::ScopedNativeWindowMapping> + native_window_mapping_; + // Parent and child widgets. - BridgedNativeWidgetHostImpl* parent_ = nullptr; - std::vector<BridgedNativeWidgetHostImpl*> children_; + NativeWidgetMacNSWindowHost* parent_ = nullptr; + std::vector<NativeWidgetMacNSWindowHost*> children_; - // The factory that was used to create |bridge_ptr_|. This must be the same - // as |parent_->bridge_factory_host_|. - BridgeFactoryHost* bridge_factory_host_ = nullptr; + // The factory that was used to create |remote_ns_window_ptr_|. This must be + // the same as |parent_->application_host_|. + remote_cocoa::ApplicationHost* application_host_ = nullptr; Widget::InitParams::Type widget_type_ = Widget::InitParams::TYPE_WINDOW; @@ -395,7 +407,7 @@ class VIEWS_EXPORT BridgedNativeWidgetHostImpl // The mojo pointer to a BridgedNativeWidget, which may exist in another // process. - remote_cocoa::mojom::BridgedNativeWidgetAssociatedPtr bridge_ptr_; + remote_cocoa::mojom::NativeWidgetNSWindowAssociatedPtr remote_ns_window_ptr_; // Remote accessibility objects corresponding to the NSWindow and its root // NSView. @@ -407,16 +419,19 @@ class VIEWS_EXPORT BridgedNativeWidgetHostImpl // views::Views accessibility tree when the NSView for this is focused. ui::AccessibilityFocusOverrider accessibility_focus_overrider_; - // TODO(ccameron): Rather than instantiate a BridgedNativeWidgetImpl here, - // we will instantiate a mojo BridgedNativeWidgetImpl interface to a Cocoa + // TODO(ccameron): Rather than instantiate a NativeWidgetNSWindowBridge here, + // we will instantiate a mojo NativeWidgetNSWindowBridge interface to a Cocoa // instance that may be in another process. - std::unique_ptr<BridgedNativeWidgetImpl> bridge_impl_; + std::unique_ptr<remote_cocoa::NativeWidgetNSWindowBridge> + in_process_ns_window_bridge_; - // Window that is guaranteed to exist in this process (see GetLocalNSWindow). - base::scoped_nsobject<NativeWidgetMacNSWindow> local_window_; + // Window that is guaranteed to exist in this process (see + // GetInProcessNSWindow). + base::scoped_nsobject<NativeWidgetMacNSWindow> in_process_ns_window_; - // Id mapping for |local_window_|'s content NSView. - std::unique_ptr<remote_cocoa::ScopedNSViewIdMapping> local_view_id_mapping_; + // Id mapping for |in_process_ns_window_|'s content NSView. + std::unique_ptr<remote_cocoa::ScopedNSViewIdMapping> + in_process_view_id_mapping_; std::unique_ptr<TooltipManager> tooltip_manager_; std::unique_ptr<ui::InputMethod> input_method_; @@ -456,11 +471,11 @@ class VIEWS_EXPORT BridgedNativeWidgetHostImpl // Contains NativeViewHost->gfx::NativeView associations. std::map<const views::View*, NSView*> associated_views_; - mojo::AssociatedBinding<remote_cocoa::mojom::BridgedNativeWidgetHost> - host_mojo_binding_; - DISALLOW_COPY_AND_ASSIGN(BridgedNativeWidgetHostImpl); + mojo::AssociatedBinding<remote_cocoa::mojom::NativeWidgetNSWindowHost> + remote_ns_window_host_binding_; + DISALLOW_COPY_AND_ASSIGN(NativeWidgetMacNSWindowHost); }; } // namespace views -#endif // UI_VIEWS_COCOA_BRIDGED_NATIVE_WIDGET_HOST_IMPL_H_ +#endif // UI_VIEWS_COCOA_NATIVE_WIDGET_MAC_NS_WINDOW_HOST_H_ diff --git a/chromium/ui/views/cocoa/bridged_native_widget_host_impl.mm b/chromium/ui/views/cocoa/native_widget_mac_ns_window_host.mm index be87bb2cede..95f1c6ce19a 100644 --- a/chromium/ui/views/cocoa/bridged_native_widget_host_impl.mm +++ b/chromium/ui/views/cocoa/native_widget_mac_ns_window_host.mm @@ -2,16 +2,17 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "ui/views/cocoa/bridged_native_widget_host_impl.h" +#include "ui/views/cocoa/native_widget_mac_ns_window_host.h" #include <utility> #include "base/mac/foundation_util.h" #include "base/strings/sys_string_conversions.h" -#include "components/remote_cocoa/app_shim/bridged_native_widget_impl.h" #include "components/remote_cocoa/app_shim/mouse_capture.h" #include "components/remote_cocoa/app_shim/native_widget_mac_nswindow.h" +#include "components/remote_cocoa/app_shim/native_widget_ns_window_bridge.h" #include "components/remote_cocoa/browser/ns_view_ids.h" +#include "components/remote_cocoa/browser/window.h" #include "mojo/public/cpp/bindings/strong_associated_binding.h" #include "ui/accelerated_widget_mac/window_resize_helper_mac.h" #include "ui/base/cocoa/animation_utils.h" @@ -22,6 +23,7 @@ #include "ui/base/ime/input_method.h" #include "ui/compositor/recyclable_compositor_mac.h" #include "ui/display/screen.h" +#include "ui/events/cocoa/cocoa_event_utils.h" #include "ui/gfx/geometry/dip_util.h" #include "ui/gfx/mac/coordinate_conversion.h" #include "ui/native_theme/native_theme_mac.h" @@ -38,7 +40,7 @@ #include "ui/views/window/dialog_delegate.h" #include "ui/views/word_lookup_client.h" -using remote_cocoa::mojom::BridgedNativeWidgetInitParams; +using remote_cocoa::mojom::NativeWidgetNSWindowInitParams; using remote_cocoa::mojom::WindowVisibilityState; namespace views { @@ -52,7 +54,7 @@ namespace { // underlying connection closes. // https://crbug.com/915572 class BridgedNativeWidgetHostDummy - : public remote_cocoa::mojom::BridgedNativeWidgetHost { + : public remote_cocoa::mojom::NativeWidgetNSWindowHost { public: BridgedNativeWidgetHostDummy() = default; ~BridgedNativeWidgetHostDummy() override = default; @@ -201,8 +203,8 @@ bool PositionWindowInScreenCoordinates(Widget* widget, return widget && widget->is_top_level(); } -std::map<uint64_t, BridgedNativeWidgetHostImpl*>& GetIdToWidgetHostImplMap() { - static base::NoDestructor<std::map<uint64_t, BridgedNativeWidgetHostImpl*>> +std::map<uint64_t, NativeWidgetMacNSWindowHost*>& GetIdToWidgetHostImplMap() { + static base::NoDestructor<std::map<uint64_t, NativeWidgetMacNSWindowHost*>> id_map; return *id_map; } @@ -220,7 +222,7 @@ uint64_t g_last_bridged_native_widget_id = 0; // IOSurface as its contents, and hosts this CALayer in a CAContext that is // the gfx::CALayerParams is then pointed to. // https://crbug.com/942213 -class BridgedNativeWidgetHostImpl::IOSurfaceToRemoteLayerInterceptor { +class NativeWidgetMacNSWindowHost::IOSurfaceToRemoteLayerInterceptor { public: IOSurfaceToRemoteLayerInterceptor() = default; ~IOSurfaceToRemoteLayerInterceptor() = default; @@ -266,7 +268,7 @@ class BridgedNativeWidgetHostImpl::IOSurfaceToRemoteLayerInterceptor { }; // static -BridgedNativeWidgetHostImpl* BridgedNativeWidgetHostImpl::GetFromNativeWindow( +NativeWidgetMacNSWindowHost* NativeWidgetMacNSWindowHost::GetFromNativeWindow( gfx::NativeWindow native_window) { NSWindow* window = native_window.GetNativeNSWindow(); if (NativeWidgetMacNSWindow* widget_window = @@ -277,13 +279,13 @@ BridgedNativeWidgetHostImpl* BridgedNativeWidgetHostImpl::GetFromNativeWindow( } // static -BridgedNativeWidgetHostImpl* BridgedNativeWidgetHostImpl::GetFromNativeView( +NativeWidgetMacNSWindowHost* NativeWidgetMacNSWindowHost::GetFromNativeView( gfx::NativeView native_view) { return GetFromNativeWindow([native_view.GetNativeNSView() window]); } // static -BridgedNativeWidgetHostImpl* BridgedNativeWidgetHostImpl::GetFromId( +NativeWidgetMacNSWindowHost* NativeWidgetMacNSWindowHost::GetFromId( uint64_t bridged_native_widget_id) { auto found = GetIdToWidgetHostImplMap().find(bridged_native_widget_id); if (found == GetIdToWidgetHostImplMap().end()) @@ -291,30 +293,31 @@ BridgedNativeWidgetHostImpl* BridgedNativeWidgetHostImpl::GetFromId( return found->second; } -BridgedNativeWidgetHostImpl::BridgedNativeWidgetHostImpl(NativeWidgetMac* owner) +NativeWidgetMacNSWindowHost::NativeWidgetMacNSWindowHost(NativeWidgetMac* owner) : widget_id_(++g_last_bridged_native_widget_id), native_widget_mac_(owner), root_view_id_(remote_cocoa::GetNewNSViewId()), accessibility_focus_overrider_(this), text_input_host_(new TextInputHost(this)), - host_mojo_binding_(this) { + remote_ns_window_host_binding_(this) { DCHECK(GetIdToWidgetHostImplMap().find(widget_id_) == GetIdToWidgetHostImplMap().end()); GetIdToWidgetHostImplMap().insert(std::make_pair(widget_id_, this)); DCHECK(owner); } -BridgedNativeWidgetHostImpl::~BridgedNativeWidgetHostImpl() { +NativeWidgetMacNSWindowHost::~NativeWidgetMacNSWindowHost() { DCHECK(children_.empty()); - if (bridge_factory_host_) { - bridge_ptr_.reset(); - bridge_factory_host_->RemoveObserver(this); - bridge_factory_host_ = nullptr; + native_window_mapping_.reset(); + if (application_host_) { + remote_ns_window_ptr_.reset(); + application_host_->RemoveObserver(this); + application_host_ = nullptr; } // Workaround for https://crbug.com/915572 - if (host_mojo_binding_.is_bound()) { - auto request = host_mojo_binding_.Unbind(); + if (remote_ns_window_host_binding_.is_bound()) { + auto request = remote_ns_window_host_binding_.Unbind(); if (request.is_pending()) { mojo::MakeStrongAssociatedBinding( std::make_unique<BridgedNativeWidgetHostDummy>(), std::move(request)); @@ -331,98 +334,108 @@ BridgedNativeWidgetHostImpl::~BridgedNativeWidgetHostImpl() { // destruction. // TODO(ccameron): When all communication from |bridge_| to this goes through // the BridgedNativeWidgetHost, this can be replaced with closing that pipe. - bridge_impl_.reset(); + in_process_ns_window_bridge_.reset(); SetFocusManager(nullptr); DestroyCompositor(); } -NativeWidgetMacNSWindow* BridgedNativeWidgetHostImpl::GetLocalNSWindow() const { - return local_window_.get(); +NativeWidgetMacNSWindow* NativeWidgetMacNSWindowHost::GetInProcessNSWindow() + const { + return in_process_ns_window_.get(); } gfx::NativeViewAccessible -BridgedNativeWidgetHostImpl::GetNativeViewAccessibleForNSView() const { - if (bridge_impl_) - return bridge_impl_->ns_view(); +NativeWidgetMacNSWindowHost::GetNativeViewAccessibleForNSView() const { + if (in_process_ns_window_bridge_) + return in_process_ns_window_bridge_->ns_view(); return remote_view_accessible_.get(); } gfx::NativeViewAccessible -BridgedNativeWidgetHostImpl::GetNativeViewAccessibleForNSWindow() const { - if (bridge_impl_) - return bridge_impl_->ns_window(); +NativeWidgetMacNSWindowHost::GetNativeViewAccessibleForNSWindow() const { + if (in_process_ns_window_bridge_) + return in_process_ns_window_bridge_->ns_window(); return remote_window_accessible_.get(); } -remote_cocoa::mojom::BridgedNativeWidget* BridgedNativeWidgetHostImpl::bridge() - const { - if (bridge_ptr_) - return bridge_ptr_.get(); - if (bridge_impl_) - return bridge_impl_.get(); +remote_cocoa::mojom::NativeWidgetNSWindow* +NativeWidgetMacNSWindowHost::GetNSWindowMojo() const { + if (remote_ns_window_ptr_) + return remote_ns_window_ptr_.get(); + if (in_process_ns_window_bridge_) + return in_process_ns_window_bridge_.get(); return nullptr; } -void BridgedNativeWidgetHostImpl::CreateLocalBridge( +void NativeWidgetMacNSWindowHost::CreateInProcessNSWindowBridge( base::scoped_nsobject<NativeWidgetMacNSWindow> window) { - local_window_ = window; - bridge_impl_ = std::make_unique<BridgedNativeWidgetImpl>( - widget_id_, this, this, text_input_host_.get()); - bridge_impl_->SetWindow(window); + in_process_ns_window_ = window; + in_process_ns_window_bridge_ = + std::make_unique<remote_cocoa::NativeWidgetNSWindowBridge>( + widget_id_, this, this, text_input_host_.get()); + in_process_ns_window_bridge_->SetWindow(window); } -void BridgedNativeWidgetHostImpl::CreateRemoteBridge( - BridgeFactoryHost* bridge_factory_host, +void NativeWidgetMacNSWindowHost::CreateRemoteNSWindow( + remote_cocoa::ApplicationHost* application_host, remote_cocoa::mojom::CreateWindowParamsPtr window_create_params) { accessibility_focus_overrider_.SetAppIsRemote(true); - bridge_factory_host_ = bridge_factory_host; - bridge_factory_host_->AddObserver(this); + application_host_ = application_host; + application_host_->AddObserver(this); // Create a local invisible window that will be used as the gfx::NativeWindow // handle to track this window in this process. { - auto local_window_create_params = + auto in_process_ns_window_create_params = remote_cocoa::mojom::CreateWindowParams::New(); - local_window_create_params->style_mask = NSBorderlessWindowMask; - local_window_ = BridgedNativeWidgetImpl::CreateNSWindow( - local_window_create_params.get()); - [local_window_ setBridgedNativeWidgetId:widget_id_]; - [local_window_ setAlphaValue:0.0]; - local_view_id_mapping_ = + in_process_ns_window_create_params->style_mask = NSBorderlessWindowMask; + in_process_ns_window_ = + remote_cocoa::NativeWidgetNSWindowBridge::CreateNSWindow( + in_process_ns_window_create_params.get()); + [in_process_ns_window_ setBridgedNativeWidgetId:widget_id_]; + [in_process_ns_window_ setAlphaValue:0.0]; + in_process_view_id_mapping_ = std::make_unique<remote_cocoa::ScopedNSViewIdMapping>( - root_view_id_, [local_window_ contentView]); + root_view_id_, [in_process_ns_window_ contentView]); } - // Initialize |bridge_ptr_| to point to a bridge created by |factory|. - remote_cocoa::mojom::BridgedNativeWidgetHostAssociatedPtr host_ptr; - host_mojo_binding_.Bind(mojo::MakeRequest(&host_ptr), - ui::WindowResizeHelperMac::Get()->task_runner()); + // Initialize |remote_ns_window_ptr_| to point to a bridge created by + // |factory|. + remote_cocoa::mojom::NativeWidgetNSWindowHostAssociatedPtr host_ptr; + remote_ns_window_host_binding_.Bind( + mojo::MakeRequest(&host_ptr), + ui::WindowResizeHelperMac::Get()->task_runner()); remote_cocoa::mojom::TextInputHostAssociatedPtr text_input_host_ptr; text_input_host_->BindRequest(mojo::MakeRequest(&text_input_host_ptr)); - bridge_factory_host_->GetFactory()->CreateBridgedNativeWidget( - widget_id_, mojo::MakeRequest(&bridge_ptr_), host_ptr.PassInterface(), - text_input_host_ptr.PassInterface()); + application_host_->GetApplication()->CreateNativeWidgetNSWindow( + widget_id_, mojo::MakeRequest(&remote_ns_window_ptr_), + host_ptr.PassInterface(), text_input_host_ptr.PassInterface()); // Create the window in its process, and attach it to its parent window. - bridge()->CreateWindow(std::move(window_create_params)); + GetNSWindowMojo()->CreateWindow(std::move(window_create_params)); } -void BridgedNativeWidgetHostImpl::InitWindow(const Widget::InitParams& params) { +void NativeWidgetMacNSWindowHost::InitWindow(const Widget::InitParams& params) { + native_window_mapping_ = + std::make_unique<remote_cocoa::ScopedNativeWindowMapping>( + gfx::NativeWindow(in_process_ns_window_.get()), application_host_, + in_process_ns_window_bridge_.get(), GetNSWindowMojo()); + Widget* widget = native_widget_mac_->GetWidget(); // Tooltip Widgets shouldn't have their own tooltip manager, but tooltips are // native on Mac, so nothing should ever want one in Widget form. DCHECK_NE(params.type, Widget::InitParams::TYPE_TOOLTIP); widget_type_ = params.type; - tooltip_manager_.reset(new TooltipManagerMac(bridge())); + tooltip_manager_.reset(new TooltipManagerMac(GetNSWindowMojo())); // Initialize the window. { - auto bridge_params = BridgedNativeWidgetInitParams::New(); - bridge_params->modal_type = widget->widget_delegate()->GetModalType(); - bridge_params->is_translucent = + auto window_params = NativeWidgetNSWindowInitParams::New(); + window_params->modal_type = widget->widget_delegate()->GetModalType(); + window_params->is_translucent = params.opacity == Widget::InitParams::TRANSLUCENT_WINDOW; - bridge_params->widget_is_top_level = widget->is_top_level(); - bridge_params->position_window_in_screen_coords = + window_params->widget_is_top_level = widget->is_top_level(); + window_params->position_window_in_screen_coords = PositionWindowInScreenCoordinates(widget, widget_type_); // OSX likes to put shadows on most things. However, frameless windows (with @@ -431,25 +444,25 @@ void BridgedNativeWidgetHostImpl::InitWindow(const Widget::InitParams& params) { // Mac. switch (params.shadow_type) { case Widget::InitParams::SHADOW_TYPE_NONE: - bridge_params->has_window_server_shadow = false; + window_params->has_window_server_shadow = false; break; case Widget::InitParams::SHADOW_TYPE_DEFAULT: // Controls should get views shadows instead of native shadows. - bridge_params->has_window_server_shadow = + window_params->has_window_server_shadow = params.type != Widget::InitParams::TYPE_CONTROL; break; case Widget::InitParams::SHADOW_TYPE_DROP: - bridge_params->has_window_server_shadow = true; + window_params->has_window_server_shadow = true; break; } // No default case, to pick up new types. // Include "regular" windows without the standard frame in the window cycle. // These use NSBorderlessWindowMask so do not get it by default. - bridge_params->force_into_collection_cycle = + window_params->force_into_collection_cycle = widget_type_ == Widget::InitParams::TYPE_WINDOW && params.remove_standard_frame; - bridge()->InitWindow(std::move(bridge_params)); + GetNSWindowMojo()->InitWindow(std::move(window_params)); } // Set a meaningful initial bounds. Note that except for frameless widgets @@ -459,22 +472,22 @@ void BridgedNativeWidgetHostImpl::InitWindow(const Widget::InitParams& params) { // before calling Widget::Show() to avoid a kWindowSizeDeterminedLater-sized // (i.e. 1x1) window appearing. UpdateLocalWindowFrame(params.bounds); - bridge()->SetInitialBounds(params.bounds, widget->GetMinimumSize()); + GetNSWindowMojo()->SetInitialBounds(params.bounds, widget->GetMinimumSize()); - // TODO(ccameron): Correctly set these based |local_window_|. + // TODO(ccameron): Correctly set these based |in_process_ns_window_|. window_bounds_in_screen_ = params.bounds; content_bounds_in_screen_ = params.bounds; // Widgets for UI controls (usually layered above web contents) start visible. if (widget_type_ == Widget::InitParams::TYPE_CONTROL) - bridge()->SetVisibilityState(WindowVisibilityState::kShowInactive); + GetNSWindowMojo()->SetVisibilityState(WindowVisibilityState::kShowInactive); } -void BridgedNativeWidgetHostImpl::CloseWindowNow() { - bool is_out_of_process = !bridge_impl_; +void NativeWidgetMacNSWindowHost::CloseWindowNow() { + bool is_out_of_process = !in_process_ns_window_bridge_; // Note that CloseWindowNow may delete |this| for in-process windows. - if (bridge()) - bridge()->CloseWindowNow(); + if (GetNSWindowMojo()) + GetNSWindowMojo()->CloseWindowNow(); // If it is out-of-process, then simulate the calls that would have been // during window closure. @@ -486,36 +499,46 @@ void BridgedNativeWidgetHostImpl::CloseWindowNow() { } } -void BridgedNativeWidgetHostImpl::SetBounds(const gfx::Rect& bounds) { +void NativeWidgetMacNSWindowHost::SetBounds(const gfx::Rect& bounds) { UpdateLocalWindowFrame(bounds); - bridge()->SetBounds(bounds, - native_widget_mac_->GetWidget()->GetMinimumSize()); + GetNSWindowMojo()->SetBounds( + bounds, native_widget_mac_->GetWidget()->GetMinimumSize()); + + if (remote_ns_window_ptr_) { + gfx::Rect window_in_screen = + gfx::ScreenRectFromNSRect([in_process_ns_window_ frame]); + gfx::Rect content_in_screen = + gfx::ScreenRectFromNSRect([in_process_ns_window_ + contentRectForFrameRect:[in_process_ns_window_ frame]]); + + OnWindowGeometryChanged(window_in_screen, content_in_screen); + } } -void BridgedNativeWidgetHostImpl::SetFullscreen(bool fullscreen) { +void NativeWidgetMacNSWindowHost::SetFullscreen(bool fullscreen) { // Note that when the NSWindow begins a fullscreen transition, the value of // |target_fullscreen_state_| updates via OnWindowFullscreenTransitionStart. // The update here is necessary for the case where we are currently in // transition (and therefore OnWindowFullscreenTransitionStart will not be // called until the current transition completes). target_fullscreen_state_ = fullscreen; - bridge()->SetFullscreen(target_fullscreen_state_); + GetNSWindowMojo()->SetFullscreen(target_fullscreen_state_); } -void BridgedNativeWidgetHostImpl::SetRootView(views::View* root_view) { +void NativeWidgetMacNSWindowHost::SetRootView(views::View* root_view) { root_view_ = root_view; if (root_view_) { // TODO(ccameron): Drag-drop functionality does not yet run over mojo. - if (bridge_impl_) { - drag_drop_client_.reset( - new DragDropClientMac(bridge_impl_.get(), root_view_)); + if (in_process_ns_window_bridge_) { + drag_drop_client_.reset(new DragDropClientMac( + in_process_ns_window_bridge_.get(), root_view_)); } } else { drag_drop_client_.reset(); } } -void BridgedNativeWidgetHostImpl::CreateCompositor( +void NativeWidgetMacNSWindowHost::CreateCompositor( const Widget::InitParams& params) { DCHECK(!compositor_); DCHECK(!layer()); @@ -550,10 +573,10 @@ void BridgedNativeWidgetHostImpl::CreateCompositor( if (is_visible_) compositor_->Unsuspend(); - bridge()->InitCompositorView(); + GetNSWindowMojo()->InitCompositorView(); } -void BridgedNativeWidgetHostImpl::UpdateCompositorProperties() { +void NativeWidgetMacNSWindowHost::UpdateCompositorProperties() { if (!compositor_) return; gfx::Size surface_size_in_dip = content_bounds_in_screen_.size(); @@ -563,11 +586,11 @@ void BridgedNativeWidgetHostImpl::UpdateCompositorProperties() { display_.device_scale_factor()); } -void BridgedNativeWidgetHostImpl::DestroyCompositor() { +void NativeWidgetMacNSWindowHost::DestroyCompositor() { if (layer()) { // LayerOwner supports a change in ownership, e.g., to animate a closing // window, but that won't work as expected for the root layer in - // BridgedNativeWidgetImpl. + // NativeWidgetNSWindowBridge. DCHECK_EQ(this, layer()->owner()); layer()->CompleteAllAnimations(); layer()->SuppressPaint(); @@ -582,7 +605,7 @@ void BridgedNativeWidgetHostImpl::DestroyCompositor() { std::move(compositor_)); } -void BridgedNativeWidgetHostImpl::SetFocusManager(FocusManager* focus_manager) { +void NativeWidgetMacNSWindowHost::SetFocusManager(FocusManager* focus_manager) { if (focus_manager_ == focus_manager) return; @@ -602,37 +625,33 @@ void BridgedNativeWidgetHostImpl::SetFocusManager(FocusManager* focus_manager) { OnDidChangeFocus(nullptr, new_focus); } -bool BridgedNativeWidgetHostImpl::SetWindowTitle(const base::string16& title) { +bool NativeWidgetMacNSWindowHost::SetWindowTitle(const base::string16& title) { if (window_title_ == title) return false; window_title_ = title; - bridge()->SetWindowTitle(window_title_); + GetNSWindowMojo()->SetWindowTitle(window_title_); return true; } -void BridgedNativeWidgetHostImpl::OnWidgetInitDone() { +void NativeWidgetMacNSWindowHost::OnWidgetInitDone() { Widget* widget = native_widget_mac_->GetWidget(); if (DialogDelegate* dialog = widget->widget_delegate()->AsDialogDelegate()) dialog->AddObserver(this); } -bool BridgedNativeWidgetHostImpl::RedispatchKeyEvent(NSEvent* event) { +bool NativeWidgetMacNSWindowHost::RedispatchKeyEvent(NSEvent* event) { // If the target window is in-process, then redispatch the event directly, // and give an accurate return value. - if (bridge_impl_) - return bridge_impl_->RedispatchKeyEvent(event); + if (in_process_ns_window_bridge_) + return in_process_ns_window_bridge_->RedispatchKeyEvent(event); // If the target window is out of process then always report the event as // handled (because it should never be handled in this process). - bridge()->RedispatchKeyEvent( - [event type], [event modifierFlags], [event timestamp], - base::SysNSStringToUTF16([event characters]), - base::SysNSStringToUTF16([event charactersIgnoringModifiers]), - [event keyCode]); + GetNSWindowMojo()->RedispatchKeyEvent(ui::EventToData(event)); return true; } -ui::InputMethod* BridgedNativeWidgetHostImpl::GetInputMethod() { +ui::InputMethod* NativeWidgetMacNSWindowHost::GetInputMethod() { if (!input_method_) { input_method_ = ui::CreateInputMethod(this, gfx::kNullAcceleratedWidget); // For now, use always-focused mode on Mac for the input method. @@ -642,13 +661,13 @@ ui::InputMethod* BridgedNativeWidgetHostImpl::GetInputMethod() { return input_method_.get(); } -gfx::Rect BridgedNativeWidgetHostImpl::GetRestoredBounds() const { +gfx::Rect NativeWidgetMacNSWindowHost::GetRestoredBounds() const { if (target_fullscreen_state_ || in_fullscreen_transition_) return window_bounds_before_fullscreen_; return window_bounds_in_screen_; } -void BridgedNativeWidgetHostImpl::SetNativeWindowProperty(const char* name, +void NativeWidgetMacNSWindowHost::SetNativeWindowProperty(const char* name, void* value) { if (value) native_window_properties_[name] = value; @@ -656,7 +675,7 @@ void BridgedNativeWidgetHostImpl::SetNativeWindowProperty(const char* name, native_window_properties_.erase(name); } -void* BridgedNativeWidgetHostImpl::GetNativeWindowProperty( +void* NativeWidgetMacNSWindowHost::GetNativeWindowProperty( const char* name) const { auto found = native_window_properties_.find(name); if (found == native_window_properties_.end()) @@ -664,8 +683,8 @@ void* BridgedNativeWidgetHostImpl::GetNativeWindowProperty( return found->second; } -void BridgedNativeWidgetHostImpl::SetParent( - BridgedNativeWidgetHostImpl* new_parent) { +void NativeWidgetMacNSWindowHost::SetParent( + NativeWidgetMacNSWindowHost* new_parent) { if (new_parent == parent_) return; @@ -681,48 +700,48 @@ void BridgedNativeWidgetHostImpl::SetParent( // same process that we were already hosted by. If this is not the case, just // close the Widget. // https://crbug.com/957927 - BridgeFactoryHost* new_bridge_factory_host = - new_parent ? new_parent->bridge_factory_host() : bridge_factory_host_; - if (new_bridge_factory_host != bridge_factory_host_) { + remote_cocoa::ApplicationHost* new_application_host = + new_parent ? new_parent->application_host() : application_host_; + if (new_application_host != application_host_) { DLOG(ERROR) << "Cannot migrate views::NativeWidget to another process, " "closing it instead."; - bridge()->CloseWindow(); + GetNSWindowMojo()->CloseWindow(); return; } parent_ = new_parent; if (parent_) { parent_->children_.push_back(this); - bridge()->SetParent(parent_->bridged_native_widget_id()); + GetNSWindowMojo()->SetParent(parent_->bridged_native_widget_id()); } else { - bridge()->SetParent(0); + GetNSWindowMojo()->SetParent(0); } } -void BridgedNativeWidgetHostImpl::SetAssociationForView(const View* view, +void NativeWidgetMacNSWindowHost::SetAssociationForView(const View* view, NSView* native_view) { DCHECK_EQ(0u, associated_views_.count(view)); associated_views_[view] = native_view; native_widget_mac_->GetWidget()->ReorderNativeViews(); } -void BridgedNativeWidgetHostImpl::ClearAssociationForView(const View* view) { +void NativeWidgetMacNSWindowHost::ClearAssociationForView(const View* view) { auto it = associated_views_.find(view); DCHECK(it != associated_views_.end()); associated_views_.erase(it); } -void BridgedNativeWidgetHostImpl::ReorderChildViews() { +void NativeWidgetMacNSWindowHost::ReorderChildViews() { Widget* widget = native_widget_mac_->GetWidget(); if (!widget->GetRootView()) return; std::map<NSView*, int> rank; RankNSViewsRecursive(widget->GetRootView(), &rank); - if (bridge_impl_) - bridge_impl_->SortSubviews(std::move(rank)); + if (in_process_ns_window_bridge_) + in_process_ns_window_bridge_->SortSubviews(std::move(rank)); } -void BridgedNativeWidgetHostImpl::RankNSViewsRecursive( +void NativeWidgetMacNSWindowHost::RankNSViewsRecursive( View* view, std::map<NSView*, int>* rank) const { auto it = associated_views_.find(view); @@ -732,32 +751,35 @@ void BridgedNativeWidgetHostImpl::RankNSViewsRecursive( RankNSViewsRecursive(child, rank); } -void BridgedNativeWidgetHostImpl::UpdateLocalWindowFrame( +void NativeWidgetMacNSWindowHost::UpdateLocalWindowFrame( const gfx::Rect& frame) { - if (!bridge_ptr_) + if (!remote_ns_window_ptr_) return; - [local_window_ setFrame:gfx::ScreenRectToNSRect(frame) display:NO animate:NO]; + [in_process_ns_window_ setFrame:gfx::ScreenRectToNSRect(frame) + display:NO + animate:NO]; } // static -NSView* BridgedNativeWidgetHostImpl::GetGlobalCaptureView() { +NSView* NativeWidgetMacNSWindowHost::GetGlobalCaptureView() { // TODO(ccameron): This will not work across process boundaries. - return [CocoaMouseCapture::GetGlobalCaptureWindow() contentView]; + return + [remote_cocoa::CocoaMouseCapture::GetGlobalCaptureWindow() contentView]; } //////////////////////////////////////////////////////////////////////////////// -// BridgedNativeWidgetHostImpl, remote_cocoa::BridgedNativeWidgetHostHelper: +// NativeWidgetMacNSWindowHost, remote_cocoa::BridgedNativeWidgetHostHelper: -id BridgedNativeWidgetHostImpl::GetNativeViewAccessible() { +id NativeWidgetMacNSWindowHost::GetNativeViewAccessible() { return root_view_ ? root_view_->GetNativeViewAccessible() : nil; } -void BridgedNativeWidgetHostImpl::DispatchKeyEvent(ui::KeyEvent* event) { +void NativeWidgetMacNSWindowHost::DispatchKeyEvent(ui::KeyEvent* event) { ignore_result( root_view_->GetWidget()->GetInputMethod()->DispatchKeyEvent(event)); } -bool BridgedNativeWidgetHostImpl::DispatchKeyEventToMenuController( +bool NativeWidgetMacNSWindowHost::DispatchKeyEventToMenuController( ui::KeyEvent* event) { MenuController* menu_controller = MenuController::GetActiveInstance(); if (menu_controller && root_view_ && @@ -768,21 +790,21 @@ bool BridgedNativeWidgetHostImpl::DispatchKeyEventToMenuController( return false; } -remote_cocoa::DragDropClient* BridgedNativeWidgetHostImpl::GetDragDropClient() { +remote_cocoa::DragDropClient* NativeWidgetMacNSWindowHost::GetDragDropClient() { return drag_drop_client_.get(); } -ui::TextInputClient* BridgedNativeWidgetHostImpl::GetTextInputClient() { +ui::TextInputClient* NativeWidgetMacNSWindowHost::GetTextInputClient() { return text_input_host_->GetTextInputClient(); } //////////////////////////////////////////////////////////////////////////////// -// BridgedNativeWidgetHostImpl, BridgeFactoryHost::Observer: -void BridgedNativeWidgetHostImpl::OnBridgeFactoryHostDestroying( - BridgeFactoryHost* host) { - DCHECK_EQ(host, bridge_factory_host_); - bridge_factory_host_->RemoveObserver(this); - bridge_factory_host_ = nullptr; +// NativeWidgetMacNSWindowHost, remote_cocoa::ApplicationHost::Observer: +void NativeWidgetMacNSWindowHost::OnApplicationHostDestroying( + remote_cocoa::ApplicationHost* host) { + DCHECK_EQ(host, application_host_); + application_host_->RemoveObserver(this); + application_host_ = nullptr; // Because the process hosting this window has ended, close the window by // sending the window close messages that the bridge would have sent. @@ -792,15 +814,15 @@ void BridgedNativeWidgetHostImpl::OnBridgeFactoryHostDestroying( // tear-down assumptions). This would have been done by the bridge, had it // shut down cleanly. while (!children_.empty()) - children_.front()->OnBridgeFactoryHostDestroying(host); + children_.front()->OnApplicationHostDestroying(host); OnWindowHasClosed(); } //////////////////////////////////////////////////////////////////////////////// -// BridgedNativeWidgetHostImpl, -// remote_cocoa::mojom::BridgedNativeWidgetHost: +// NativeWidgetMacNSWindowHost, +// remote_cocoa::mojom::NativeWidgetNSWindowHost: -void BridgedNativeWidgetHostImpl::OnVisibilityChanged(bool window_visible) { +void NativeWidgetMacNSWindowHost::OnVisibilityChanged(bool window_visible) { is_visible_ = window_visible; if (compositor_) { layer()->SetVisible(window_visible); @@ -815,26 +837,26 @@ void BridgedNativeWidgetHostImpl::OnVisibilityChanged(bool window_visible) { window_visible); } -void BridgedNativeWidgetHostImpl::OnWindowNativeThemeChanged() { +void NativeWidgetMacNSWindowHost::OnWindowNativeThemeChanged() { ui::NativeTheme::GetInstanceForNativeUi()->NotifyObservers(); } -void BridgedNativeWidgetHostImpl::OnScrollEvent( +void NativeWidgetMacNSWindowHost::OnScrollEvent( std::unique_ptr<ui::Event> event) { root_view_->GetWidget()->OnScrollEvent(event->AsScrollEvent()); } -void BridgedNativeWidgetHostImpl::OnMouseEvent( +void NativeWidgetMacNSWindowHost::OnMouseEvent( std::unique_ptr<ui::Event> event) { root_view_->GetWidget()->OnMouseEvent(event->AsMouseEvent()); } -void BridgedNativeWidgetHostImpl::OnGestureEvent( +void NativeWidgetMacNSWindowHost::OnGestureEvent( std::unique_ptr<ui::Event> event) { root_view_->GetWidget()->OnGestureEvent(event->AsGestureEvent()); } -bool BridgedNativeWidgetHostImpl::DispatchKeyEventRemote( +bool NativeWidgetMacNSWindowHost::DispatchKeyEventRemote( std::unique_ptr<ui::Event> event, bool* event_handled) { DispatchKeyEvent(event->AsKeyEvent()); @@ -842,7 +864,7 @@ bool BridgedNativeWidgetHostImpl::DispatchKeyEventRemote( return true; } -bool BridgedNativeWidgetHostImpl::DispatchKeyEventToMenuControllerRemote( +bool NativeWidgetMacNSWindowHost::DispatchKeyEventToMenuControllerRemote( std::unique_ptr<ui::Event> event, bool* event_swallowed, bool* event_handled) { @@ -851,7 +873,7 @@ bool BridgedNativeWidgetHostImpl::DispatchKeyEventToMenuControllerRemote( return true; } -bool BridgedNativeWidgetHostImpl::GetHasMenuController( +bool NativeWidgetMacNSWindowHost::GetHasMenuController( bool* has_menu_controller) { MenuController* menu_controller = MenuController::GetActiveInstance(); *has_menu_controller = menu_controller && root_view_ && @@ -861,23 +883,23 @@ bool BridgedNativeWidgetHostImpl::GetHasMenuController( return true; } -void BridgedNativeWidgetHostImpl::OnViewSizeChanged(const gfx::Size& new_size) { +void NativeWidgetMacNSWindowHost::OnViewSizeChanged(const gfx::Size& new_size) { root_view_->SetSize(new_size); } -bool BridgedNativeWidgetHostImpl::GetSheetOffsetY(int32_t* offset_y) { +bool NativeWidgetMacNSWindowHost::GetSheetOffsetY(int32_t* offset_y) { *offset_y = native_widget_mac_->SheetOffsetY(); return true; } -void BridgedNativeWidgetHostImpl::SetKeyboardAccessible(bool enabled) { +void NativeWidgetMacNSWindowHost::SetKeyboardAccessible(bool enabled) { views::FocusManager* focus_manager = root_view_->GetWidget()->GetFocusManager(); if (focus_manager) focus_manager->SetKeyboardAccessible(enabled); } -void BridgedNativeWidgetHostImpl::OnIsFirstResponderChanged( +void NativeWidgetMacNSWindowHost::OnIsFirstResponderChanged( bool is_first_responder) { accessibility_focus_overrider_.SetViewIsFirstResponder(is_first_responder); if (is_first_responder) { @@ -891,14 +913,14 @@ void BridgedNativeWidgetHostImpl::OnIsFirstResponderChanged( } } -void BridgedNativeWidgetHostImpl::OnMouseCaptureActiveChanged(bool is_active) { +void NativeWidgetMacNSWindowHost::OnMouseCaptureActiveChanged(bool is_active) { DCHECK_NE(is_mouse_capture_active_, is_active); is_mouse_capture_active_ = is_active; if (!is_mouse_capture_active_) native_widget_mac_->GetWidget()->OnMouseCaptureLost(); } -bool BridgedNativeWidgetHostImpl::GetIsDraggableBackgroundAt( +bool NativeWidgetMacNSWindowHost::GetIsDraggableBackgroundAt( const gfx::Point& location_in_content, bool* is_draggable_background) { int component = @@ -907,7 +929,7 @@ bool BridgedNativeWidgetHostImpl::GetIsDraggableBackgroundAt( return true; } -bool BridgedNativeWidgetHostImpl::GetTooltipTextAt( +bool NativeWidgetMacNSWindowHost::GetTooltipTextAt( const gfx::Point& location_in_content, base::string16* new_tooltip_text) { views::View* view = @@ -921,7 +943,7 @@ bool BridgedNativeWidgetHostImpl::GetTooltipTextAt( return true; } -void BridgedNativeWidgetHostImpl::GetWordAt( +void NativeWidgetMacNSWindowHost::GetWordAt( const gfx::Point& location_in_content, bool* found_word, gfx::DecoratedText* decorated_word, @@ -949,12 +971,12 @@ void BridgedNativeWidgetHostImpl::GetWordAt( *found_word = true; } -bool BridgedNativeWidgetHostImpl::GetWidgetIsModal(bool* widget_is_modal) { +bool NativeWidgetMacNSWindowHost::GetWidgetIsModal(bool* widget_is_modal) { *widget_is_modal = native_widget_mac_->GetWidget()->IsModal(); return true; } -bool BridgedNativeWidgetHostImpl::GetIsFocusedViewTextual(bool* is_textual) { +bool NativeWidgetMacNSWindowHost::GetIsFocusedViewTextual(bool* is_textual) { views::FocusManager* focus_manager = root_view_ ? root_view_->GetWidget()->GetFocusManager() : nullptr; *is_textual = focus_manager && focus_manager->GetFocusedView() && @@ -963,7 +985,7 @@ bool BridgedNativeWidgetHostImpl::GetIsFocusedViewTextual(bool* is_textual) { return true; } -void BridgedNativeWidgetHostImpl::OnWindowGeometryChanged( +void NativeWidgetMacNSWindowHost::OnWindowGeometryChanged( const gfx::Rect& new_window_bounds_in_screen, const gfx::Rect& new_content_bounds_in_screen) { UpdateLocalWindowFrame(new_window_bounds_in_screen); @@ -983,15 +1005,16 @@ void BridgedNativeWidgetHostImpl::OnWindowGeometryChanged( // Note we can't use new_window_bounds_in_screen.size(), since it includes the // titlebar for the purposes of detecting a window move. - if (content_has_resized) + if (content_has_resized) { native_widget_mac_->GetWidget()->OnNativeWidgetSizeChanged( content_bounds_in_screen_.size()); - // Update the compositor surface and layer size. - UpdateCompositorProperties(); + // Update the compositor surface and layer size. + UpdateCompositorProperties(); + } } -void BridgedNativeWidgetHostImpl::OnWindowFullscreenTransitionStart( +void NativeWidgetMacNSWindowHost::OnWindowFullscreenTransitionStart( bool target_fullscreen_state) { target_fullscreen_state_ = target_fullscreen_state; in_fullscreen_transition_ = true; @@ -1004,7 +1027,7 @@ void BridgedNativeWidgetHostImpl::OnWindowFullscreenTransitionStart( native_widget_mac_->OnWindowFullscreenStateChange(); } -void BridgedNativeWidgetHostImpl::OnWindowFullscreenTransitionComplete( +void NativeWidgetMacNSWindowHost::OnWindowFullscreenTransitionComplete( bool actual_fullscreen_state) { in_fullscreen_transition_ = false; @@ -1012,14 +1035,14 @@ void BridgedNativeWidgetHostImpl::OnWindowFullscreenTransitionComplete( native_widget_mac_->OnSizeConstraintsChanged(); } -void BridgedNativeWidgetHostImpl::OnWindowMiniaturizedChanged( +void NativeWidgetMacNSWindowHost::OnWindowMiniaturizedChanged( bool miniaturized) { is_miniaturized_ = miniaturized; if (native_widget_mac_) native_widget_mac_->GetWidget()->OnNativeWidgetWindowShowStateChanged(); } -void BridgedNativeWidgetHostImpl::OnWindowDisplayChanged( +void NativeWidgetMacNSWindowHost::OnWindowDisplayChanged( const display::Display& new_display) { bool scale_factor_changed = display_.device_scale_factor() != new_display.device_scale_factor(); @@ -1041,7 +1064,7 @@ void BridgedNativeWidgetHostImpl::OnWindowDisplayChanged( } } -void BridgedNativeWidgetHostImpl::OnWindowWillClose() { +void NativeWidgetMacNSWindowHost::OnWindowWillClose() { Widget* widget = native_widget_mac_->GetWidget(); if (DialogDelegate* dialog = widget->widget_delegate()->AsDialogDelegate()) dialog->RemoveObserver(this); @@ -1050,14 +1073,14 @@ void BridgedNativeWidgetHostImpl::OnWindowWillClose() { SetParent(nullptr); } -void BridgedNativeWidgetHostImpl::OnWindowHasClosed() { +void NativeWidgetMacNSWindowHost::OnWindowHasClosed() { // OnWindowHasClosed will be called only after all child windows have had // OnWindowWillClose called on them. DCHECK(children_.empty()); native_widget_mac_->WindowDestroyed(); } -void BridgedNativeWidgetHostImpl::OnWindowKeyStatusChanged( +void NativeWidgetMacNSWindowHost::OnWindowKeyStatusChanged( bool is_key, bool is_content_first_responder, bool full_keyboard_access_enabled) { @@ -1082,7 +1105,7 @@ void BridgedNativeWidgetHostImpl::OnWindowKeyStatusChanged( } } -void BridgedNativeWidgetHostImpl::DoDialogButtonAction( +void NativeWidgetMacNSWindowHost::DoDialogButtonAction( ui::DialogButton button) { views::DialogDelegate* dialog = root_view_->GetWidget()->widget_delegate()->AsDialogDelegate(); @@ -1096,7 +1119,7 @@ void BridgedNativeWidgetHostImpl::DoDialogButtonAction( } } -bool BridgedNativeWidgetHostImpl::GetDialogButtonInfo( +bool NativeWidgetMacNSWindowHost::GetDialogButtonInfo( ui::DialogButton button, bool* button_exists, base::string16* button_label, @@ -1114,14 +1137,14 @@ bool BridgedNativeWidgetHostImpl::GetDialogButtonInfo( return true; } -bool BridgedNativeWidgetHostImpl::GetDoDialogButtonsExist(bool* buttons_exist) { +bool NativeWidgetMacNSWindowHost::GetDoDialogButtonsExist(bool* buttons_exist) { views::DialogDelegate* dialog = root_view_->GetWidget()->widget_delegate()->AsDialogDelegate(); *buttons_exist = dialog && dialog->GetDialogButtons(); return true; } -bool BridgedNativeWidgetHostImpl::GetShouldShowWindowTitle( +bool NativeWidgetMacNSWindowHost::GetShouldShowWindowTitle( bool* should_show_window_title) { *should_show_window_title = root_view_ @@ -1130,21 +1153,21 @@ bool BridgedNativeWidgetHostImpl::GetShouldShowWindowTitle( return true; } -bool BridgedNativeWidgetHostImpl::GetCanWindowBecomeKey( +bool NativeWidgetMacNSWindowHost::GetCanWindowBecomeKey( bool* can_window_become_key) { *can_window_become_key = root_view_ ? root_view_->GetWidget()->CanActivate() : false; return true; } -bool BridgedNativeWidgetHostImpl::GetAlwaysRenderWindowAsKey( +bool NativeWidgetMacNSWindowHost::GetAlwaysRenderWindowAsKey( bool* always_render_as_key) { *always_render_as_key = root_view_ ? root_view_->GetWidget()->ShouldPaintAsActive() : false; return true; } -bool BridgedNativeWidgetHostImpl::GetCanWindowClose(bool* can_window_close) { +bool NativeWidgetMacNSWindowHost::GetCanWindowClose(bool* can_window_close) { *can_window_close = true; views::NonClientView* non_client_view = root_view_ ? root_view_->GetWidget()->non_client_view() : nullptr; @@ -1153,7 +1176,7 @@ bool BridgedNativeWidgetHostImpl::GetCanWindowClose(bool* can_window_close) { return true; } -bool BridgedNativeWidgetHostImpl::GetWindowFrameTitlebarHeight( +bool NativeWidgetMacNSWindowHost::GetWindowFrameTitlebarHeight( bool* override_titlebar_height, float* titlebar_height) { native_widget_mac_->GetWindowFrameTitlebarHeight(override_titlebar_height, @@ -1161,11 +1184,11 @@ bool BridgedNativeWidgetHostImpl::GetWindowFrameTitlebarHeight( return true; } -void BridgedNativeWidgetHostImpl::OnFocusWindowToolbar() { +void NativeWidgetMacNSWindowHost::OnFocusWindowToolbar() { native_widget_mac_->OnFocusWindowToolbar(); } -void BridgedNativeWidgetHostImpl::SetRemoteAccessibilityTokens( +void NativeWidgetMacNSWindowHost::SetRemoteAccessibilityTokens( const std::vector<uint8_t>& window_token, const std::vector<uint8_t>& view_token) { remote_window_accessible_ = @@ -1177,7 +1200,7 @@ void BridgedNativeWidgetHostImpl::SetRemoteAccessibilityTokens( setTopLevelUIElement:remote_window_accessible_.get()]; } -bool BridgedNativeWidgetHostImpl::GetRootViewAccessibilityToken( +bool NativeWidgetMacNSWindowHost::GetRootViewAccessibilityToken( int64_t* pid, std::vector<uint8_t>* token) { *pid = getpid(); @@ -1186,7 +1209,7 @@ bool BridgedNativeWidgetHostImpl::GetRootViewAccessibilityToken( return true; } -bool BridgedNativeWidgetHostImpl::ValidateUserInterfaceItem( +bool NativeWidgetMacNSWindowHost::ValidateUserInterfaceItem( int32_t command, remote_cocoa::mojom::ValidateUserInterfaceItemResultPtr* out_result) { *out_result = remote_cocoa::mojom::ValidateUserInterfaceItemResult::New(); @@ -1194,7 +1217,7 @@ bool BridgedNativeWidgetHostImpl::ValidateUserInterfaceItem( return true; } -bool BridgedNativeWidgetHostImpl::ExecuteCommand( +bool NativeWidgetMacNSWindowHost::ExecuteCommand( int32_t command, WindowOpenDisposition window_open_disposition, bool is_before_first_responder, @@ -1204,7 +1227,7 @@ bool BridgedNativeWidgetHostImpl::ExecuteCommand( return true; } -bool BridgedNativeWidgetHostImpl::HandleAccelerator( +bool NativeWidgetMacNSWindowHost::HandleAccelerator( const ui::Accelerator& accelerator, bool require_priority_handler, bool* was_handled) { @@ -1220,17 +1243,17 @@ bool BridgedNativeWidgetHostImpl::HandleAccelerator( } //////////////////////////////////////////////////////////////////////////////// -// BridgedNativeWidgetHostImpl, -// remote_cocoa::mojom::BridgedNativeWidgetHost synchronous callbacks: +// NativeWidgetMacNSWindowHost, +// remote_cocoa::mojom::NativeWidgetNSWindowHost synchronous callbacks: -void BridgedNativeWidgetHostImpl::GetSheetOffsetY( +void NativeWidgetMacNSWindowHost::GetSheetOffsetY( GetSheetOffsetYCallback callback) { int32_t offset_y = 0; GetSheetOffsetY(&offset_y); std::move(callback).Run(offset_y); } -void BridgedNativeWidgetHostImpl::DispatchKeyEventRemote( +void NativeWidgetMacNSWindowHost::DispatchKeyEventRemote( std::unique_ptr<ui::Event> event, DispatchKeyEventRemoteCallback callback) { bool event_handled = false; @@ -1238,7 +1261,7 @@ void BridgedNativeWidgetHostImpl::DispatchKeyEventRemote( std::move(callback).Run(event_handled); } -void BridgedNativeWidgetHostImpl::DispatchKeyEventToMenuControllerRemote( +void NativeWidgetMacNSWindowHost::DispatchKeyEventToMenuControllerRemote( std::unique_ptr<ui::Event> event, DispatchKeyEventToMenuControllerRemoteCallback callback) { ui::KeyEvent* key_event = event->AsKeyEvent(); @@ -1246,14 +1269,14 @@ void BridgedNativeWidgetHostImpl::DispatchKeyEventToMenuControllerRemote( std::move(callback).Run(event_swallowed, key_event->handled()); } -void BridgedNativeWidgetHostImpl::GetHasMenuController( +void NativeWidgetMacNSWindowHost::GetHasMenuController( GetHasMenuControllerCallback callback) { bool has_menu_controller = false; GetHasMenuController(&has_menu_controller); std::move(callback).Run(has_menu_controller); } -void BridgedNativeWidgetHostImpl::GetIsDraggableBackgroundAt( +void NativeWidgetMacNSWindowHost::GetIsDraggableBackgroundAt( const gfx::Point& location_in_content, GetIsDraggableBackgroundAtCallback callback) { bool is_draggable_background = false; @@ -1261,7 +1284,7 @@ void BridgedNativeWidgetHostImpl::GetIsDraggableBackgroundAt( std::move(callback).Run(is_draggable_background); } -void BridgedNativeWidgetHostImpl::GetTooltipTextAt( +void NativeWidgetMacNSWindowHost::GetTooltipTextAt( const gfx::Point& location_in_content, GetTooltipTextAtCallback callback) { base::string16 new_tooltip_text; @@ -1269,21 +1292,21 @@ void BridgedNativeWidgetHostImpl::GetTooltipTextAt( std::move(callback).Run(new_tooltip_text); } -void BridgedNativeWidgetHostImpl::GetIsFocusedViewTextual( +void NativeWidgetMacNSWindowHost::GetIsFocusedViewTextual( GetIsFocusedViewTextualCallback callback) { bool is_textual = false; GetIsFocusedViewTextual(&is_textual); std::move(callback).Run(is_textual); } -void BridgedNativeWidgetHostImpl::GetWidgetIsModal( +void NativeWidgetMacNSWindowHost::GetWidgetIsModal( GetWidgetIsModalCallback callback) { bool widget_is_modal = false; GetWidgetIsModal(&widget_is_modal); std::move(callback).Run(widget_is_modal); } -void BridgedNativeWidgetHostImpl::GetDialogButtonInfo( +void NativeWidgetMacNSWindowHost::GetDialogButtonInfo( ui::DialogButton button, GetDialogButtonInfoCallback callback) { bool exists = false; @@ -1294,42 +1317,42 @@ void BridgedNativeWidgetHostImpl::GetDialogButtonInfo( std::move(callback).Run(exists, label, is_enabled, is_default); } -void BridgedNativeWidgetHostImpl::GetDoDialogButtonsExist( +void NativeWidgetMacNSWindowHost::GetDoDialogButtonsExist( GetDoDialogButtonsExistCallback callback) { bool buttons_exist = false; GetDoDialogButtonsExist(&buttons_exist); std::move(callback).Run(buttons_exist); } -void BridgedNativeWidgetHostImpl::GetShouldShowWindowTitle( +void NativeWidgetMacNSWindowHost::GetShouldShowWindowTitle( GetShouldShowWindowTitleCallback callback) { bool should_show_window_title = false; GetShouldShowWindowTitle(&should_show_window_title); std::move(callback).Run(should_show_window_title); } -void BridgedNativeWidgetHostImpl::GetCanWindowBecomeKey( +void NativeWidgetMacNSWindowHost::GetCanWindowBecomeKey( GetCanWindowBecomeKeyCallback callback) { bool can_window_become_key = false; GetCanWindowBecomeKey(&can_window_become_key); std::move(callback).Run(can_window_become_key); } -void BridgedNativeWidgetHostImpl::GetAlwaysRenderWindowAsKey( +void NativeWidgetMacNSWindowHost::GetAlwaysRenderWindowAsKey( GetAlwaysRenderWindowAsKeyCallback callback) { bool always_render_as_key = false; GetAlwaysRenderWindowAsKey(&always_render_as_key); std::move(callback).Run(always_render_as_key); } -void BridgedNativeWidgetHostImpl::GetCanWindowClose( +void NativeWidgetMacNSWindowHost::GetCanWindowClose( GetCanWindowCloseCallback callback) { bool can_window_close = false; GetCanWindowClose(&can_window_close); std::move(callback).Run(can_window_close); } -void BridgedNativeWidgetHostImpl::GetWindowFrameTitlebarHeight( +void NativeWidgetMacNSWindowHost::GetWindowFrameTitlebarHeight( GetWindowFrameTitlebarHeightCallback callback) { bool override_titlebar_height = false; float titlebar_height = 0; @@ -1337,7 +1360,7 @@ void BridgedNativeWidgetHostImpl::GetWindowFrameTitlebarHeight( std::move(callback).Run(override_titlebar_height, titlebar_height); } -void BridgedNativeWidgetHostImpl::GetRootViewAccessibilityToken( +void NativeWidgetMacNSWindowHost::GetRootViewAccessibilityToken( GetRootViewAccessibilityTokenCallback callback) { std::vector<uint8_t> token; int64_t pid; @@ -1345,7 +1368,7 @@ void BridgedNativeWidgetHostImpl::GetRootViewAccessibilityToken( std::move(callback).Run(pid, token); } -void BridgedNativeWidgetHostImpl::ValidateUserInterfaceItem( +void NativeWidgetMacNSWindowHost::ValidateUserInterfaceItem( int32_t command, ValidateUserInterfaceItemCallback callback) { remote_cocoa::mojom::ValidateUserInterfaceItemResultPtr result; @@ -1353,7 +1376,7 @@ void BridgedNativeWidgetHostImpl::ValidateUserInterfaceItem( std::move(callback).Run(std::move(result)); } -void BridgedNativeWidgetHostImpl::ExecuteCommand( +void NativeWidgetMacNSWindowHost::ExecuteCommand( int32_t command, WindowOpenDisposition window_open_disposition, bool is_before_first_responder, @@ -1364,7 +1387,7 @@ void BridgedNativeWidgetHostImpl::ExecuteCommand( std::move(callback).Run(was_executed); } -void BridgedNativeWidgetHostImpl::HandleAccelerator( +void NativeWidgetMacNSWindowHost::HandleAccelerator( const ui::Accelerator& accelerator, bool require_priority_handler, HandleAcceleratorCallback callback) { @@ -1374,21 +1397,21 @@ void BridgedNativeWidgetHostImpl::HandleAccelerator( } //////////////////////////////////////////////////////////////////////////////// -// BridgedNativeWidgetHostImpl, DialogObserver: +// NativeWidgetMacNSWindowHost, DialogObserver: -void BridgedNativeWidgetHostImpl::OnDialogChanged() { +void NativeWidgetMacNSWindowHost::OnDialogChanged() { // Note it's only necessary to clear the TouchBar. If the OS needs it again, // a new one will be created. - bridge()->ClearTouchBar(); + GetNSWindowMojo()->ClearTouchBar(); } //////////////////////////////////////////////////////////////////////////////// -// BridgedNativeWidgetHostImpl, FocusChangeListener: +// NativeWidgetMacNSWindowHost, FocusChangeListener: -void BridgedNativeWidgetHostImpl::OnWillChangeFocus(View* focused_before, +void NativeWidgetMacNSWindowHost::OnWillChangeFocus(View* focused_before, View* focused_now) {} -void BridgedNativeWidgetHostImpl::OnDidChangeFocus(View* focused_before, +void NativeWidgetMacNSWindowHost::OnDidChangeFocus(View* focused_before, View* focused_now) { ui::InputMethod* input_method = native_widget_mac_->GetWidget()->GetInputMethod(); @@ -1404,50 +1427,48 @@ void BridgedNativeWidgetHostImpl::OnDidChangeFocus(View* focused_before, } //////////////////////////////////////////////////////////////////////////////// -// BridgedNativeWidgetImpl, internal::InputMethodDelegate: +// NativeWidgetMacNSWindowHost, internal::InputMethodDelegate: -ui::EventDispatchDetails BridgedNativeWidgetHostImpl::DispatchKeyEventPostIME( - ui::KeyEvent* key, - DispatchKeyEventPostIMECallback callback) { +ui::EventDispatchDetails NativeWidgetMacNSWindowHost::DispatchKeyEventPostIME( + ui::KeyEvent* key) { DCHECK(focus_manager_); if (!focus_manager_->OnKeyEvent(*key)) key->StopPropagation(); else native_widget_mac_->GetWidget()->OnKeyEvent(key); - RunDispatchKeyEventPostIMECallback(key, std::move(callback)); return ui::EventDispatchDetails(); } //////////////////////////////////////////////////////////////////////////////// -// BridgedNativeWidgetHostImpl, AccessibilityFocusOverrider::Client: +// NativeWidgetMacNSWindowHost, AccessibilityFocusOverrider::Client: -id BridgedNativeWidgetHostImpl::GetAccessibilityFocusedUIElement() { +id NativeWidgetMacNSWindowHost::GetAccessibilityFocusedUIElement() { return [GetNativeViewAccessible() accessibilityFocusedUIElement]; } //////////////////////////////////////////////////////////////////////////////// -// BridgedNativeWidgetHostImpl, LayerDelegate: +// NativeWidgetMacNSWindowHost, LayerDelegate: -void BridgedNativeWidgetHostImpl::OnPaintLayer( +void NativeWidgetMacNSWindowHost::OnPaintLayer( const ui::PaintContext& context) { native_widget_mac_->GetWidget()->OnNativeWidgetPaint(context); } -void BridgedNativeWidgetHostImpl::OnDeviceScaleFactorChanged( +void NativeWidgetMacNSWindowHost::OnDeviceScaleFactorChanged( float old_device_scale_factor, float new_device_scale_factor) { native_widget_mac_->GetWidget()->DeviceScaleFactorChanged( old_device_scale_factor, new_device_scale_factor); } -void BridgedNativeWidgetHostImpl::UpdateVisualState() { +void NativeWidgetMacNSWindowHost::UpdateVisualState() { native_widget_mac_->GetWidget()->LayoutRootViewIfNecessary(); } //////////////////////////////////////////////////////////////////////////////// -// BridgedNativeWidgetHostImpl, AcceleratedWidgetMac: +// NativeWidgetMacNSWindowHost, AcceleratedWidgetMac: -void BridgedNativeWidgetHostImpl::AcceleratedWidgetCALayerParamsUpdated() { +void NativeWidgetMacNSWindowHost::AcceleratedWidgetCALayerParamsUpdated() { const gfx::CALayerParams* ca_layer_params = compositor_->widget()->GetCALayerParams(); if (ca_layer_params) { @@ -1457,7 +1478,7 @@ void BridgedNativeWidgetHostImpl::AcceleratedWidgetCALayerParamsUpdated() { // mach port has been specified (in practice, when software compositing is // enabled). // https://crbug.com/942213 - if (bridge_ptr_ && ca_layer_params->io_surface_mach_port) { + if (remote_ns_window_ptr_ && ca_layer_params->io_surface_mach_port) { gfx::CALayerParams updated_ca_layer_params = *ca_layer_params; if (!io_surface_to_remote_layer_interceptor_) { io_surface_to_remote_layer_interceptor_ = @@ -1465,9 +1486,9 @@ void BridgedNativeWidgetHostImpl::AcceleratedWidgetCALayerParamsUpdated() { } io_surface_to_remote_layer_interceptor_->UpdateCALayerParams( &updated_ca_layer_params); - bridge_ptr_->SetCALayerParams(updated_ca_layer_params); + remote_ns_window_ptr_->SetCALayerParams(updated_ca_layer_params); } else { - bridge()->SetCALayerParams(*ca_layer_params); + GetNSWindowMojo()->SetCALayerParams(*ca_layer_params); } } diff --git a/chromium/ui/views/cocoa/text_input_host.h b/chromium/ui/views/cocoa/text_input_host.h index 27284019fda..f53ba460a26 100644 --- a/chromium/ui/views/cocoa/text_input_host.h +++ b/chromium/ui/views/cocoa/text_input_host.h @@ -16,11 +16,11 @@ class TextInputClient; namespace views { -class BridgedNativeWidgetHostImpl; +class NativeWidgetMacNSWindowHost; class VIEWS_EXPORT TextInputHost : public remote_cocoa::mojom::TextInputHost { public: - explicit TextInputHost(BridgedNativeWidgetHostImpl* host_impl); + explicit TextInputHost(NativeWidgetMacNSWindowHost* host_impl); ~TextInputHost() override; void BindRequest(remote_cocoa::mojom::TextInputHostAssociatedRequest request); @@ -78,7 +78,7 @@ class VIEWS_EXPORT TextInputHost : public remote_cocoa::mojom::TextInputHost { // IME requests using the old |text_input_client_|. ui::TextInputClient* pending_text_input_client_ = nullptr; - BridgedNativeWidgetHostImpl* const host_impl_; + NativeWidgetMacNSWindowHost* const host_impl_; mojo::AssociatedBinding<remote_cocoa::mojom::TextInputHost> mojo_binding_; DISALLOW_COPY_AND_ASSIGN(TextInputHost); diff --git a/chromium/ui/views/cocoa/text_input_host.mm b/chromium/ui/views/cocoa/text_input_host.mm index a61021991f7..2af42b55ba7 100644 --- a/chromium/ui/views/cocoa/text_input_host.mm +++ b/chromium/ui/views/cocoa/text_input_host.mm @@ -4,11 +4,11 @@ #include "ui/views/cocoa/text_input_host.h" -#include "components/remote_cocoa/app_shim/bridged_native_widget_impl.h" +#include "components/remote_cocoa/app_shim/native_widget_ns_window_bridge.h" #include "ui/accelerated_widget_mac/window_resize_helper_mac.h" #include "ui/base/ime/text_input_client.h" #include "ui/events/keycodes/dom/dom_code.h" -#include "ui/views/cocoa/bridged_native_widget_host_impl.h" +#include "ui/views/cocoa/native_widget_mac_ns_window_host.h" namespace { @@ -131,7 +131,7 @@ namespace views { //////////////////////////////////////////////////////////////////////////////// // TextInputHost, public: -TextInputHost::TextInputHost(BridgedNativeWidgetHostImpl* host_impl) +TextInputHost::TextInputHost(NativeWidgetMacNSWindowHost* host_impl) : host_impl_(host_impl), mojo_binding_(this) {} TextInputHost::~TextInputHost() = default; @@ -175,8 +175,8 @@ void TextInputHost::SetTextInputClient( text_input_client_ = new_text_input_client; pending_text_input_client_ = new_text_input_client; - if (host_impl_->bridge_impl_ && - host_impl_->bridge_impl_->NeedsUpdateWindows()) { + if (host_impl_->in_process_ns_window_bridge_ && + host_impl_->in_process_ns_window_bridge_->NeedsUpdateWindows()) { text_input_client_ = old_text_input_client; [NSApp updateWindows]; // Note: |pending_text_input_client_| (and therefore +[NSTextInputContext diff --git a/chromium/ui/views/cocoa/tooltip_manager_mac.h b/chromium/ui/views/cocoa/tooltip_manager_mac.h index c1d1c1bf04d..e8a0eb14515 100644 --- a/chromium/ui/views/cocoa/tooltip_manager_mac.h +++ b/chromium/ui/views/cocoa/tooltip_manager_mac.h @@ -10,16 +10,16 @@ namespace remote_cocoa { namespace mojom { -class BridgedNativeWidget; +class NativeWidgetNSWindow; } // namespace mojom } // namespace remote_cocoa namespace views { -// Manages native Cocoa tooltips for the given BridgedNativeWidgetHostImpl. +// Manages native Cocoa tooltips for the given NativeWidgetNSWindowHostImpl. class TooltipManagerMac : public TooltipManager { public: - explicit TooltipManagerMac(remote_cocoa::mojom::BridgedNativeWidget* bridge); + explicit TooltipManagerMac(remote_cocoa::mojom::NativeWidgetNSWindow* bridge); ~TooltipManagerMac() override; // TooltipManager: @@ -29,7 +29,7 @@ class TooltipManagerMac : public TooltipManager { void TooltipTextChanged(View* view) override; private: - remote_cocoa::mojom::BridgedNativeWidget* + remote_cocoa::mojom::NativeWidgetNSWindow* bridge_; // Weak. Owned by the owner of this. DISALLOW_COPY_AND_ASSIGN(TooltipManagerMac); diff --git a/chromium/ui/views/cocoa/tooltip_manager_mac.mm b/chromium/ui/views/cocoa/tooltip_manager_mac.mm index b625af9f60d..da34d6035bb 100644 --- a/chromium/ui/views/cocoa/tooltip_manager_mac.mm +++ b/chromium/ui/views/cocoa/tooltip_manager_mac.mm @@ -6,7 +6,7 @@ #include "base/no_destructor.h" #import "components/remote_cocoa/app_shim/bridged_content_view.h" -#import "components/remote_cocoa/app_shim/bridged_native_widget_impl.h" +#import "components/remote_cocoa/app_shim/native_widget_ns_window_bridge.h" #include "ui/base/cocoa/cocoa_base_utils.h" #include "ui/gfx/font_list.h" #import "ui/gfx/mac/coordinate_conversion.h" @@ -21,7 +21,7 @@ const int kTooltipMaxWidthPixels = 250; namespace views { TooltipManagerMac::TooltipManagerMac( - remote_cocoa::mojom::BridgedNativeWidget* bridge) + remote_cocoa::mojom::NativeWidgetNSWindow* bridge) : bridge_(bridge) {} TooltipManagerMac::~TooltipManagerMac() { diff --git a/chromium/ui/views/color_chooser/color_chooser_view.cc b/chromium/ui/views/color_chooser/color_chooser_view.cc index 252fb7c73e5..3427ed0d5d6 100644 --- a/chromium/ui/views/color_chooser/color_chooser_view.cc +++ b/chromium/ui/views/color_chooser/color_chooser_view.cc @@ -4,6 +4,9 @@ #include "ui/views/color_chooser/color_chooser_view.h" +#include <memory> +#include <utility> + #include <stdint.h> #include "base/logging.h" @@ -366,21 +369,21 @@ ColorChooserView::ColorChooserView(ColorChooserListener* listener, DCHECK(listener_); SetBackground(CreateSolidBackground(SK_ColorLTGRAY)); - SetLayoutManager(std::make_unique<BoxLayout>( - BoxLayout::kVertical, gfx::Insets(kMarginWidth), kMarginWidth)); + SetLayoutManager( + std::make_unique<BoxLayout>(BoxLayout::Orientation::kVertical, + gfx::Insets(kMarginWidth), kMarginWidth)); - View* container = new View(); + auto container = std::make_unique<View>(); container->SetLayoutManager(std::make_unique<BoxLayout>( - BoxLayout::kHorizontal, gfx::Insets(), kMarginWidth)); - saturation_value_ = new SaturationValueView(this); - container->AddChildView(saturation_value_); - hue_ = new HueView(this); - container->AddChildView(hue_); - AddChildView(container); - - View* container2 = new View(); - GridLayout* layout = container2->SetLayoutManager( - std::make_unique<views::GridLayout>(container2)); + BoxLayout::Orientation::kHorizontal, gfx::Insets(), kMarginWidth)); + saturation_value_ = + container->AddChildView(std::make_unique<SaturationValueView>(this)); + hue_ = container->AddChildView(std::make_unique<HueView>(this)); + AddChildView(std::move(container)); + + auto container2 = std::make_unique<View>(); + GridLayout* layout = + container2->SetLayoutManager(std::make_unique<views::GridLayout>()); ColumnSet* columns = layout->AddColumnSet(0); columns->AddColumn( GridLayout::LEADING, GridLayout::FILL, 0, GridLayout::USE_PREF, 0, 0); @@ -388,15 +391,15 @@ ColorChooserView::ColorChooserView(ColorChooserListener* listener, columns->AddColumn( GridLayout::FILL, GridLayout::FILL, 1, GridLayout::USE_PREF, 0, 0); layout->StartRow(0, 0); - textfield_ = new Textfield(); - textfield_->set_controller(this); - textfield_->SetDefaultWidthInChars(kTextfieldLengthInChars); - textfield_->SetAccessibleName( + auto textfield = std::make_unique<Textfield>(); + textfield->set_controller(this); + textfield->SetDefaultWidthInChars(kTextfieldLengthInChars); + textfield->SetAccessibleName( l10n_util::GetStringUTF16(IDS_APP_ACCNAME_COLOR_CHOOSER_HEX_INPUT)); - layout->AddView(textfield_); - selected_color_patch_ = new SelectedColorPatchView(); - layout->AddView(selected_color_patch_); - AddChildView(container2); + textfield_ = layout->AddView(std::move(textfield)); + selected_color_patch_ = + layout->AddView(std::make_unique<SelectedColorPatchView>()); + AddChildView(std::move(container2)); OnColorChanged(initial_color); } diff --git a/chromium/ui/views/controls/animated_image_view.cc b/chromium/ui/views/controls/animated_image_view.cc index bb379bc3d7e..aca500c6792 100644 --- a/chromium/ui/views/controls/animated_image_view.cc +++ b/chromium/ui/views/controls/animated_image_view.cc @@ -90,9 +90,6 @@ void AnimatedImageView::OnPaint(gfx::Canvas* canvas) { canvas->Restore(); } -const char* AnimatedImageView::GetClassName() const { - return "AnimatedImageView"; -} void AnimatedImageView::NativeViewHierarchyChanged() { ui::Compositor* compositor = GetWidget()->GetCompositor(); @@ -142,4 +139,8 @@ void AnimatedImageView::ClearCurrentCompositor() { } } +BEGIN_METADATA(AnimatedImageView) +METADATA_PARENT_CLASS(ImageViewBase) +END_METADATA() + } // namespace views diff --git a/chromium/ui/views/controls/animated_image_view.h b/chromium/ui/views/controls/animated_image_view.h index f3868c28a97..58ea3ee2b55 100644 --- a/chromium/ui/views/controls/animated_image_view.h +++ b/chromium/ui/views/controls/animated_image_view.h @@ -34,6 +34,8 @@ namespace views { class VIEWS_EXPORT AnimatedImageView : public ImageViewBase, public ui::CompositorAnimationObserver { public: + METADATA_HEADER(AnimatedImageView); + enum class State { kPlaying, // The animation is currently playing. kStopped // The animation is stopped and paint will raster the first @@ -60,7 +62,6 @@ class VIEWS_EXPORT AnimatedImageView : public ImageViewBase, // Overridden from View: void OnPaint(gfx::Canvas* canvas) override; - const char* GetClassName() const override; void NativeViewHierarchyChanged() override; void RemovedFromWidget() override; diff --git a/chromium/ui/views/controls/button/button.cc b/chromium/ui/views/controls/button/button.cc index cd9b168c18c..7c7562e22a1 100644 --- a/chromium/ui/views/controls/button/button.cc +++ b/chromium/ui/views/controls/button/button.cc @@ -70,10 +70,6 @@ void Button::WidgetObserverButtonBridge::OnWidgetDestroying(Widget* widget) { } //////////////////////////////////////////////////////////////////////////////// -// Button, static public: - -// static -const char Button::kViewClassName[] = "Button"; // static const Button* Button::AsButton(const views::View* view) { @@ -227,10 +223,6 @@ void Button::RemoveButtonObserver(ButtonObserver* observer) { //////////////////////////////////////////////////////////////////////////////// // Button, View overrides: -const char* Button::GetClassName() const { - return kViewClassName; -} - bool Button::OnMousePressed(const ui::MouseEvent& event) { return button_controller_->OnMousePressed(event); } @@ -482,7 +474,9 @@ Button::CreateButtonControllerDelegate() { } Button::Button(ButtonListener* listener) - : listener_(listener), ink_drop_base_color_(gfx::kPlaceholderColor) { + : AnimationDelegateViews(this), + listener_(listener), + ink_drop_base_color_(gfx::kPlaceholderColor) { SetFocusBehavior(FocusBehavior::ACCESSIBLE_ONLY); SetProperty(kIsButtonProperty, true); hover_animation_.SetSlideDuration(kHoverFadeDurationMs); @@ -599,4 +593,8 @@ void Button::WidgetActivationChanged(Widget* widget, bool active) { StateChanged(state()); } +BEGIN_METADATA(Button) +METADATA_PARENT_CLASS(InkDropHostView) +END_METADATA() + } // namespace views diff --git a/chromium/ui/views/controls/button/button.h b/chromium/ui/views/controls/button/button.h index c923869f45a..cc11bbff633 100644 --- a/chromium/ui/views/controls/button/button.h +++ b/chromium/ui/views/controls/button/button.h @@ -11,9 +11,9 @@ #include "base/macros.h" #include "build/build_config.h" #include "ui/events/event_constants.h" -#include "ui/gfx/animation/animation_delegate.h" #include "ui/gfx/animation/throb_animation.h" #include "ui/native_theme/native_theme.h" +#include "ui/views/animation/animation_delegate_views.h" #include "ui/views/animation/ink_drop_host_view.h" #include "ui/views/animation/ink_drop_state.h" #include "ui/views/controls/button/button_controller_delegate.h" @@ -45,8 +45,10 @@ class VIEWS_EXPORT ButtonListener { // not be part of the focus chain, unless in accessibility mode (see // SetFocusForPlatform()). class VIEWS_EXPORT Button : public InkDropHostView, - public gfx::AnimationDelegate { + public AnimationDelegateViews { public: + METADATA_HEADER(Button); + ~Button() override; // Button states for various button sub-types. @@ -72,9 +74,6 @@ class VIEWS_EXPORT Button : public InkDropHostView, CLICK_NONE, }; - // The menu button's class name. - static const char kViewClassName[]; - static const Button* AsButton(const View* view); static Button* AsButton(View* view); @@ -165,7 +164,6 @@ class VIEWS_EXPORT Button : public InkDropHostView, void RemoveButtonObserver(ButtonObserver* observer); // Overridden from View: - const char* GetClassName() const override; bool OnMousePressed(const ui::MouseEvent& event) override; bool OnMouseDragged(const ui::MouseEvent& event) override; void OnMouseReleased(const ui::MouseEvent& event) override; @@ -198,7 +196,7 @@ class VIEWS_EXPORT Button : public InkDropHostView, std::unique_ptr<InkDrop> CreateInkDrop() override; SkColor GetInkDropBaseColor() const override; - // Overridden from gfx::AnimationDelegate: + // Overridden from views::AnimationDelegateViews: void AnimationProgressed(const gfx::Animation* animation) override; // Returns the click action for the given key event. diff --git a/chromium/ui/views/controls/button/checkbox.cc b/chromium/ui/views/controls/button/checkbox.cc index f0f88894609..affe2c7e257 100644 --- a/chromium/ui/views/controls/button/checkbox.cc +++ b/chromium/ui/views/controls/button/checkbox.cc @@ -31,9 +31,6 @@ namespace views { -// static -const char Checkbox::kViewClassName[] = "Checkbox"; - Checkbox::Checkbox(const base::string16& label, ButtonListener* listener) : LabelButton(listener, label), checked_(false), label_ax_id_(0) { SetHorizontalAlignment(gfx::ALIGN_LEFT); @@ -77,7 +74,7 @@ void Checkbox::SetMultiLine(bool multi_line) { } bool Checkbox::GetMultiLine() const { - return label()->multi_line(); + return label()->GetMultiLine(); } void Checkbox::SetAssociatedLabel(View* labelling_view) { @@ -92,10 +89,6 @@ void Checkbox::SetAssociatedLabel(View* labelling_view) { node_data.GetString16Attribute(ax::mojom::StringAttribute::kName)); } -const char* Checkbox::GetClassName() const { - return kViewClassName; -} - void Checkbox::GetAccessibleNodeData(ui::AXNodeData* node_data) { LabelButton::GetAccessibleNodeData(node_data); node_data->role = ax::mojom::Role::kCheckBox; diff --git a/chromium/ui/views/controls/button/checkbox.h b/chromium/ui/views/controls/button/checkbox.h index 68ea1f7b2ab..47b500e0a3c 100644 --- a/chromium/ui/views/controls/button/checkbox.h +++ b/chromium/ui/views/controls/button/checkbox.h @@ -26,8 +26,6 @@ class VIEWS_EXPORT Checkbox : public LabelButton { public: METADATA_HEADER(Checkbox); - static const char kViewClassName[]; - // |force_md| forces MD even when --secondary-ui-md flag is not set. explicit Checkbox(const base::string16& label, ButtonListener* listener = nullptr); @@ -51,7 +49,6 @@ class VIEWS_EXPORT Checkbox : public LabelButton { protected: // LabelButton: - const char* GetClassName() const override; void OnThemeChanged() override; std::unique_ptr<InkDrop> CreateInkDrop() override; std::unique_ptr<InkDropRipple> CreateInkDropRipple() const override; diff --git a/chromium/ui/views/controls/button/image_button.cc b/chromium/ui/views/controls/button/image_button.cc index ea134d4e078..e7717ed341f 100644 --- a/chromium/ui/views/controls/button/image_button.cc +++ b/chromium/ui/views/controls/button/image_button.cc @@ -24,8 +24,6 @@ namespace views { static constexpr int kDefaultWidth = 16; static constexpr int kDefaultHeight = 14; -const char ImageButton::kViewClassName[] = "ImageButton"; - //////////////////////////////////////////////////////////////////////////////// // ImageButton, public: @@ -55,8 +53,9 @@ void ImageButton::SetImage(ButtonState for_state, const gfx::ImageSkia& image) { if (old_preferred_size != GetPreferredSize()) PreferredSizeChanged(); - if (state() == for_state) - SchedulePaint(); + // Even if |for_state| isn't the current state this image could be painted; + // see |GetImageToPaint()|. So, always repaint. + SchedulePaint(); } void ImageButton::SetBackgroundImage(SkColor color, @@ -94,13 +93,6 @@ void ImageButton::SetImageVerticalAlignment(VerticalAlignment v_alignment) { OnPropertyChanged(&v_alignment_, kPropertyEffectsPaint); } -void ImageButton::SetBackgroundImageAlignment(HorizontalAlignment h_align, - VerticalAlignment v_align) { - h_background_alignment_ = h_align; - v_background_alignment_ = v_align; - SchedulePaint(); -} - gfx::Size ImageButton::GetMinimumImageSize() const { return minimum_image_size_; } @@ -115,10 +107,6 @@ void ImageButton::SetMinimumImageSize(const gfx::Size& size) { //////////////////////////////////////////////////////////////////////////////// // ImageButton, View overrides: -const char* ImageButton::GetClassName() const { - return kViewClassName; -} - gfx::Size ImageButton::CalculatePreferredSize() const { gfx::Size size(kDefaultWidth, kDefaultHeight); if (!images_[STATE_NORMAL].isNull()) { @@ -160,20 +148,14 @@ void ImageButton::PaintButtonContents(gfx::Canvas* canvas) { } if (!background_image_.isNull()) { - // If the background image alignment was not set, use the image - // alignment. - HorizontalAlignment h_alignment = - h_background_alignment_.value_or(GetImageHorizontalAlignment()); - VerticalAlignment v_alignment = - v_background_alignment_.value_or(GetImageVerticalAlignment()); - gfx::Point background_position = ComputeImagePaintPosition( - background_image_, h_alignment, v_alignment); + // The background image alignment is the same as for the image. + gfx::Point background_position = + ComputeImagePaintPosition(background_image_); canvas->DrawImageInt(background_image_, background_position.x(), background_position.y()); } - gfx::Point position = ComputeImagePaintPosition( - img, GetImageHorizontalAlignment(), GetImageVerticalAlignment()); + gfx::Point position = ComputeImagePaintPosition(img); canvas->DrawImageInt(img, position.x(), position.y()); } } @@ -199,12 +181,9 @@ gfx::ImageSkia ImageButton::GetImageToPaint() { // ImageButton, private: const gfx::Point ImageButton::ComputeImagePaintPosition( - const gfx::ImageSkia& image, - HorizontalAlignment h_alignment, - VerticalAlignment v_alignment) { - int x = 0, y = 0; - gfx::Rect rect = GetContentsBounds(); - + const gfx::ImageSkia& image) const { + HorizontalAlignment h_alignment = GetImageHorizontalAlignment(); + VerticalAlignment v_alignment = GetImageVerticalAlignment(); if (draw_image_mirrored_) { if (h_alignment == ALIGN_RIGHT) h_alignment = ALIGN_LEFT; @@ -212,20 +191,21 @@ const gfx::Point ImageButton::ComputeImagePaintPosition( h_alignment = ALIGN_RIGHT; } + const gfx::Rect rect = GetContentsBounds(); + + int x = 0; if (h_alignment == ALIGN_CENTER) x = (rect.width() - image.width()) / 2; else if (h_alignment == ALIGN_RIGHT) x = rect.width() - image.width(); + int y = 0; if (v_alignment == ALIGN_MIDDLE) y = (rect.height() - image.height()) / 2; else if (v_alignment == ALIGN_BOTTOM) y = rect.height() - image.height(); - x += rect.x(); - y += rect.y(); - - return gfx::Point(x, y); + return rect.origin() + gfx::Vector2d(x, y); } //////////////////////////////////////////////////////////////////////////////// diff --git a/chromium/ui/views/controls/button/image_button.h b/chromium/ui/views/controls/button/image_button.h index 833234d5ce9..bcad731513c 100644 --- a/chromium/ui/views/controls/button/image_button.h +++ b/chromium/ui/views/controls/button/image_button.h @@ -23,8 +23,6 @@ class VIEWS_EXPORT ImageButton : public Button { public: METADATA_HEADER(ImageButton); - static const char kViewClassName[]; - // An enum describing the horizontal alignment of images on Buttons. enum HorizontalAlignment { ALIGN_LEFT = 0, @@ -53,7 +51,8 @@ class VIEWS_EXPORT ImageButton : public Button { // consolidated. virtual void SetImage(ButtonState state, const gfx::ImageSkia& image); - // Set the background details. + // Set the background details. The background image uses the same alignment + // as the image. void SetBackgroundImage(SkColor color, const gfx::ImageSkia* image, const gfx::ImageSkia* mask); @@ -64,10 +63,6 @@ class VIEWS_EXPORT ImageButton : public Button { void SetImageHorizontalAlignment(HorizontalAlignment h_alignment); void SetImageVerticalAlignment(VerticalAlignment v_alignment); - // Sets how the background is laid out within the button's bounds. - void SetBackgroundImageAlignment(HorizontalAlignment h_align, - VerticalAlignment v_align); - // The minimum size of the contents (not including the border). The contents // will be at least this size, but may be larger if the image itself is // larger. @@ -80,7 +75,6 @@ class VIEWS_EXPORT ImageButton : public Button { } // Overridden from View: - const char* GetClassName() const override; gfx::Size CalculatePreferredSize() const override; views::PaintInfo::ScaleType GetPaintScaleType() const override; @@ -110,20 +104,13 @@ class VIEWS_EXPORT ImageButton : public Button { FRIEND_TEST_ALL_PREFIXES(ImageButtonFactoryTest, CreateVectorImageButton); // Returns the correct position of the image for painting. - const gfx::Point ComputeImagePaintPosition(const gfx::ImageSkia& image, - HorizontalAlignment h_alignment, - VerticalAlignment v_alignment); + const gfx::Point ComputeImagePaintPosition(const gfx::ImageSkia& image) const; // Image alignment. HorizontalAlignment h_alignment_ = ALIGN_LEFT; VerticalAlignment v_alignment_ = ALIGN_TOP; gfx::Size minimum_image_size_; - // Background alignment. If these are not set, the background image uses the - // image alignment. - base::Optional<HorizontalAlignment> h_background_alignment_; - base::Optional<VerticalAlignment> v_background_alignment_; - // Whether we draw our resources horizontally flipped. This can happen in the // linux titlebar, where image resources were designed to be flipped so a // small curved corner in the close button designed to fit into the frame diff --git a/chromium/ui/views/controls/button/image_button_unittest.cc b/chromium/ui/views/controls/button/image_button_unittest.cc index ad2be97e889..ec76c1846f1 100644 --- a/chromium/ui/views/controls/button/image_button_unittest.cc +++ b/chromium/ui/views/controls/button/image_button_unittest.cc @@ -39,13 +39,6 @@ class Parent : public views::View { namespace views { -namespace { -const ImageButton::HorizontalAlignment kDefaultHorizontalAlignment = - ImageButton::ALIGN_LEFT; -const ImageButton::VerticalAlignment kDefaultVerticalAlignment = - ImageButton::ALIGN_TOP; -} // namespace - using ImageButtonTest = ViewsTestBase; TEST_F(ImageButtonTest, Basics) { @@ -128,30 +121,20 @@ TEST_F(ImageButtonTest, ImagePositionWithBorder) { button.SetImage(Button::STATE_NORMAL, &image); // The image should be painted at the top-left corner. - EXPECT_EQ(gfx::Point(), - button.ComputeImagePaintPosition(image, kDefaultHorizontalAlignment, - kDefaultVerticalAlignment)); + EXPECT_EQ(gfx::Point(), button.ComputeImagePaintPosition(image)); button.SetBorder(views::CreateEmptyBorder(10, 5, 0, 0)); - EXPECT_EQ(gfx::Point(5, 10), - button.ComputeImagePaintPosition(image, kDefaultHorizontalAlignment, - kDefaultVerticalAlignment)); + EXPECT_EQ(gfx::Point(5, 10), button.ComputeImagePaintPosition(image)); button.SetBorder(NullBorder()); button.SetBounds(0, 0, 50, 50); - EXPECT_EQ(gfx::Point(), - button.ComputeImagePaintPosition(image, kDefaultHorizontalAlignment, - kDefaultVerticalAlignment)); + EXPECT_EQ(gfx::Point(), button.ComputeImagePaintPosition(image)); button.SetImageHorizontalAlignment(ImageButton::ALIGN_CENTER); button.SetImageVerticalAlignment(ImageButton::ALIGN_MIDDLE); - EXPECT_EQ(gfx::Point(15, 10), - button.ComputeImagePaintPosition(image, ImageButton::ALIGN_CENTER, - ImageButton::ALIGN_MIDDLE)); + EXPECT_EQ(gfx::Point(15, 10), button.ComputeImagePaintPosition(image)); button.SetBorder(views::CreateEmptyBorder(10, 10, 0, 0)); - EXPECT_EQ(gfx::Point(20, 15), - button.ComputeImagePaintPosition(image, ImageButton::ALIGN_CENTER, - ImageButton::ALIGN_MIDDLE)); + EXPECT_EQ(gfx::Point(20, 15), button.ComputeImagePaintPosition(image)); // The entire button's size should take the border into account. EXPECT_EQ(gfx::Size(30, 40), button.GetPreferredSize()); @@ -171,9 +154,7 @@ TEST_F(ImageButtonTest, LeftAlignedMirrored) { // Because the coordinates are flipped, we should expect this to draw as if // it were ALIGN_RIGHT. - EXPECT_EQ(gfx::Point(30, 0), - button.ComputeImagePaintPosition(image, ImageButton::ALIGN_LEFT, - ImageButton::ALIGN_BOTTOM)); + EXPECT_EQ(gfx::Point(30, 0), button.ComputeImagePaintPosition(image)); } TEST_F(ImageButtonTest, RightAlignedMirrored) { @@ -187,9 +168,7 @@ TEST_F(ImageButtonTest, RightAlignedMirrored) { // Because the coordinates are flipped, we should expect this to draw as if // it were ALIGN_LEFT. - EXPECT_EQ(gfx::Point(0, 0), - button.ComputeImagePaintPosition(image, ImageButton::ALIGN_RIGHT, - ImageButton::ALIGN_BOTTOM)); + EXPECT_EQ(gfx::Point(0, 0), button.ComputeImagePaintPosition(image)); } TEST_F(ImageButtonTest, PreferredSizeInvalidation) { diff --git a/chromium/ui/views/controls/button/label_button.cc b/chromium/ui/views/controls/button/label_button.cc index 90e6f9e1b00..214b1a74c5b 100644 --- a/chromium/ui/views/controls/button/label_button.cc +++ b/chromium/ui/views/controls/button/label_button.cc @@ -29,9 +29,6 @@ namespace views { -// static -const char LabelButton::kViewClassName[] = "LabelButton"; - LabelButton::LabelButton(ButtonListener* listener, const base::string16& text, int button_context) @@ -70,7 +67,7 @@ void LabelButton::SetImage(ButtonState for_state, const gfx::ImageSkia& image) { } base::string16 LabelButton::GetText() const { - return label_->text(); + return label_->GetText(); } void LabelButton::SetText(const base::string16& text) { @@ -259,7 +256,7 @@ void LabelButton::Layout() { label_area.height()); gfx::Point image_origin(child_area.origin()); - if (label_->multi_line()) { + if (label_->GetMultiLine()) { // Right now this code currently only works for CheckBox and RadioButton // descendants that have multi-line enabled for their label. image_origin.Offset( @@ -295,10 +292,6 @@ void LabelButton::Layout() { Button::Layout(); } -const char* LabelButton::GetClassName() const { - return kViewClassName; -} - void LabelButton::EnableCanvasFlippingForRTLUI(bool flip) { Button::EnableCanvasFlippingForRTLUI(flip); image_->EnableCanvasFlippingForRTLUI(flip); @@ -389,7 +382,7 @@ void LabelButton::GetExtraParams(ui::NativeTheme::ExtraParams* params) const { params->button.is_focused = HasFocus() && IsAccessibilityFocusable(); params->button.has_border = false; params->button.classic_state = 0; - params->button.background_color = label_->background_color(); + params->button.background_color = label_->GetBackgroundColor(); } void LabelButton::ResetColorsFromNativeTheme() { @@ -510,7 +503,7 @@ gfx::Size LabelButton::GetUnclampedSizeWithoutLabel() const { void LabelButton::ResetLabelEnabledColor() { const SkColor color = button_state_colors_[state()]; - if (state() != STATE_DISABLED && label_->enabled_color() != color) + if (state() != STATE_DISABLED && label_->GetEnabledColor() != color) label_->SetEnabledColor(color); } diff --git a/chromium/ui/views/controls/button/label_button.h b/chromium/ui/views/controls/button/label_button.h index 93538c101aa..c183274ec44 100644 --- a/chromium/ui/views/controls/button/label_button.h +++ b/chromium/ui/views/controls/button/label_button.h @@ -31,8 +31,6 @@ class VIEWS_EXPORT LabelButton : public Button, public NativeThemeDelegate { public: METADATA_HEADER(LabelButton); - static const char kViewClassName[]; - // Creates a LabelButton with ButtonPressed() events sent to |listener| and // label |text|. |button_context| is a value from views::style::TextContext // and determines the appearance of |text|. @@ -93,7 +91,6 @@ class VIEWS_EXPORT LabelButton : public Button, public NativeThemeDelegate { gfx::Size CalculatePreferredSize() const override; int GetHeightForWidth(int w) const override; void Layout() override; - const char* GetClassName() const override; void EnableCanvasFlippingForRTLUI(bool flip) override; void GetAccessibleNodeData(ui::AXNodeData* node_data) override; void AddLayerBeneathView(ui::Layer* new_layer) override; diff --git a/chromium/ui/views/controls/button/label_button_label.cc b/chromium/ui/views/controls/button/label_button_label.cc index b2eff942a40..81c7c3dc00c 100644 --- a/chromium/ui/views/controls/button/label_button_label.cc +++ b/chromium/ui/views/controls/button/label_button_label.cc @@ -40,7 +40,7 @@ void LabelButtonLabel::SetColorForEnableState() { : requested_disabled_color_); } else { int style = GetEnabled() ? style::STYLE_PRIMARY : style::STYLE_DISABLED; - Label::SetEnabledColor(style::GetColor(*this, text_context(), style)); + Label::SetEnabledColor(style::GetColor(*this, GetTextContext(), style)); } } diff --git a/chromium/ui/views/controls/button/label_button_label_unittest.cc b/chromium/ui/views/controls/button/label_button_label_unittest.cc index 1f5cd70ee28..a1254e2cc9f 100644 --- a/chromium/ui/views/controls/button/label_button_label_unittest.cc +++ b/chromium/ui/views/controls/button/label_button_label_unittest.cc @@ -43,7 +43,7 @@ class TestLabel : public LabelButtonLabel { // LabelButtonLabel: void SchedulePaintInRect(const gfx::Rect& r) override { LabelButtonLabel::SchedulePaintInRect(r); - *last_color_ = enabled_color(); + *last_color_ = GetEnabledColor(); } private: diff --git a/chromium/ui/views/controls/button/label_button_unittest.cc b/chromium/ui/views/controls/button/label_button_unittest.cc index 483d3fa33d7..c55ee6e146f 100644 --- a/chromium/ui/views/controls/button/label_button_unittest.cc +++ b/chromium/ui/views/controls/button/label_button_unittest.cc @@ -481,24 +481,24 @@ TEST_F(LabelButtonTest, ChangeLabelImageSpacing) { TEST_F(LabelButtonTest, HighlightedButtonStyle) { // The NativeTheme might not provide SK_ColorBLACK, but it should be the same // for normal and pressed states. - EXPECT_EQ(themed_normal_text_color_, button_->label()->enabled_color()); + EXPECT_EQ(themed_normal_text_color_, button_->label()->GetEnabledColor()); button_->SetState(Button::STATE_PRESSED); - EXPECT_EQ(themed_normal_text_color_, button_->label()->enabled_color()); + EXPECT_EQ(themed_normal_text_color_, button_->label()->GetEnabledColor()); } // Ensure the label gets the correct enabled color after // LabelButton::ResetColorsFromNativeTheme() is invoked. TEST_F(LabelButtonTest, ResetColorsFromNativeTheme) { ASSERT_FALSE(color_utils::IsInvertedColorScheme()); - ASSERT_NE(button_->label()->background_color(), SK_ColorBLACK); - EXPECT_EQ(themed_normal_text_color_, button_->label()->enabled_color()); + ASSERT_NE(button_->label()->GetBackgroundColor(), SK_ColorBLACK); + EXPECT_EQ(themed_normal_text_color_, button_->label()->GetEnabledColor()); button_->label()->SetBackgroundColor(SK_ColorBLACK); button_->label()->SetAutoColorReadabilityEnabled(true); - EXPECT_NE(themed_normal_text_color_, button_->label()->enabled_color()); + EXPECT_NE(themed_normal_text_color_, button_->label()->GetEnabledColor()); button_->ResetColorsFromNativeTheme(); - EXPECT_EQ(themed_normal_text_color_, button_->label()->enabled_color()); + EXPECT_EQ(themed_normal_text_color_, button_->label()->GetEnabledColor()); } // Test fixture for a LabelButton that has an ink drop configured. diff --git a/chromium/ui/views/controls/button/md_text_button.cc b/chromium/ui/views/controls/button/md_text_button.cc index 0e8d5319b7e..0b4f7d74b60 100644 --- a/chromium/ui/views/controls/button/md_text_button.cc +++ b/chromium/ui/views/controls/button/md_text_button.cc @@ -114,7 +114,7 @@ void MdTextButton::OnThemeChanged() { } SkColor MdTextButton::GetInkDropBaseColor() const { - return color_utils::DeriveDefaultIconColor(label()->enabled_color()); + return color_utils::DeriveDefaultIconColor(label()->GetEnabledColor()); } void MdTextButton::StateChanged(ButtonState old_state) { @@ -240,20 +240,20 @@ void MdTextButton::UpdatePadding() { void MdTextButton::UpdateColors() { bool is_disabled = state() == STATE_DISABLED; SkColor enabled_text_color = - style::GetColor(*this, label()->text_context(), + style::GetColor(*this, label()->GetTextContext(), is_prominent_ ? style::STYLE_DIALOG_BUTTON_DEFAULT : style::STYLE_PRIMARY); if (!explicitly_set_normal_color()) { const auto colors = explicitly_set_colors(); LabelButton::SetEnabledTextColors(enabled_text_color); // Disabled buttons need the disabled color explicitly set. - // This ensures that label()->enabled_color() returns the correct color as + // This ensures that label()->GetEnabledColor() returns the correct color as // the basis for calculating the stroke color. enabled_text_color isn't used // since a descendant could have overridden the label enabled color. if (is_disabled) { - LabelButton::SetTextColor(STATE_DISABLED, - style::GetColor(*this, label()->text_context(), - style::STYLE_DISABLED)); + LabelButton::SetTextColor( + STATE_DISABLED, style::GetColor(*this, label()->GetTextContext(), + style::STYLE_DISABLED)); } set_explicitly_set_colors(colors); } diff --git a/chromium/ui/views/controls/button/menu_button.cc b/chromium/ui/views/controls/button/menu_button.cc index 32bf07c7533..d78277df63a 100644 --- a/chromium/ui/views/controls/button/menu_button.cc +++ b/chromium/ui/views/controls/button/menu_button.cc @@ -11,8 +11,6 @@ #include "ui/views/controls/button/menu_button_controller.h" namespace views { -// static -const char MenuButton::kViewClassName[] = "MenuButton"; MenuButton::MenuButton(const base::string16& text, MenuButtonListener* menu_button_listener, @@ -31,18 +29,14 @@ bool MenuButton::Activate(const ui::Event* event) { return button_controller()->Activate(event); } -bool MenuButton::IsTriggerableEventType(const ui::Event& event) { - return button_controller()->IsTriggerableEventType(event); -} - -const char* MenuButton::GetClassName() const { - return kViewClassName; -} - void MenuButton::NotifyClick(const ui::Event& event) { // Notify MenuButtonListener via MenuButtonController, instead of // ButtonListener::ButtonPressed. button_controller()->Activate(&event); } +BEGIN_METADATA(MenuButton) +METADATA_PARENT_CLASS(LabelButton) +END_METADATA() + } // namespace views diff --git a/chromium/ui/views/controls/button/menu_button.h b/chromium/ui/views/controls/button/menu_button.h index 61423f00713..d55a4752620 100644 --- a/chromium/ui/views/controls/button/menu_button.h +++ b/chromium/ui/views/controls/button/menu_button.h @@ -23,7 +23,7 @@ class MenuButtonListener; //////////////////////////////////////////////////////////////////////////////// class VIEWS_EXPORT MenuButton : public LabelButton { public: - static const char kViewClassName[]; + METADATA_HEADER(MenuButton); // How much padding to put on the left and right of the menu marker. static constexpr int kMenuMarkerPaddingLeft = 3; @@ -41,12 +41,6 @@ class VIEWS_EXPORT MenuButton : public LabelButton { bool Activate(const ui::Event* event); - // TODO(cyan): Remove this method and move into MenuButtonController. - virtual bool IsTriggerableEventType(const ui::Event& event); - - // View: - const char* GetClassName() const override; - protected: // Button: void NotifyClick(const ui::Event& event) final; diff --git a/chromium/ui/views/controls/button/menu_button_controller.cc b/chromium/ui/views/controls/button/menu_button_controller.cc index 3a741cca1bf..7d77b7a2dee 100644 --- a/chromium/ui/views/controls/button/menu_button_controller.cc +++ b/chromium/ui/views/controls/button/menu_button_controller.cc @@ -20,6 +20,17 @@ using base::TimeTicks; namespace views { +namespace { +ui::EventType NotifyActionToMouseEventType(Button::NotifyAction notify_action) { + switch (notify_action) { + case Button::NOTIFY_ON_PRESS: + return ui::ET_MOUSE_PRESSED; + case Button::NOTIFY_ON_RELEASE: + return ui::ET_MOUSE_RELEASED; + } +} +} // namespace + //////////////////////////////////////////////////////////////////////////////// // // MenuButtonController::PressedLock @@ -66,7 +77,13 @@ MenuButtonController::MenuButtonController( Button* button, MenuButtonListener* listener, std::unique_ptr<ButtonControllerDelegate> delegate) - : ButtonController(button, std::move(delegate)), listener_(listener) {} + : ButtonController(button, std::move(delegate)), listener_(listener) { + // Triggers on button press by default, unless drag-and-drop is enabled, see + // MenuButtonController::IsTriggerableEventType. + // TODO(cyan): Investigate using PlatformStyle::kMenuNotifyActivationAction. + // TODO(cyan): Move NotifyAction into ButtonController. + button->set_notify_action(Button::NOTIFY_ON_PRESS); +} MenuButtonController::~MenuButtonController() = default; @@ -267,12 +284,13 @@ bool MenuButtonController::IsTriggerableEventType(const ui::Event& event) { // would also activate a context menu. if (!(mouse_event->button_flags() & button()->triggerable_event_flags())) return false; - // If dragging is supported activate on release, otherwise activate on - // pressed. + + // Activate on release if dragging, otherwise activate based on + // notify_action. ui::EventType active_on = delegate()->GetDragOperations(mouse_event->location()) == ui::DragDropTypes::DRAG_NONE - ? ui::ET_MOUSE_PRESSED + ? NotifyActionToMouseEventType(button()->notify_action()) : ui::ET_MOUSE_RELEASED; return event.type() == active_on; } diff --git a/chromium/ui/views/controls/button/menu_button_unittest.cc b/chromium/ui/views/controls/button/menu_button_unittest.cc index 18665c52d20..f296f95aac6 100644 --- a/chromium/ui/views/controls/button/menu_button_unittest.cc +++ b/chromium/ui/views/controls/button/menu_button_unittest.cc @@ -243,7 +243,7 @@ class TestDragDropClient : public aura::client::DragDropClient, ~TestDragDropClient() override; // aura::client::DragDropClient: - int StartDragAndDrop(const ui::OSExchangeData& data, + int StartDragAndDrop(std::unique_ptr<ui::OSExchangeData> data, aura::Window* root_window, aura::Window* source_window, const gfx::Point& screen_location, @@ -273,7 +273,7 @@ TestDragDropClient::TestDragDropClient() = default; TestDragDropClient::~TestDragDropClient() = default; int TestDragDropClient::StartDragAndDrop( - const ui::OSExchangeData& data, + std::unique_ptr<ui::OSExchangeData> data, aura::Window* root_window, aura::Window* source_window, const gfx::Point& screen_location, diff --git a/chromium/ui/views/controls/button/radio_button.cc b/chromium/ui/views/controls/button/radio_button.cc index e7189a5924c..975862f4496 100644 --- a/chromium/ui/views/controls/button/radio_button.cc +++ b/chromium/ui/views/controls/button/radio_button.cc @@ -16,9 +16,6 @@ namespace views { -// static -const char RadioButton::kViewClassName[] = "RadioButton"; - RadioButton::RadioButton(const base::string16& label, int group_id) : Checkbox(label, nullptr) { SetGroup(group_id); @@ -26,10 +23,6 @@ RadioButton::RadioButton(const base::string16& label, int group_id) RadioButton::~RadioButton() = default; -const char* RadioButton::GetClassName() const { - return kViewClassName; -} - void RadioButton::GetAccessibleNodeData(ui::AXNodeData* node_data) { Checkbox::GetAccessibleNodeData(node_data); node_data->role = ax::mojom::Role::kRadioButton; @@ -114,4 +107,8 @@ void RadioButton::GetViewsInGroupFromParent(int group, Views* views) { parent()->GetViewsInGroup(group, views); } +BEGIN_METADATA(RadioButton) +METADATA_PARENT_CLASS(Checkbox) +END_METADATA() + } // namespace views diff --git a/chromium/ui/views/controls/button/radio_button.h b/chromium/ui/views/controls/button/radio_button.h index eda57860d3c..3bb6d4a5fe3 100644 --- a/chromium/ui/views/controls/button/radio_button.h +++ b/chromium/ui/views/controls/button/radio_button.h @@ -16,14 +16,12 @@ namespace views { // platform specific objects to replicate the native platforms looks and feel. class VIEWS_EXPORT RadioButton : public Checkbox { public: - // The button's class name. - static const char kViewClassName[]; + METADATA_HEADER(RadioButton); RadioButton(const base::string16& label, int group_id); ~RadioButton() override; // Overridden from View: - const char* GetClassName() const override; void GetAccessibleNodeData(ui::AXNodeData* node_data) override; View* GetSelectedViewForGroup(int group) override; bool IsGroupFocusTraversable() const override; diff --git a/chromium/ui/views/controls/button/toggle_button.cc b/chromium/ui/views/controls/button/toggle_button.cc index 57a0eafcec7..5e6f4a4af54 100644 --- a/chromium/ui/views/controls/button/toggle_button.cc +++ b/chromium/ui/views/controls/button/toggle_button.cc @@ -67,9 +67,6 @@ class ToggleButton::ThumbView : public InkDropHostView { static constexpr int kShadowBlur = 2; // views::View: - const char* GetClassName() const override { - return "ToggleButton::ThumbView"; - } void OnPaint(gfx::Canvas* canvas) override { const float dsf = canvas->UndoDeviceScaleFactor(); @@ -111,9 +108,6 @@ class ToggleButton::ThumbView : public InkDropHostView { DISALLOW_COPY_AND_ASSIGN(ThumbView); }; -// static -const char ToggleButton::kViewClassName[] = "ToggleButton"; - ToggleButton::ToggleButton(ButtonListener* listener) : Button(listener) { slide_animation_.SetSlideDuration(80 /* ms */); slide_animation_.SetTweenType(gfx::Tween::LINEAR); @@ -200,10 +194,6 @@ SkColor ToggleButton::GetTrackColor(bool is_on) const { return SkColorSetA(GetNativeTheme()->GetSystemColor(color_id), kTrackAlpha); } -const char* ToggleButton::GetClassName() const { - return kViewClassName; -} - bool ToggleButton::CanAcceptEvent(const ui::Event& event) { return GetAcceptsEvents() && Button::CanAcceptEvent(event); } diff --git a/chromium/ui/views/controls/button/toggle_button.h b/chromium/ui/views/controls/button/toggle_button.h index e8b77da6f57..54fa757da89 100644 --- a/chromium/ui/views/controls/button/toggle_button.h +++ b/chromium/ui/views/controls/button/toggle_button.h @@ -17,8 +17,6 @@ class VIEWS_EXPORT ToggleButton : public Button { public: METADATA_HEADER(ToggleButton); - static const char kViewClassName[]; - explicit ToggleButton(ButtonListener* listener); ~ToggleButton() override; @@ -49,7 +47,6 @@ class VIEWS_EXPORT ToggleButton : public Button { SkColor GetTrackColor(bool is_on) const; // views::View: - const char* GetClassName() const override; bool CanAcceptEvent(const ui::Event& event) override; void OnBoundsChanged(const gfx::Rect& previous_bounds) override; void OnThemeChanged() override; diff --git a/chromium/ui/views/controls/combobox/combobox.cc b/chromium/ui/views/controls/combobox/combobox.cc index 107b9e7af60..caf05ca43c9 100644 --- a/chromium/ui/views/controls/combobox/combobox.cc +++ b/chromium/ui/views/controls/combobox/combobox.cc @@ -117,9 +117,6 @@ int GetAdjacentIndex(ui::ComboboxModel* model, int increment, int index) { } // namespace -// static -const char Combobox::kViewClassName[] = "views/Combobox"; - // Adapts a ui::ComboboxModel to a ui::MenuModel. class Combobox::ComboboxMenuModel : public ui::MenuModel { public: @@ -358,10 +355,6 @@ gfx::Size Combobox::CalculatePreferredSize() const { return gfx::Size(total_width, content_size_.height() + insets.height()); } -const char* Combobox::GetClassName() const { - return kViewClassName; -} - bool Combobox::SkipDefaultKeyEventProcessing(const ui::KeyEvent& e) { // Escape should close the drop down list when it is active, not host UI. if (e.key_code() != ui::VKEY_ESCAPE || diff --git a/chromium/ui/views/controls/combobox/combobox.h b/chromium/ui/views/controls/combobox/combobox.h index de84ae9d4bf..23771760cdd 100644 --- a/chromium/ui/views/controls/combobox/combobox.h +++ b/chromium/ui/views/controls/combobox/combobox.h @@ -41,8 +41,6 @@ class VIEWS_EXPORT Combobox : public View, public: METADATA_HEADER(Combobox); - // The combobox's class name. - static const char kViewClassName[]; static constexpr int kDefaultComboboxTextContext = style::CONTEXT_BUTTON; static constexpr int kDefaultComboboxTextStyle = style::STYLE_PRIMARY; @@ -86,7 +84,6 @@ class VIEWS_EXPORT Combobox : public View, // Overridden from View: gfx::Size CalculatePreferredSize() const override; - const char* GetClassName() const override; bool SkipDefaultKeyEventProcessing(const ui::KeyEvent& e) override; bool OnKeyPressed(const ui::KeyEvent& e) override; void OnPaint(gfx::Canvas* canvas) override; diff --git a/chromium/ui/views/controls/editable_combobox/editable_combobox.cc b/chromium/ui/views/controls/editable_combobox/editable_combobox.cc index d418b307dfb..cc70d3cd1c8 100644 --- a/chromium/ui/views/controls/editable_combobox/editable_combobox.cc +++ b/chromium/ui/views/controls/editable_combobox/editable_combobox.cc @@ -15,6 +15,7 @@ #include "base/strings/string16.h" #include "build/build_config.h" #include "third_party/skia/include/core/SkColor.h" +#include "ui/accessibility/ax_action_data.h" #include "ui/accessibility/ax_node_data.h" #include "ui/base/accelerators/accelerator.h" #include "ui/base/ime/text_input_type.h" @@ -104,7 +105,8 @@ class Arrow : public Button { } void GetAccessibleNodeData(ui::AXNodeData* node_data) override { - node_data->role = ax::mojom::Role::kPopUpButton; + node_data->role = ax::mojom::Role::kComboBoxMenuButton; + node_data->SetName(GetAccessibleName()); node_data->SetHasPopup(ax::mojom::HasPopup::kMenu); if (GetEnabled()) node_data->SetDefaultActionVerb(ax::mojom::DefaultActionVerb::kOpen); @@ -117,9 +119,6 @@ class Arrow : public Button { } // namespace -// static -const char EditableCombobox::kViewClassName[] = "EditableCombobox"; - // Adapts a ui::ComboboxModel to a ui::MenuModel to be used by EditableCombobox. // Also provides a filtering capability. class EditableCombobox::EditableComboboxMenuModel @@ -278,6 +277,7 @@ class EditableCombobox::EditableComboboxPreTargetHandler event->flags() == event->changed_button_flags()) HandlePressEvent(event->root_location()); } + void OnTouchEvent(ui::TouchEvent* event) override { if (event->type() == ui::ET_TOUCH_PRESSED) HandlePressEvent(event->root_location()); @@ -357,7 +357,6 @@ void EditableCombobox::SetText(const base::string16& text) { // SetText does not actually notify the TextfieldController, so we call the // handling code directly. HandleNewContent(text); - ShowDropDownMenu(); } const gfx::FontList& EditableCombobox::GetFontList() const { @@ -370,6 +369,8 @@ void EditableCombobox::SelectRange(const gfx::Range& range) { void EditableCombobox::SetAccessibleName(const base::string16& name) { textfield_->SetAccessibleName(name); + if (arrow_) + arrow_->SetAccessibleName(name); } void EditableCombobox::SetAssociatedLabel(View* labelling_view) { @@ -384,7 +385,6 @@ void EditableCombobox::RevealPasswords(bool revealed) { textfield_->SetTextInputType(revealed ? ui::TEXT_INPUT_TYPE_TEXT : ui::TEXT_INPUT_TYPE_PASSWORD); menu_model_->UpdateItemsShown(); - ShowDropDownMenu(); } int EditableCombobox::GetItemCountForTest() { @@ -398,10 +398,6 @@ base::string16 EditableCombobox::GetItemForTest(int index) { //////////////////////////////////////////////////////////////////////////////// // EditableCombobox, View overrides: -const char* EditableCombobox::GetClassName() const { - return kViewClassName; -} - void EditableCombobox::Layout() { View::Layout(); if (arrow_) { @@ -415,6 +411,13 @@ void EditableCombobox::OnThemeChanged() { textfield_->OnThemeChanged(); } +void EditableCombobox::GetAccessibleNodeData(ui::AXNodeData* node_data) { + node_data->role = ax::mojom::Role::kComboBoxGrouping; + + node_data->SetName(textfield_->accessible_name()); + node_data->SetValue(GetText()); +} + //////////////////////////////////////////////////////////////////////////////// // EditableCombobox, TextfieldController overrides: @@ -424,27 +427,17 @@ void EditableCombobox::ContentsChanged(Textfield* sender, ShowDropDownMenu(ui::MENU_SOURCE_KEYBOARD); } -bool EditableCombobox::HandleMouseEvent(Textfield* sender, - const ui::MouseEvent& mouse_event) { - // We show the menu on mouse release instead of mouse press so that the menu - // showing up doesn't interrupt a potential text selection operation by the - // user. - if (mouse_event.type() == ui::ET_MOUSE_PRESSED) { - mouse_pressed_ = true; - } else if (mouse_event.type() == ui::ET_MOUSE_RELEASED) { - mouse_pressed_ = false; - ShowDropDownMenu(ui::MENU_SOURCE_MOUSE); +bool EditableCombobox::HandleKeyEvent(Textfield* sender, + const ui::KeyEvent& key_event) { + if (key_event.type() == ui::ET_KEY_PRESSED && + (key_event.key_code() == ui::VKEY_UP || + key_event.key_code() == ui::VKEY_DOWN)) { + ShowDropDownMenu(ui::MENU_SOURCE_KEYBOARD); + return true; } return false; } -bool EditableCombobox::HandleGestureEvent( - Textfield* sender, - const ui::GestureEvent& gesture_event) { - ShowDropDownMenu(ui::MENU_SOURCE_TOUCH); - return false; -} - //////////////////////////////////////////////////////////////////////////////// // EditableCombobox, View overrides: @@ -452,23 +445,15 @@ void EditableCombobox::OnViewBlurred(View* observed_view) { CloseMenu(); } -void EditableCombobox::OnViewFocused(View* observed_view) { - // We only show the menu if the mouse is not currently pressed to avoid - // interrupting a text selection operation. The menu will be shown on mouse - // release inside HandleMouseEvent. - if (!mouse_pressed_) - ShowDropDownMenu(); -} - //////////////////////////////////////////////////////////////////////////////// // EditableCombobox, ButtonListener overrides: void EditableCombobox::ButtonPressed(Button* sender, const ui::Event& event) { + textfield_->RequestFocus(); if (menu_runner_ && menu_runner_->IsRunning()) { CloseMenu(); return; } - textfield_->RequestFocus(); ui::MenuSourceType source_type = ui::MENU_SOURCE_MOUSE; if (event.IsKeyEvent()) source_type = ui::MENU_SOURCE_KEYBOARD; @@ -523,7 +508,9 @@ void EditableCombobox::ShowDropDownMenu(ui::MenuSourceType source_type) { CloseMenu(); return; } - if (!textfield_->HasFocus() || (menu_runner_ && menu_runner_->IsRunning())) + if (menu_runner_ && menu_runner_->IsRunning()) + return; + if (!GetWidget()) return; // Since we don't capture the mouse, we want to see the events that happen in @@ -554,4 +541,8 @@ void EditableCombobox::ShowDropDownMenu(ui::MenuSourceType source_type) { MenuAnchorPosition::kTopLeft, source_type); } +BEGIN_METADATA(EditableCombobox) +METADATA_PARENT_CLASS(View) +END_METADATA() + } // namespace views diff --git a/chromium/ui/views/controls/editable_combobox/editable_combobox.h b/chromium/ui/views/controls/editable_combobox/editable_combobox.h index d0208e1e406..1bdfcecd336 100644 --- a/chromium/ui/views/controls/editable_combobox/editable_combobox.h +++ b/chromium/ui/views/controls/editable_combobox/editable_combobox.h @@ -41,13 +41,13 @@ class VIEWS_EXPORT EditableCombobox : public View, public ViewObserver, public ButtonListener { public: + METADATA_HEADER(EditableCombobox); + enum class Type { kRegular, kPassword, }; - // The class name. - static const char kViewClassName[]; static constexpr int kDefaultTextContext = style::CONTEXT_BUTTON; static constexpr int kDefaultTextStyle = style::STYLE_PRIMARY; @@ -119,20 +119,17 @@ class VIEWS_EXPORT EditableCombobox : public View, void ShowDropDownMenu(ui::MenuSourceType source_type = ui::MENU_SOURCE_NONE); // Overridden from View: - const char* GetClassName() const override; void Layout() override; void OnThemeChanged() override; + void GetAccessibleNodeData(ui::AXNodeData* node_data) override; // Overridden from TextfieldController: void ContentsChanged(Textfield* sender, const base::string16& new_contents) override; - bool HandleMouseEvent(Textfield* sender, - const ui::MouseEvent& mouse_event) override; - bool HandleGestureEvent(Textfield* sender, - const ui::GestureEvent& gesture_event) override; + bool HandleKeyEvent(Textfield* sender, + const ui::KeyEvent& key_event) override; // Overridden from ViewObserver: - void OnViewFocused(View* observed_view) override; void OnViewBlurred(View* observed_view) override; // Overridden from ButtonListener: @@ -160,10 +157,6 @@ class VIEWS_EXPORT EditableCombobox : public View, const Type type_; - // True between mouse press and release, used to avoid opening the menu and - // interrupting textfield selection interactions. - bool mouse_pressed_ = false; - // Set while the drop-down is showing. std::unique_ptr<MenuRunner> menu_runner_; diff --git a/chromium/ui/views/controls/editable_combobox/editable_combobox_unittest.cc b/chromium/ui/views/controls/editable_combobox/editable_combobox_unittest.cc index 396f86bb4cc..54eab4cbf0d 100644 --- a/chromium/ui/views/controls/editable_combobox/editable_combobox_unittest.cc +++ b/chromium/ui/views/controls/editable_combobox/editable_combobox_unittest.cc @@ -25,6 +25,7 @@ #include "ui/gfx/geometry/point.h" #include "ui/gfx/geometry/rect.h" #include "ui/gfx/render_text.h" +#include "ui/views/context_menu_controller.h" #include "ui/views/controls/editable_combobox/editable_combobox_listener.h" #include "ui/views/controls/menu/menu_runner.h" #include "ui/views/controls/textfield/textfield.h" @@ -56,6 +57,27 @@ class DummyListener : public EditableComboboxListener { DISALLOW_COPY_AND_ASSIGN(DummyListener); }; +// No-op test double of a ContextMenuController +class TestContextMenuController : public ContextMenuController { + public: + TestContextMenuController() = default; + ~TestContextMenuController() override = default; + + // ContextMenuController: + void ShowContextMenuForViewImpl(View* source, + const gfx::Point& point, + ui::MenuSourceType source_type) override { + opened_menu_ = true; + } + + bool opened_menu() const { return opened_menu_; } + + private: + bool opened_menu_ = false; + + DISALLOW_COPY_AND_ASSIGN(TestContextMenuController); +}; + class EditableComboboxTest : public ViewsTestBase { public: EditableComboboxTest() { views::test::DisableMenuClosureAnimations(); } @@ -171,6 +193,18 @@ void EditableComboboxTest::InitWidget() { container->AddChildView(dummy_focusable_view_); widget_->Show(); +#if defined(OS_MACOSX) + // The event loop needs to be flushed here, otherwise in various tests: + // 1. The actual showing of the native window backing the widget gets delayed + // until a spin of the event loop. + // 2. The combobox menu object is triggered, and it starts listening for the + // "window did become key" notification as a sign that it lost focus and + // should close. + // 3. The event loop is spun, and the actual showing of the native window + // triggers the close of the menu opened from within the window. + base::RunLoop().RunUntilIdle(); +#endif + event_generator_ = std::make_unique<ui::test::EventGenerator>(GetRootWindow(widget_)); event_generator_->set_target(ui::test::EventGenerator::Target::WINDOW); @@ -208,7 +242,7 @@ void EditableComboboxTest::DragMouseTo(const gfx::Point& location) { } bool EditableComboboxTest::IsMenuOpen() { - return combobox_->GetMenuRunnerForTest() && + return combobox_ && combobox_->GetMenuRunnerForTest() && combobox_->GetMenuRunnerForTest()->IsRunning(); } @@ -246,16 +280,24 @@ void EditableComboboxTest::SendKeyEvent(ui::KeyboardCode key_code, event_generator_->PressKey(key_code, flags); } -TEST_F(EditableComboboxTest, FocusOnTextfieldOpensMenu) { +TEST_F(EditableComboboxTest, FocusOnTextfieldDoesntOpenMenu) { InitEditableCombobox(); EXPECT_FALSE(IsMenuOpen()); combobox_->GetTextfieldForTest()->RequestFocus(); + EXPECT_FALSE(IsMenuOpen()); +} + +TEST_F(EditableComboboxTest, ArrowDownOpensMenu) { + InitEditableCombobox(); + EXPECT_FALSE(IsMenuOpen()); + combobox_->GetTextfieldForTest()->RequestFocus(); + SendKeyEvent(ui::VKEY_DOWN); EXPECT_TRUE(IsMenuOpen()); } TEST_F(EditableComboboxTest, TabMovesToOtherViewAndClosesMenu) { InitEditableCombobox(); - combobox_->GetTextfieldForTest()->RequestFocus(); + ClickArrow(); EXPECT_TRUE(IsMenuOpen()); EXPECT_TRUE(combobox_->GetTextfieldForTest()->HasFocus()); SendKeyEvent(ui::VKEY_TAB); @@ -268,8 +310,9 @@ TEST_F(EditableComboboxTest, TabMovesToOtherViewAndClosesMenu) { TEST_F(EditableComboboxTest, ClickOutsideEditableComboboxWithoutLosingFocusClosesMenu) { InitEditableCombobox(); - combobox_->GetTextfieldForTest()->RequestFocus(); + ClickArrow(); EXPECT_TRUE(IsMenuOpen()); + EXPECT_TRUE(combobox_->GetTextfieldForTest()->HasFocus()); const gfx::Point outside_point(combobox_->x() + combobox_->width() + 1, combobox_->y() + 1); @@ -282,7 +325,7 @@ TEST_F(EditableComboboxTest, TEST_F(EditableComboboxTest, ClickTextfieldDoesntCloseMenu) { InitEditableCombobox(); - combobox_->GetTextfieldForTest()->RequestFocus(); + ClickArrow(); EXPECT_TRUE(IsMenuOpen()); MenuRunner* menu_runner1 = combobox_->GetMenuRunnerForTest(); @@ -296,7 +339,7 @@ TEST_F(EditableComboboxTest, ClickTextfieldDoesntCloseMenu) { TEST_F(EditableComboboxTest, RemovingControlWhileMenuOpenClosesMenu) { InitEditableCombobox(); - combobox_->GetTextfieldForTest()->RequestFocus(); + ClickArrow(); EXPECT_TRUE(IsMenuOpen()); parent_of_combobox_->RemoveChildView(combobox_); EXPECT_EQ(nullptr, combobox_->GetMenuRunnerForTest()); @@ -304,7 +347,7 @@ TEST_F(EditableComboboxTest, RemovingControlWhileMenuOpenClosesMenu) { TEST_F(EditableComboboxTest, RemovingParentOfControlWhileMenuOpenClosesMenu) { InitEditableCombobox(); - combobox_->GetTextfieldForTest()->RequestFocus(); + ClickArrow(); EXPECT_TRUE(IsMenuOpen()); widget_->GetContentsView()->RemoveChildView(parent_of_combobox_); EXPECT_EQ(nullptr, combobox_->GetMenuRunnerForTest()); @@ -497,8 +540,8 @@ TEST_F(EditableComboboxTest, TypingInTextfieldUnhighlightsMenuItem) { TEST_F(EditableComboboxTest, ClickOnMenuItemSelectsItAndClosesMenu) { InitEditableCombobox(); - combobox_->GetTextfieldForTest()->RequestFocus(); - EXPECT_TRUE(IsMenuOpen()); + ClickArrow(); + ASSERT_TRUE(IsMenuOpen()); ClickMenuItem(/*index=*/0); WaitForMenuClosureAnimation(); @@ -526,7 +569,8 @@ TEST_F(EditableComboboxTest, MenuCanAdaptToContentChange) { ASCIIToUTF16("bac"), ASCIIToUTF16("bad")}; InitEditableCombobox(items, /*filter_on_edit=*/true); - combobox_->GetTextfieldForTest()->RequestFocus(); + ClickArrow(); + ASSERT_TRUE(IsMenuOpen()); SendKeyEvent(ui::VKEY_DOWN); SendKeyEvent(ui::VKEY_RETURN); @@ -569,7 +613,7 @@ TEST_F(EditableComboboxTest, RefocusingReopensMenuBasedOnLatestContent) { // "bac", the selected item, instead of showing completions of "b", what we // had typed. dummy_focusable_view_->RequestFocus(); - combobox_->GetTextfieldForTest()->RequestFocus(); + ClickArrow(); EXPECT_TRUE(IsMenuOpen()); ASSERT_EQ(2, combobox_->GetItemCountForTest()); } @@ -709,36 +753,33 @@ TEST_F(EditableComboboxTest, ArrowButtonOpensAndClosesMenu) { EXPECT_FALSE(IsMenuOpen()); } -TEST_F(EditableComboboxTest, ShowMenuOnMouseRelease) { +TEST_F(EditableComboboxTest, ShowContextMenuOnMouseRelease) { std::vector<base::string16> items = {ASCIIToUTF16("item0"), ASCIIToUTF16("item1")}; InitEditableCombobox(items, /*filter_on_edit=*/false, /*show_on_empty=*/true); - dummy_focusable_view_->RequestFocus(); - WaitForMenuClosureAnimation(); EXPECT_FALSE(IsMenuOpen()); - - // Without initial focus. - EXPECT_FALSE(combobox_->GetTextfieldForTest()->HasFocus()); + TestContextMenuController context_menu_controller; + combobox_->GetTextfieldForTest()->set_context_menu_controller( + &context_menu_controller); const gfx::Point textfield_point(combobox_->x() + 1, combobox_->y() + 1); - PerformMouseEvent(widget_, textfield_point, ui::ET_MOUSE_PRESSED); + ui::MouseEvent click_mouse_event(ui::ET_MOUSE_PRESSED, textfield_point, + textfield_point, ui::EventTimeForNow(), + ui::EF_RIGHT_MOUSE_BUTTON, + ui::EF_RIGHT_MOUSE_BUTTON); + widget_->OnMouseEvent(&click_mouse_event); EXPECT_FALSE(IsMenuOpen()); - PerformMouseEvent(widget_, textfield_point, ui::ET_MOUSE_RELEASED); - EXPECT_TRUE(IsMenuOpen()); - - SendKeyEvent(ui::VKEY_ESCAPE); - WaitForMenuClosureAnimation(); + ui::MouseEvent release_mouse_event(ui::ET_MOUSE_RELEASED, textfield_point, + textfield_point, ui::EventTimeForNow(), + ui::EF_RIGHT_MOUSE_BUTTON, + ui::EF_RIGHT_MOUSE_BUTTON); + widget_->OnMouseEvent(&release_mouse_event); + // The context menu should appear, not the combobox dropdown. EXPECT_FALSE(IsMenuOpen()); - - // With initial focus. - EXPECT_TRUE(combobox_->GetTextfieldForTest()->HasFocus()); - PerformMouseEvent(widget_, textfield_point, ui::ET_MOUSE_PRESSED); - EXPECT_FALSE(IsMenuOpen()); - PerformMouseEvent(widget_, textfield_point, ui::ET_MOUSE_RELEASED); - EXPECT_TRUE(IsMenuOpen()); + EXPECT_TRUE(context_menu_controller.opened_menu()); } -TEST_F(EditableComboboxTest, DragToSelectDoesntOpenTheMenuUntilDone) { +TEST_F(EditableComboboxTest, DragToSelectDoesntOpenTheMenu) { std::vector<base::string16> items = {ASCIIToUTF16("item0"), ASCIIToUTF16("item1")}; InitEditableCombobox(items, /*filter_on_edit=*/false, @@ -765,7 +806,18 @@ TEST_F(EditableComboboxTest, DragToSelectDoesntOpenTheMenuUntilDone) { PerformMouseEvent(widget_, end_point, ui::ET_MOUSE_RELEASED); ASSERT_EQ(ASCIIToUTF16("abc"), combobox_->GetTextfieldForTest()->GetSelectedText()); - EXPECT_TRUE(IsMenuOpen()); + EXPECT_FALSE(IsMenuOpen()); +} + +TEST_F(EditableComboboxTest, NoCrashWithoutWidget) { + std::vector<base::string16> items = {ASCIIToUTF16("item0"), + ASCIIToUTF16("item1")}; + auto combobox = std::make_unique<EditableCombobox>( + std::make_unique<ui::SimpleComboboxModel>(items), + /*filter_on_edit=*/false, + /*show_on_empty=*/true, EditableCombobox::Type::kPassword); + // Showing the dropdown should silently fail. + combobox->RevealPasswords(true); } } // namespace diff --git a/chromium/ui/views/controls/focus_ring.cc b/chromium/ui/views/controls/focus_ring.cc index 95c79c95fd9..0fcf8e4ade1 100644 --- a/chromium/ui/views/controls/focus_ring.cc +++ b/chromium/ui/views/controls/focus_ring.cc @@ -25,13 +25,12 @@ double GetCornerRadius() { } // namespace -const char FocusRing::kViewClassName[] = "FocusRing"; - // static std::unique_ptr<FocusRing> FocusRing::Install(View* parent) { auto ring = base::WrapUnique<FocusRing>(new FocusRing()); ring->set_owned_by_client(); parent->AddChildView(ring.get()); + ring->EnableCanvasFlippingForRTLUI(parent->flip_canvas_on_paint_for_rtl_ui()); ring->InvalidateLayout(); ring->SchedulePaint(); return ring; @@ -63,10 +62,6 @@ void FocusRing::SetColor(base::Optional<SkColor> color) { SchedulePaint(); } -const char* FocusRing::GetClassName() const { - return kViewClassName; -} - void FocusRing::Layout() { // The focus ring handles its own sizing, which is simply to fill the parent // and extend a little beyond its borders. @@ -107,6 +102,8 @@ void FocusRing::OnPaint(gfx::Canvas* canvas) { path = GetHighlightPath(parent()); DCHECK(IsPathUseable(path)); + DCHECK_EQ(flip_canvas_on_paint_for_rtl_ui(), + parent()->flip_canvas_on_paint_for_rtl_ui()); SkRect bounds; SkRRect rbounds; if (path.isRect(&bounds)) { @@ -179,4 +176,8 @@ SkPath GetHighlightPath(const View* view) { return path; } +BEGIN_METADATA(FocusRing) +METADATA_PARENT_CLASS(View) +END_METADATA() + } // namespace views diff --git a/chromium/ui/views/controls/focus_ring.h b/chromium/ui/views/controls/focus_ring.h index f5e22f6d862..561e8ec57fc 100644 --- a/chromium/ui/views/controls/focus_ring.h +++ b/chromium/ui/views/controls/focus_ring.h @@ -37,7 +37,7 @@ namespace views { // these take care of repainting it when the state changes. class VIEWS_EXPORT FocusRing : public View, public ViewObserver { public: - static const char kViewClassName[]; + METADATA_HEADER(FocusRing); using ViewPredicate = std::function<bool(View* view)>; @@ -77,7 +77,6 @@ class VIEWS_EXPORT FocusRing : public View, public ViewObserver { void SetColor(base::Optional<SkColor> color); // View: - const char* GetClassName() const override; void Layout() override; void ViewHierarchyChanged( const ViewHierarchyChangedDetails& details) override; diff --git a/chromium/ui/views/controls/image_view.cc b/chromium/ui/views/controls/image_view.cc index 2e9c4062526..8ca173c2233 100644 --- a/chromium/ui/views/controls/image_view.cc +++ b/chromium/ui/views/controls/image_view.cc @@ -23,9 +23,6 @@ void* GetBitmapPixels(const gfx::ImageSkia& img, float image_scale) { } // namespace -// static -const char ImageView::kViewClassName[] = "ImageView"; - ImageView::ImageView() = default; ImageView::~ImageView() = default; @@ -76,10 +73,6 @@ void ImageView::OnPaint(gfx::Canvas* canvas) { OnPaintImage(canvas); } -const char* ImageView::GetClassName() const { - return kViewClassName; -} - void ImageView::OnPaintImage(gfx::Canvas* canvas) { last_paint_scale_ = canvas->image_scale(); last_painted_bitmap_pixels_ = nullptr; @@ -130,4 +123,8 @@ gfx::ImageSkia ImageView::GetPaintImage(float scale) { return scaled_image_; } +BEGIN_METADATA(ImageView) +METADATA_PARENT_CLASS(ImageViewBase) +END_METADATA() + } // namespace views diff --git a/chromium/ui/views/controls/image_view.h b/chromium/ui/views/controls/image_view.h index e80a97a34a1..0df833d88d7 100644 --- a/chromium/ui/views/controls/image_view.h +++ b/chromium/ui/views/controls/image_view.h @@ -27,8 +27,7 @@ namespace views { ///////////////////////////////////////////////////////////////////////////// class VIEWS_EXPORT ImageView : public ImageViewBase { public: - // Internal class name. - static const char kViewClassName[]; + METADATA_HEADER(ImageView); ImageView(); ~ImageView() override; @@ -47,7 +46,6 @@ class VIEWS_EXPORT ImageView : public ImageViewBase { // Overridden from View: void OnPaint(gfx::Canvas* canvas) override; - const char* GetClassName() const override; protected: // Overridden from ImageViewBase: diff --git a/chromium/ui/views/controls/image_view_base.h b/chromium/ui/views/controls/image_view_base.h index 3784c9c0196..e8bc0b2196d 100644 --- a/chromium/ui/views/controls/image_view_base.h +++ b/chromium/ui/views/controls/image_view_base.h @@ -53,7 +53,6 @@ class VIEWS_EXPORT ImageViewBase : public View { // Overridden from View: void OnPaint(gfx::Canvas* canvas) override = 0; void GetAccessibleNodeData(ui::AXNodeData* node_data) override; - const char* GetClassName() const override = 0; base::string16 GetTooltipText(const gfx::Point& p) const override; gfx::Size CalculatePreferredSize() const override; views::PaintInfo::ScaleType GetPaintScaleType() const override; diff --git a/chromium/ui/views/controls/image_view_unittest.cc b/chromium/ui/views/controls/image_view_unittest.cc index 1ff073b62c5..f5b69ab8a92 100644 --- a/chromium/ui/views/controls/image_view_unittest.cc +++ b/chromium/ui/views/controls/image_view_unittest.cc @@ -51,9 +51,9 @@ class ImageViewTest : public ViewsTestBase, widget_.Init(params); View* container = new View(); // Make sure children can take up exactly as much space as they require. - BoxLayout::Orientation orientation = GetParam() == Axis::kHorizontal - ? BoxLayout::kHorizontal - : BoxLayout::kVertical; + BoxLayout::Orientation orientation = + GetParam() == Axis::kHorizontal ? BoxLayout::Orientation::kHorizontal + : BoxLayout::Orientation::kVertical; container->SetLayoutManager(std::make_unique<BoxLayout>(orientation)); widget_.SetContentsView(container); diff --git a/chromium/ui/views/controls/label.cc b/chromium/ui/views/controls/label.cc index 24d08c798ce..e2d51fc87b2 100644 --- a/chromium/ui/views/controls/label.cc +++ b/chromium/ui/views/controls/label.cc @@ -44,8 +44,6 @@ bool IsOpaque(SkColor color) { namespace views { -const char Label::kViewClassName[] = "Label"; - Label::Label() : Label(base::string16()) { } @@ -82,19 +80,36 @@ void Label::SetFontList(const gfx::FontList& font_list) { ResetLayout(); } +const base::string16& Label::GetText() const { + return full_text_->text(); +} + void Label::SetText(const base::string16& new_text) { - if (new_text == text()) + if (new_text == GetText()) return; full_text_->SetText(new_text); - ResetLayout(); + OnPropertyChanged(&new_text, kPropertyEffectsLayout); stored_selection_range_ = gfx::Range::InvalidRange(); } -void Label::SetAutoColorReadabilityEnabled(bool enabled) { - if (auto_color_readability_ == enabled) +int Label::GetTextContext() const { + return text_context_; +} + +bool Label::GetAutoColorReadabilityEnabled() const { + return auto_color_readability_enabled_; +} + +void Label::SetAutoColorReadabilityEnabled( + bool auto_color_readability_enabled) { + if (auto_color_readability_enabled_ == auto_color_readability_enabled) return; - auto_color_readability_ = enabled; - RecalculateColors(); + auto_color_readability_enabled_ = auto_color_readability_enabled; + OnPropertyChanged(&auto_color_readability_enabled_, kPropertyEffectsPaint); +} + +SkColor Label::GetEnabledColor() const { + return actual_enabled_color_; } void Label::SetEnabledColor(SkColor color) { @@ -102,7 +117,11 @@ void Label::SetEnabledColor(SkColor color) { return; requested_enabled_color_ = color; enabled_color_set_ = true; - RecalculateColors(); + OnPropertyChanged(&requested_enabled_color_, kPropertyEffectsPaint); +} + +SkColor Label::GetBackgroundColor() const { + return background_color_; } void Label::SetBackgroundColor(SkColor color) { @@ -110,7 +129,11 @@ void Label::SetBackgroundColor(SkColor color) { return; background_color_ = color; background_color_set_ = true; - RecalculateColors(); + OnPropertyChanged(&background_color_, kPropertyEffectsPaint); +} + +SkColor Label::GetSelectionTextColor() const { + return actual_selection_text_color_; } void Label::SetSelectionTextColor(SkColor color) { @@ -118,7 +141,11 @@ void Label::SetSelectionTextColor(SkColor color) { return; requested_selection_text_color_ = color; selection_text_color_set_ = true; - RecalculateColors(); + OnPropertyChanged(&requested_selection_text_color_, kPropertyEffectsPaint); +} + +SkColor Label::GetSelectionBackgroundColor() const { + return selection_background_color_; } void Label::SetSelectionBackgroundColor(SkColor color) { @@ -126,7 +153,11 @@ void Label::SetSelectionBackgroundColor(SkColor color) { return; selection_background_color_ = color; selection_background_color_set_ = true; - RecalculateColors(); + OnPropertyChanged(&selection_background_color_, kPropertyEffectsPaint); +} + +const gfx::ShadowValues& Label::shadows() const { + return full_text_->shadows(); } void Label::SetShadows(const gfx::ShadowValues& shadows) { @@ -136,53 +167,82 @@ void Label::SetShadows(const gfx::ShadowValues& shadows) { ResetLayout(); } +bool Label::GetSubpixelRenderingEnabled() const { + return subpixel_rendering_enabled_; +} + void Label::SetSubpixelRenderingEnabled(bool subpixel_rendering_enabled) { if (subpixel_rendering_enabled_ == subpixel_rendering_enabled) return; subpixel_rendering_enabled_ = subpixel_rendering_enabled; - RecalculateColors(); + OnPropertyChanged(&subpixel_rendering_enabled_, kPropertyEffectsPaint); +} + +gfx::HorizontalAlignment Label::GetHorizontalAlignment() const { + return full_text_->horizontal_alignment(); } void Label::SetHorizontalAlignment(gfx::HorizontalAlignment alignment) { alignment = gfx::MaybeFlipForRTL(alignment); - if (horizontal_alignment() == alignment) + if (GetHorizontalAlignment() == alignment) return; full_text_->SetHorizontalAlignment(alignment); ResetLayout(); } +int Label::GetLineHeight() const { + return full_text_->min_line_height(); +} + void Label::SetLineHeight(int height) { - if (line_height() == height) + if (GetLineHeight() == height) return; full_text_->SetMinLineHeight(height); - ResetLayout(); + OnPropertyChanged(&height, kPropertyEffectsLayout); +} + +bool Label::GetMultiLine() const { + return multi_line_; } void Label::SetMultiLine(bool multi_line) { DCHECK(!multi_line || (elide_behavior_ == gfx::ELIDE_TAIL || elide_behavior_ == gfx::NO_ELIDE)); - if (this->multi_line() == multi_line) + if (this->GetMultiLine() == multi_line) return; multi_line_ = multi_line; full_text_->SetMultiline(multi_line); full_text_->SetReplaceNewlineCharsWithSymbols(!multi_line); - ResetLayout(); + OnPropertyChanged(&multi_line_, kPropertyEffectsLayout); +} + +int Label::GetMaxLines() const { + return max_lines_; } void Label::SetMaxLines(int max_lines) { if (max_lines_ == max_lines) return; max_lines_ = max_lines; - ResetLayout(); + OnPropertyChanged(&max_lines_, kPropertyEffectsLayout); +} + +bool Label::GetObscured() const { + return full_text_->obscured(); } void Label::SetObscured(bool obscured) { - if (this->obscured() == obscured) + if (this->GetObscured() == obscured) return; full_text_->SetObscured(obscured); if (obscured) SetSelectable(false); - ResetLayout(); + OnPropertyChanged(&obscured, kPropertyEffectsLayout); +} + +bool Label::GetAllowCharacterBreak() const { + return full_text_->word_wrap_behavior() == gfx::WRAP_LONG_WORDS ? true + : false; } void Label::SetAllowCharacterBreak(bool allow_character_break) { @@ -191,41 +251,75 @@ void Label::SetAllowCharacterBreak(bool allow_character_break) { if (full_text_->word_wrap_behavior() == behavior) return; full_text_->SetWordWrapBehavior(behavior); - if (multi_line()) { - ResetLayout(); - } + OnPropertyChanged(&allow_character_break, kPropertyEffectsLayout); +} + +gfx::ElideBehavior Label::GetElideBehavior() const { + return elide_behavior_; } void Label::SetElideBehavior(gfx::ElideBehavior elide_behavior) { - DCHECK(!multi_line() || (elide_behavior_ == gfx::ELIDE_TAIL || - elide_behavior_ == gfx::NO_ELIDE)); + DCHECK(!GetMultiLine() || (elide_behavior_ == gfx::ELIDE_TAIL || + elide_behavior_ == gfx::NO_ELIDE)); if (elide_behavior_ == elide_behavior) return; elide_behavior_ = elide_behavior; ResetLayout(); } +base::string16 Label::GetTooltipText() const { + return tooltip_text_; +} + void Label::SetTooltipText(const base::string16& tooltip_text) { DCHECK(handles_tooltips_); + if (tooltip_text_ == tooltip_text) + return; tooltip_text_ = tooltip_text; + OnPropertyChanged(&tooltip_text_, kPropertyEffectsNone); +} + +bool Label::GetHandlesTooltips() const { + return handles_tooltips_; } void Label::SetHandlesTooltips(bool enabled) { + if (handles_tooltips_ == enabled) + return; handles_tooltips_ = enabled; + OnPropertyChanged(&handles_tooltips_, kPropertyEffectsNone); } void Label::SizeToFit(int fixed_width) { - DCHECK(multi_line()); + DCHECK(GetMultiLine()); DCHECK_EQ(0, max_width_); fixed_width_ = fixed_width; SizeToPreferredSize(); } +int Label::GetMaximumWidth() const { + return max_width_; +} + void Label::SetMaximumWidth(int max_width) { - DCHECK(multi_line()); + DCHECK(GetMultiLine()); DCHECK_EQ(0, fixed_width_); + if (max_width_ == max_width) + return; max_width_ = max_width; - SizeToPreferredSize(); + OnPropertyChanged(&max_width_, kPropertyEffectsPreferredSizeChanged); +} + +bool Label::GetCollapseWhenHidden() const { + return collapse_when_hidden_; +} + +void Label::SetCollapseWhenHidden(bool value) { + if (collapse_when_hidden_ == value) + return; + collapse_when_hidden_ = value; + OnPropertyChanged(&collapse_when_hidden_, + kPropertyEffectsPreferredSizeChanged); } size_t Label::GetRequiredLines() const { @@ -243,11 +337,15 @@ base::i18n::TextDirection Label::GetTextDirectionForTesting() { } bool Label::IsSelectionSupported() const { - return !obscured() && full_text_->IsSelectionSupported(); + return !GetObscured() && full_text_->IsSelectionSupported(); +} + +bool Label::GetSelectable() const { + return !!selection_controller_; } bool Label::SetSelectable(bool value) { - if (value == selectable()) + if (value == GetSelectable()) return true; if (!value) { @@ -305,17 +403,17 @@ gfx::Size Label::CalculatePreferredSize() const { if (!GetVisible() && collapse_when_hidden_) return gfx::Size(); - if (multi_line() && fixed_width_ != 0 && !text().empty()) + if (GetMultiLine() && fixed_width_ != 0 && !GetText().empty()) return gfx::Size(fixed_width_, GetHeightForWidth(fixed_width_)); gfx::Size size(GetTextSize()); const gfx::Insets insets = GetInsets(); size.Enlarge(insets.width(), insets.height()); - if (multi_line() && max_width_ != 0 && max_width_ < size.width()) + if (GetMultiLine() && max_width_ != 0 && max_width_ < size.width()) return gfx::Size(max_width_, GetHeightForWidth(max_width_)); - if (multi_line() && max_lines() > 0) + if (GetMultiLine() && GetMaxLines() > 0) return gfx::Size(size.width(), GetHeightForWidth(size.width())); return size; } @@ -333,7 +431,7 @@ gfx::Size Label::GetMinimumSize() const { base::string16(gfx::kEllipsisUTF16), font_list())); } - if (!multi_line()) { + if (!GetMultiLine()) { if (elide_behavior_ == gfx::NO_ELIDE) { // If elision is disabled on single-line Labels, use text size as minimum. // This is OK because clients can use |gfx::ElideBehavior::TRUNCATE| @@ -353,8 +451,8 @@ int Label::GetHeightForWidth(int w) const { w -= GetInsets().width(); int height = 0; - int base_line_height = std::max(line_height(), font_list().GetHeight()); - if (!multi_line() || text().empty() || w <= 0) { + int base_line_height = std::max(GetLineHeight(), font_list().GetHeight()); + if (!GetMultiLine() || GetText().empty() || w <= 0) { height = base_line_height; } else { // SetDisplayRect() has a side effect for later calls of GetStringSize(). @@ -365,10 +463,10 @@ int Label::GetHeightForWidth(int w) const { // and |full_text_| can cache the height. full_text_->SetDisplayRect(gfx::Rect(0, 0, w, 0)); int string_height = full_text_->GetStringSize().height(); - // Cap the number of lines to |max_lines()| if multi-line and non-zero - // |max_lines()|. - height = multi_line() && max_lines() > 0 - ? std::min(max_lines() * base_line_height, string_height) + // Cap the number of lines to |GetMaxLines()| if multi-line and non-zero + // |GetMaxLines()|. + height = GetMultiLine() && GetMaxLines() > 0 + ? std::min(GetMaxLines() * base_line_height, string_height) : string_height; } height -= gfx::ShadowValue::GetMargin(full_text_->shadows()).height(); @@ -379,10 +477,6 @@ void Label::Layout() { ClearDisplayText(); } -const char* Label::GetClassName() const { - return kViewClassName; -} - View* Label::GetTooltipHandlerForPoint(const gfx::Point& point) { if (!handles_tooltips_ || (tooltip_text_.empty() && !ShouldShowDefaultTooltip())) @@ -420,29 +514,38 @@ base::string16 Label::GetTooltipText(const gfx::Point& p) const { return base::string16(); } +void Label::OnHandlePropertyChangeEffects(PropertyEffects property_effects) { + if (property_effects & kPropertyEffectsPreferredSizeChanged) + SizeToPreferredSize(); + if (property_effects & kPropertyEffectsLayout) + ResetLayout(); + if (property_effects & kPropertyEffectsPaint) + RecalculateColors(); +} + std::unique_ptr<gfx::RenderText> Label::CreateRenderText() const { // Multi-line labels only support NO_ELIDE and ELIDE_TAIL for now. // TODO(warx): Investigate more elide text support. gfx::ElideBehavior elide_behavior = - multi_line() && (elide_behavior_ != gfx::NO_ELIDE) ? gfx::ELIDE_TAIL - : elide_behavior_; + GetMultiLine() && (elide_behavior_ != gfx::NO_ELIDE) ? gfx::ELIDE_TAIL + : elide_behavior_; auto render_text = gfx::RenderText::CreateHarfBuzzInstance(); - render_text->SetHorizontalAlignment(horizontal_alignment()); + render_text->SetHorizontalAlignment(GetHorizontalAlignment()); render_text->SetDirectionalityMode(full_text_->directionality_mode()); render_text->SetElideBehavior(elide_behavior); - render_text->SetObscured(obscured()); - render_text->SetMinLineHeight(line_height()); + render_text->SetObscured(GetObscured()); + render_text->SetMinLineHeight(GetLineHeight()); render_text->SetFontList(font_list()); render_text->set_shadows(shadows()); render_text->SetCursorEnabled(false); - render_text->SetText(text()); - render_text->SetMultiline(multi_line()); - render_text->SetMaxLines(multi_line() ? max_lines() : 0); + render_text->SetText(GetText()); + render_text->SetMultiline(GetMultiLine()); + render_text->SetMaxLines(GetMultiLine() ? GetMaxLines() : 0); render_text->SetWordWrapBehavior(full_text_->word_wrap_behavior()); // Setup render text for selection controller. - if (selectable()) { + if (GetSelectable()) { render_text->set_focused(HasFocus()); if (stored_selection_range_.IsValid()) render_text->SelectRange(stored_selection_range_); @@ -601,7 +704,7 @@ bool Label::OnKeyPressed(const ui::KeyEvent& event) { } break; case ui::VKEY_A: - if (control && !alt && !text().empty()) { + if (control && !alt && !GetText().empty()) { SelectAll(); DCHECK(HasSelection()); UpdateSelectionClipboard(); @@ -734,8 +837,8 @@ bool Label::PasteSelectionClipboard() { void Label::UpdateSelectionClipboard() { #if defined(OS_LINUX) && !defined(OS_CHROMEOS) - if (!obscured()) { - ui::ScopedClipboardWriter(ui::CLIPBOARD_TYPE_SELECTION) + if (!GetObscured()) { + ui::ScopedClipboardWriter(ui::ClipboardType::kSelection) .WriteText(GetSelectedText()); } #endif @@ -748,9 +851,9 @@ bool Label::IsCommandIdChecked(int command_id) const { bool Label::IsCommandIdEnabled(int command_id) const { switch (command_id) { case IDS_APP_COPY: - return HasSelection() && !obscured(); + return HasSelection() && !GetObscured(); case IDS_APP_SELECT_ALL: - return GetRenderTextForSelectionController() && !text().empty(); + return GetRenderTextForSelectionController() && !GetText().empty(); } return false; } @@ -787,7 +890,7 @@ bool Label::GetAcceleratorForCommandId(int command_id, } const gfx::RenderText* Label::GetRenderTextForSelectionController() const { - if (!selectable()) + if (!GetSelectable()) return nullptr; MaybeBuildDisplayText(); @@ -814,7 +917,7 @@ void Label::Init(const base::string16& text, enabled_color_set_ = background_color_set_ = false; selection_text_color_set_ = selection_background_color_set_ = false; subpixel_rendering_enabled_ = true; - auto_color_readability_ = true; + auto_color_readability_enabled_ = true; multi_line_ = false; max_lines_ = 0; UpdateColorsFromTheme(); @@ -857,8 +960,8 @@ void Label::MaybeBuildDisplayText() const { gfx::Size Label::GetTextSize() const { gfx::Size size; - if (text().empty()) { - size = gfx::Size(0, std::max(line_height(), font_list().GetHeight())); + if (GetText().empty()) { + size = gfx::Size(0, std::max(GetLineHeight(), font_list().GetHeight())); } else { // Cancel the display rect of |full_text_|. The display rect may be // specified in GetHeightForWidth(), and specifying empty Rect cancels @@ -876,7 +979,7 @@ gfx::Size Label::GetTextSize() const { SkColor Label::GetForegroundColor(SkColor foreground, SkColor background) const { - return (auto_color_readability_ && IsOpaque(background)) + return (auto_color_readability_enabled_ && IsOpaque(background)) ? color_utils::BlendForMinContrast(foreground, background).color : foreground; } @@ -933,8 +1036,9 @@ void Label::UpdateColorsFromTheme() { bool Label::ShouldShowDefaultTooltip() const { const gfx::Size text_size = GetTextSize(); const gfx::Size size = GetContentsBounds().size(); - return !obscured() && (text_size.width() > size.width() || - (multi_line() && text_size.height() > size.height())); + return !GetObscured() && + (text_size.width() > size.width() || + (GetMultiLine() && text_size.height() > size.height())); } void Label::ClearDisplayText() const { @@ -958,9 +1062,9 @@ base::string16 Label::GetSelectedText() const { } void Label::CopyToClipboard() { - if (!HasSelection() || obscured()) + if (!HasSelection() || GetObscured()) return; - ui::ScopedClipboardWriter(ui::CLIPBOARD_TYPE_COPY_PASTE) + ui::ScopedClipboardWriter(ui::ClipboardType::kCopyPaste) .WriteText(GetSelectedText()); } @@ -970,4 +1074,25 @@ void Label::BuildContextMenuContents() { IDS_APP_SELECT_ALL); } +BEGIN_METADATA(Label) +METADATA_PARENT_CLASS(View) +ADD_PROPERTY_METADATA(Label, bool, AutoColorReadabilityEnabled) +ADD_PROPERTY_METADATA(Label, base::string16, Text) +ADD_PROPERTY_METADATA(Label, SkColor, EnabledColor) +ADD_PROPERTY_METADATA(Label, SkColor, BackgroundColor) +ADD_PROPERTY_METADATA(Label, SkColor, SelectionTextColor) +ADD_PROPERTY_METADATA(Label, SkColor, SelectionBackgroundColor) +ADD_PROPERTY_METADATA(Label, bool, SubpixelRenderingEnabled) +ADD_PROPERTY_METADATA(Label, int, LineHeight) +ADD_PROPERTY_METADATA(Label, bool, MultiLine) +ADD_PROPERTY_METADATA(Label, int, MaxLines) +ADD_PROPERTY_METADATA(Label, bool, Obscured) +ADD_PROPERTY_METADATA(Label, bool, AllowCharacterBreak) +ADD_PROPERTY_METADATA(Label, base::string16, TooltipText) +ADD_PROPERTY_METADATA(Label, bool, HandlesTooltips) +ADD_PROPERTY_METADATA(Label, bool, CollapseWhenHidden) +ADD_PROPERTY_METADATA(Label, int, MaximumWidth) +ADD_READONLY_PROPERTY_METADATA(Label, int, TextContext) +END_METADATA() + } // namespace views diff --git a/chromium/ui/views/controls/label.h b/chromium/ui/views/controls/label.h index 50e30e5cf35..ce169b164bc 100644 --- a/chromium/ui/views/controls/label.h +++ b/chromium/ui/views/controls/label.h @@ -14,6 +14,7 @@ #include "ui/gfx/render_text.h" #include "ui/gfx/text_constants.h" #include "ui/views/context_menu_controller.h" +#include "ui/views/metadata/metadata_header_macros.h" #include "ui/views/selection_controller_delegate.h" #include "ui/views/style/typography.h" #include "ui/views/view.h" @@ -31,8 +32,7 @@ class VIEWS_EXPORT Label : public View, public SelectionControllerDelegate, public ui::SimpleMenuModel::Delegate { public: - // Internal class name. - static const char kViewClassName[]; + METADATA_HEADER(Label); // 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 @@ -77,72 +77,71 @@ class VIEWS_EXPORT Label : public View, virtual void SetFontList(const gfx::FontList& font_list); // Get or set the label text. - const base::string16& text() const { return full_text_->text(); } + const base::string16& GetText() const; virtual void SetText(const base::string16& text); // Where the label appears in the UI. Passed in from the constructor. This is // a value from views::style::TextContext or an enum that extends it. - int text_context() const { return text_context_; } + int GetTextContext() const; // Enables or disables auto-color-readability (enabled by default). If this // is enabled, then calls to set any foreground or background color will // trigger an automatic mapper that uses color_utils::BlendForMinContrast() // to ensure that the foreground colors are readable over the background // color. + bool GetAutoColorReadabilityEnabled() const; void SetAutoColorReadabilityEnabled(bool enabled); - // Sets the color. This will automatically force the color to be readable - // over the current background color, if auto color readability is enabled. + // Gets/Sets the color. This will automatically force the color to be + // readable over the current background color, if auto color readability is + // enabled. + SkColor GetEnabledColor() const; virtual void SetEnabledColor(SkColor color); - SkColor enabled_color() const { return actual_enabled_color_; } - - // Sets the background color. This won't be explicitly drawn, but the label - // will force the text color to be readable over it. + // Gets/Sets the background color. This won't be explicitly drawn, but the + // label will force the text color to be readable over it. + SkColor GetBackgroundColor() const; void SetBackgroundColor(SkColor color); - SkColor background_color() const { return background_color_; } - // Sets the selection text color. This will automatically force the color to - // be readable over the selection background color, if auto color readability - // is enabled. Initialized with system default. + // Gets/Sets the selection text color. This will automatically force the color + // to be readable over the selection background color, if auto color + // readability is enabled. Initialized with system default. + SkColor GetSelectionTextColor() const; void SetSelectionTextColor(SkColor color); - SkColor selection_text_color() const { return actual_selection_text_color_; } - // Sets the selection background color. Initialized with system default. + // Gets/Sets the selection background color. Initialized with system default. + SkColor GetSelectionBackgroundColor() const; void SetSelectionBackgroundColor(SkColor color); - SkColor selection_background_color() const { - return selection_background_color_; - } - // Set drop shadows underneath the text. + // Get/Set drop shadows underneath the text. + const gfx::ShadowValues& shadows() const; void SetShadows(const gfx::ShadowValues& shadows); - const gfx::ShadowValues& shadows() const { return full_text_->shadows(); } - // Sets whether subpixel rendering is used; the default is true, but this + // Gets/Sets whether subpixel rendering is used; the default is true, but this // feature also requires an opaque background color. // TODO(mukai): rename this as SetSubpixelRenderingSuppressed() to keep the // consistency with RenderText field name. + bool GetSubpixelRenderingEnabled() const; void SetSubpixelRenderingEnabled(bool subpixel_rendering_enabled); - // Sets the horizontal alignment; the argument value is mirrored in RTL UI. + // Gets/Sets the horizontal alignment; the argument value is mirrored in RTL + // UI. + gfx::HorizontalAlignment GetHorizontalAlignment() const; void SetHorizontalAlignment(gfx::HorizontalAlignment alignment); - gfx::HorizontalAlignment horizontal_alignment() const { - return full_text_->horizontal_alignment(); - } // Get or set the distance in pixels between baselines of multi-line text. // Default is 0, indicating the distance between lines should be the standard // one for the label's text, font list, and platform. - int line_height() const { return full_text_->min_line_height(); } + int GetLineHeight() const; void SetLineHeight(int height); // Get or set if the label text can wrap on multiple lines; default is false. - bool multi_line() const { return multi_line_; } + bool GetMultiLine() const; void SetMultiLine(bool multi_line); // If multi-line, a non-zero value will cap the number of lines rendered, and // elide the rest (currently only ELIDE_TAIL supported). See gfx::RenderText. - int max_lines() const { return max_lines_; } + int GetMaxLines() const; void SetMaxLines(int max_lines); // Returns the number of lines required to render all text. The actual number @@ -151,28 +150,31 @@ class VIEWS_EXPORT Label : public View, // Get or set if the label text should be obscured before rendering (e.g. // should "Password!" display as "*********"); default is false. - bool obscured() const { return full_text_->obscured(); } + bool GetObscured() const; void SetObscured(bool obscured); - // Sets whether multi-line text can wrap mid-word; the default is false. + // Gets/Sets whether multi-line text can wrap mid-word; the default is false. // TODO(mukai): allow specifying WordWrapBehavior. + bool GetAllowCharacterBreak() const; void SetAllowCharacterBreak(bool allow_character_break); - // Sets the eliding or fading behavior, applied as necessary. The default is - // to elide at the end. Eliding is not well-supported for multi-line labels. + // Gets/Sets the eliding or fading behavior, applied as necessary. The default + // is to elide at the end. Eliding is not well-supported for multi-line + // labels. + gfx::ElideBehavior GetElideBehavior() const; void SetElideBehavior(gfx::ElideBehavior elide_behavior); - gfx::ElideBehavior elide_behavior() const { return elide_behavior_; } - // Sets the tooltip text. Default behavior for a label (single-line) is to - // show the full text if it is wider than its bounds. Calling this overrides - // the default behavior and lets you set a custom tooltip. To revert to - // default behavior, call this with an empty string. + // Gets/Sets the tooltip text. Default behavior for a label (single-line) is + // to show the full text if it is wider than its bounds. Calling this + // overrides the default behavior and lets you set a custom tooltip. To + // revert to default behavior, call this with an empty string. + base::string16 GetTooltipText() const; void SetTooltipText(const base::string16& tooltip_text); // Get or set whether this label can act as a tooltip handler; the default is // true. Set to false whenever an ancestor view should handle tooltips // instead. - bool handles_tooltips() const { return handles_tooltips_; } + bool GetHandlesTooltips() const; void SetHandlesTooltips(bool enabled); // Resizes the label so its width is set to the fixed width and its height @@ -185,10 +187,13 @@ class VIEWS_EXPORT Label : public View, void SizeToFit(int fixed_width); // Like SizeToFit, but uses a smaller width if possible. + int GetMaximumWidth() const; void SetMaximumWidth(int max_width); - // Sets whether the preferred size is empty when the label is not visible. - void set_collapse_when_hidden(bool value) { collapse_when_hidden_ = value; } + // Gets/Sets whether the preferred size is empty when the label is not + // visible. + bool GetCollapseWhenHidden() const; + void SetCollapseWhenHidden(bool value); // Get the text as displayed to the user, respecting the obscured flag. base::string16 GetDisplayTextForTesting(); @@ -204,7 +209,7 @@ class VIEWS_EXPORT Label : public View, virtual bool IsSelectionSupported() const; // Returns true if the label is selectable. Default is false. - bool selectable() const { return !!selection_controller_; } + bool GetSelectable() const; // Sets whether the label is selectable. False is returned if the call fails, // i.e. when selection is not supported but |selectable| is true. For example, @@ -230,12 +235,12 @@ class VIEWS_EXPORT Label : public View, gfx::Size GetMinimumSize() const override; int GetHeightForWidth(int w) const override; void Layout() override; - const char* GetClassName() const override; View* GetTooltipHandlerForPoint(const gfx::Point& point) override; bool CanProcessEventsWithinSubtree() const override; WordLookupClient* GetWordLookupClient() override; void GetAccessibleNodeData(ui::AXNodeData* node_data) override; base::string16 GetTooltipText(const gfx::Point& p) const override; + void OnHandlePropertyChangeEffects(PropertyEffects property_effects) override; protected: // Create a single RenderText instance to actually be painted. @@ -381,7 +386,7 @@ class VIEWS_EXPORT Label : public View, gfx::ElideBehavior elide_behavior_; bool subpixel_rendering_enabled_; - bool auto_color_readability_; + bool auto_color_readability_enabled_; // TODO(mukai): remove |multi_line_| when all RenderText can render multiline. bool multi_line_; int max_lines_; diff --git a/chromium/ui/views/controls/label_unittest.cc b/chromium/ui/views/controls/label_unittest.cc index 2cab103de88..847e8c76124 100644 --- a/chromium/ui/views/controls/label_unittest.cc +++ b/chromium/ui/views/controls/label_unittest.cc @@ -286,14 +286,14 @@ TEST_F(LabelTest, FontPropertyArial) { TEST_F(LabelTest, TextProperty) { base::string16 test_text(ASCIIToUTF16("A random string.")); label()->SetText(test_text); - EXPECT_EQ(test_text, label()->text()); + EXPECT_EQ(test_text, label()->GetText()); } TEST_F(LabelTest, ColorProperty) { SkColor color = SkColorSetARGB(20, 40, 10, 5); label()->SetAutoColorReadabilityEnabled(false); label()->SetEnabledColor(color); - EXPECT_EQ(color, label()->enabled_color()); + EXPECT_EQ(color, label()->GetEnabledColor()); } TEST_F(LabelTest, AlignmentProperty) { @@ -307,18 +307,18 @@ TEST_F(LabelTest, AlignmentProperty) { // The alignment should be flipped in RTL UI. label()->SetHorizontalAlignment(gfx::ALIGN_RIGHT); EXPECT_EQ(reverse_alignment ? gfx::ALIGN_LEFT : gfx::ALIGN_RIGHT, - label()->horizontal_alignment()); + label()->GetHorizontalAlignment()); label()->SetHorizontalAlignment(gfx::ALIGN_LEFT); EXPECT_EQ(reverse_alignment ? gfx::ALIGN_RIGHT : gfx::ALIGN_LEFT, - label()->horizontal_alignment()); + label()->GetHorizontalAlignment()); label()->SetHorizontalAlignment(gfx::ALIGN_CENTER); - EXPECT_EQ(gfx::ALIGN_CENTER, label()->horizontal_alignment()); + EXPECT_EQ(gfx::ALIGN_CENTER, label()->GetHorizontalAlignment()); for (size_t j = 0; j < 2; ++j) { label()->SetHorizontalAlignment(gfx::ALIGN_TO_HEAD); const bool rtl = j == 0; label()->SetText(rtl ? base::WideToUTF16(L"\x5d0") : ASCIIToUTF16("A")); - EXPECT_EQ(gfx::ALIGN_TO_HEAD, label()->horizontal_alignment()); + EXPECT_EQ(gfx::ALIGN_TO_HEAD, label()->GetHorizontalAlignment()); } } @@ -328,7 +328,7 @@ TEST_F(LabelTest, AlignmentProperty) { TEST_F(LabelTest, ElideBehavior) { base::string16 text(ASCIIToUTF16("This is example text.")); label()->SetText(text); - EXPECT_EQ(gfx::ELIDE_TAIL, label()->elide_behavior()); + EXPECT_EQ(gfx::ELIDE_TAIL, label()->GetElideBehavior()); gfx::Size size = label()->GetPreferredSize(); label()->SetBoundsRect(gfx::Rect(size)); EXPECT_EQ(text, label()->GetDisplayTextForTesting()); @@ -348,7 +348,7 @@ TEST_F(LabelTest, ElideBehaviorMinimumWidth) { label()->SetText(text); // Default should be |gfx::ELIDE_TAIL|. - EXPECT_EQ(gfx::ELIDE_TAIL, label()->elide_behavior()); + EXPECT_EQ(gfx::ELIDE_TAIL, label()->GetElideBehavior()); gfx::Size size = label()->GetMinimumSize(); // Elidable labels have a minimum width that fits |gfx::kEllipsisUTF16|. EXPECT_EQ(gfx::Canvas::GetStringWidth(base::string16(gfx::kEllipsisUTF16), @@ -367,7 +367,7 @@ TEST_F(LabelTest, ElideBehaviorMinimumWidth) { // Non-elidable single-line labels should take up their full text size, since // this behavior implies the text should not be cut off. - EXPECT_FALSE(label()->multi_line()); + EXPECT_FALSE(label()->GetMultiLine()); label()->SetElideBehavior(gfx::NO_ELIDE); size = label()->GetMinimumSize(); EXPECT_EQ(text.length(), label()->GetDisplayTextForTesting().length()); @@ -377,11 +377,11 @@ TEST_F(LabelTest, ElideBehaviorMinimumWidth) { } TEST_F(LabelTest, MultiLineProperty) { - EXPECT_FALSE(label()->multi_line()); + EXPECT_FALSE(label()->GetMultiLine()); label()->SetMultiLine(true); - EXPECT_TRUE(label()->multi_line()); + EXPECT_TRUE(label()->GetMultiLine()); label()->SetMultiLine(false); - EXPECT_FALSE(label()->multi_line()); + EXPECT_FALSE(label()->GetMultiLine()); } TEST_F(LabelTest, ObscuredProperty) { @@ -390,30 +390,30 @@ TEST_F(LabelTest, ObscuredProperty) { label()->SizeToPreferredSize(); // The text should be unobscured by default. - EXPECT_FALSE(label()->obscured()); + EXPECT_FALSE(label()->GetObscured()); EXPECT_EQ(test_text, label()->GetDisplayTextForTesting()); - EXPECT_EQ(test_text, label()->text()); + EXPECT_EQ(test_text, label()->GetText()); label()->SetObscured(true); label()->SizeToPreferredSize(); - EXPECT_TRUE(label()->obscured()); + EXPECT_TRUE(label()->GetObscured()); EXPECT_EQ(base::string16(test_text.size(), gfx::RenderText::kPasswordReplacementChar), label()->GetDisplayTextForTesting()); - EXPECT_EQ(test_text, label()->text()); + EXPECT_EQ(test_text, label()->GetText()); label()->SetText(test_text + test_text); label()->SizeToPreferredSize(); EXPECT_EQ(base::string16(test_text.size() * 2, gfx::RenderText::kPasswordReplacementChar), label()->GetDisplayTextForTesting()); - EXPECT_EQ(test_text + test_text, label()->text()); + EXPECT_EQ(test_text + test_text, label()->GetText()); label()->SetObscured(false); label()->SizeToPreferredSize(); - EXPECT_FALSE(label()->obscured()); + EXPECT_FALSE(label()->GetObscured()); EXPECT_EQ(test_text + test_text, label()->GetDisplayTextForTesting()); - EXPECT_EQ(test_text + test_text, label()->text()); + EXPECT_EQ(test_text + test_text, label()->GetText()); } TEST_F(LabelTest, ObscuredSurrogatePair) { @@ -425,7 +425,7 @@ TEST_F(LabelTest, ObscuredSurrogatePair) { label()->SizeToPreferredSize(); EXPECT_EQ(base::string16(1, gfx::RenderText::kPasswordReplacementChar), label()->GetDisplayTextForTesting()); - EXPECT_EQ(test_text, label()->text()); + EXPECT_EQ(test_text, label()->GetText()); } // This test case verifies the label preferred size will change based on the @@ -456,7 +456,7 @@ TEST_F(LabelTest, TooltipProperty) { // Initially, label has no bounds, its text does not fit, and therefore its // text should be returned as the tooltip text. - EXPECT_EQ(label()->text(), label()->GetTooltipText(gfx::Point())); + EXPECT_EQ(label()->GetText(), label()->GetTooltipText(gfx::Point())); // While tooltip handling is disabled, GetTooltipText() should fail. label()->SetHandlesTooltips(false); @@ -477,7 +477,7 @@ TEST_F(LabelTest, TooltipProperty) { // When the tooltip text is set to an empty string, the original behavior is // restored. label()->SetTooltipText(base::string16()); - EXPECT_EQ(label()->text(), label()->GetTooltipText(gfx::Point())); + EXPECT_EQ(label()->GetText(), label()->GetTooltipText(gfx::Point())); // While tooltip handling is disabled, GetTooltipText() should fail. label()->SetHandlesTooltips(false); @@ -531,7 +531,7 @@ TEST_F(LabelTest, Accessibility) { ui::AXNodeData node_data; label()->GetAccessibleNodeData(&node_data); EXPECT_EQ(ax::mojom::Role::kStaticText, node_data.role); - EXPECT_EQ(label()->text(), + EXPECT_EQ(label()->GetText(), node_data.GetString16Attribute(ax::mojom::StringAttribute::kName)); EXPECT_FALSE( node_data.HasIntAttribute(ax::mojom::IntAttribute::kRestriction)); @@ -557,7 +557,7 @@ TEST_F(LabelTest, TextChangeWithoutLayout) { TEST_F(LabelTest, EmptyLabelSizing) { const gfx::Size expected_size(0, label()->font_list().GetHeight()); EXPECT_EQ(expected_size, label()->GetPreferredSize()); - label()->SetMultiLine(!label()->multi_line()); + label()->SetMultiLine(!label()->GetMultiLine()); EXPECT_EQ(expected_size, label()->GetPreferredSize()); } @@ -768,7 +768,7 @@ TEST_F(LabelTest, GetTooltipHandlerForPoint) { label()->SetBounds(0, 0, 10, 10); // By default, labels start out as tooltip handlers. - ASSERT_TRUE(label()->handles_tooltips()); + ASSERT_TRUE(label()->GetHandlesTooltips()); // There's a default tooltip if the text is too big to fit. EXPECT_EQ(label(), label()->GetTooltipHandlerForPoint(gfx::Point(2, 2))); @@ -827,7 +827,7 @@ TEST_F(LabelTest, ResetRenderTextData) { // Querying fields or size information should not recompute the layout // unnecessarily. - EXPECT_EQ(ASCIIToUTF16("Example"), label()->text()); + EXPECT_EQ(ASCIIToUTF16("Example"), label()->GetText()); EXPECT_FALSE(label()->display_text_); EXPECT_EQ(preferred_size, label()->GetPreferredSize()); @@ -879,7 +879,7 @@ TEST_F(LabelTest, NoSchedulePaintInOnPaint) { label.SetEnabled(false); EXPECT_TRUE(Increased(label.schedule_paint_count(), &count)); - label.SetText(label.text() + ASCIIToUTF16("Changed")); + label.SetText(label.GetText() + ASCIIToUTF16("Changed")); EXPECT_TRUE(Increased(label.schedule_paint_count(), &count)); label.SizeToPreferredSize(); @@ -946,20 +946,20 @@ TEST_F(LabelTest, DefaultDirectionalityIsFromText) { TEST_F(LabelSelectionTest, Selectable) { // By default, labels don't support text selection. - EXPECT_FALSE(label()->selectable()); + EXPECT_FALSE(label()->GetSelectable()); ASSERT_TRUE(label()->SetSelectable(true)); - EXPECT_TRUE(label()->selectable()); + EXPECT_TRUE(label()->GetSelectable()); // Verify that making a label multiline still causes the label to support text // selection. label()->SetMultiLine(true); - EXPECT_TRUE(label()->selectable()); + EXPECT_TRUE(label()->GetSelectable()); // Verify that obscuring the label text causes the label to not support text // selection. label()->SetObscured(true); - EXPECT_FALSE(label()->selectable()); + EXPECT_FALSE(label()->GetSelectable()); } // Verify that labels supporting text selection get focus on clicks. @@ -1022,7 +1022,7 @@ TEST_F(LabelSelectionTest, DoubleTripleClick) { // Triple clicking should select all the text. PerformClick(GetCursorPoint(0)); - EXPECT_EQ(label()->text(), GetSelectedText()); + EXPECT_EQ(label()->GetText(), GetSelectedText()); // Clicking again should alternate to double click. PerformClick(GetCursorPoint(0)); @@ -1053,7 +1053,7 @@ TEST_F(LabelSelectionTest, MouseDrag) { EXPECT_STR_EQ(" mouse drag", GetSelectedText()); event_generator()->PressKey(ui::VKEY_C, kControlCommandModifier); - EXPECT_STR_EQ(" mouse drag", GetClipboardText(ui::CLIPBOARD_TYPE_COPY_PASTE)); + EXPECT_STR_EQ(" mouse drag", GetClipboardText(ui::ClipboardType::kCopyPaste)); } TEST_F(LabelSelectionTest, MouseDragMultilineLTR) { @@ -1138,7 +1138,8 @@ TEST_F(LabelSelectionTest, MouseDragMultilineRTL) { label()->SetMultiLine(true); label()->SetText(ToRTL("012\n345")); // Sanity check. - EXPECT_EQ(WideToUTF16(L"\x5d0\x5d1\x5d2\n\x5d3\x5d4\x5d5"), label()->text()); + EXPECT_EQ(WideToUTF16(L"\x5d0\x5d1\x5d2\n\x5d3\x5d4\x5d5"), + label()->GetText()); label()->SizeToPreferredSize(); ASSERT_TRUE(label()->SetSelectable(true)); @@ -1249,14 +1250,14 @@ TEST_F(LabelSelectionTest, SelectionClipboard) { // selection clipboard. label()->SelectRange(gfx::Range(2, 5)); EXPECT_STR_EQ("bel", GetSelectedText()); - EXPECT_TRUE(GetClipboardText(ui::CLIPBOARD_TYPE_SELECTION).empty()); + EXPECT_TRUE(GetClipboardText(ui::ClipboardType::kSelection).empty()); // Verify text selection using the mouse updates the selection clipboard. PerformMousePress(GetCursorPoint(5)); PerformMouseDragTo(GetCursorPoint(0)); PerformMouseRelease(GetCursorPoint(0)); EXPECT_STR_EQ("Label", GetSelectedText()); - EXPECT_STR_EQ("Label", GetClipboardText(ui::CLIPBOARD_TYPE_SELECTION)); + EXPECT_STR_EQ("Label", GetClipboardText(ui::ClipboardType::kSelection)); } #endif @@ -1275,7 +1276,7 @@ TEST_F(LabelSelectionTest, KeyboardActions) { EXPECT_EQ(initial_text, GetSelectedText()); event_generator()->PressKey(ui::VKEY_C, kControlCommandModifier); - EXPECT_EQ(initial_text, GetClipboardText(ui::CLIPBOARD_TYPE_COPY_PASTE)); + EXPECT_EQ(initial_text, GetClipboardText(ui::ClipboardType::kCopyPaste)); // The selection should get cleared on changing the text, but focus should not // be affected. @@ -1286,7 +1287,7 @@ TEST_F(LabelSelectionTest, KeyboardActions) { // Obscured labels do not support text selection. label()->SetObscured(true); - EXPECT_FALSE(label()->selectable()); + EXPECT_FALSE(label()->GetSelectable()); event_generator()->PressKey(ui::VKEY_A, kControlCommandModifier); EXPECT_EQ(base::string16(), GetSelectedText()); } @@ -1318,7 +1319,7 @@ TEST_F(LabelSelectionTest, ContextMenuContents) { // 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()->selectable()); + EXPECT_FALSE(label()->GetSelectable()); EXPECT_FALSE(IsMenuCommandEnabled(IDS_APP_COPY)); EXPECT_FALSE(IsMenuCommandEnabled(IDS_APP_SELECT_ALL)); label()->SetObscured(false); diff --git a/chromium/ui/views/controls/link.cc b/chromium/ui/views/controls/link.cc index 476a77af1ca..9b200179b8a 100644 --- a/chromium/ui/views/controls/link.cc +++ b/chromium/ui/views/controls/link.cc @@ -23,7 +23,6 @@ namespace views { -const char Link::kViewClassName[] = "Link"; constexpr int Link::kFocusBorderPadding; Link::Link(const base::string16& title, int text_context, int text_style) @@ -61,16 +60,12 @@ gfx::Insets Link::GetInsets() const { gfx::Insets insets = Label::GetInsets(); if (GetFocusStyle() == FocusStyle::RING && GetFocusBehavior() != FocusBehavior::NEVER) { - DCHECK(!text().empty()); + DCHECK(!GetText().empty()); insets += gfx::Insets(kFocusBorderPadding); } return insets; } -const char* Link::GetClassName() const { - return kViewClassName; -} - gfx::NativeCursor Link::GetCursor(const ui::MouseEvent& event) { if (!GetEnabled()) return gfx::kNullCursor; @@ -253,7 +248,7 @@ void Link::RecalculateFont() { void Link::ConfigureFocus() { // Disable focusability for empty links. - if (text().empty()) { + if (GetText().empty()) { SetFocusBehavior(FocusBehavior::NEVER); } else { #if defined(OS_MACOSX) @@ -279,4 +274,8 @@ SkColor Link::GetColor() { : ui::NativeTheme::kColorId_LinkEnabled); } +BEGIN_METADATA(Link) +METADATA_PARENT_CLASS(Label) +END_METADATA() + } // namespace views diff --git a/chromium/ui/views/controls/link.h b/chromium/ui/views/controls/link.h index 3379ee9bd54..8f4f6af95f4 100644 --- a/chromium/ui/views/controls/link.h +++ b/chromium/ui/views/controls/link.h @@ -26,7 +26,7 @@ class LinkListener; //////////////////////////////////////////////////////////////////////////////// class VIEWS_EXPORT Link : public Label { public: - static const char kViewClassName[]; + METADATA_HEADER(Link); // The padding for the focus ring border when rendering a focused Link with // FocusStyle::RING. @@ -56,7 +56,6 @@ class VIEWS_EXPORT Link : public Label { // Label: void PaintFocusRing(gfx::Canvas* canvas) const override; gfx::Insets GetInsets() const override; - const char* GetClassName() const override; gfx::NativeCursor GetCursor(const ui::MouseEvent& event) override; bool CanProcessEventsWithinSubtree() const override; bool OnMousePressed(const ui::MouseEvent& event) override; diff --git a/chromium/ui/views/controls/menu/menu_closure_animation_mac.h b/chromium/ui/views/controls/menu/menu_closure_animation_mac.h index e966edd9888..fecbe83fc41 100644 --- a/chromium/ui/views/controls/menu/menu_closure_animation_mac.h +++ b/chromium/ui/views/controls/menu/menu_closure_animation_mac.h @@ -6,6 +6,7 @@ #define UI_VIEWS_CONTROLS_MENU_MENU_CLOSURE_ANIMATION_MAC_H_ #include "base/macros.h" +#include "base/memory/weak_ptr.h" #include "base/timer/timer.h" #include "ui/gfx/animation/animation.h" #include "ui/gfx/animation/animation_delegate.h" @@ -29,7 +30,9 @@ class SubmenuView; // This class also supports animating a menu away without animating the // selection effect, which is achieved by passing nullptr for the item to // animate. In this case, the animation skips straight to step 3 above. -class VIEWS_EXPORT MenuClosureAnimationMac : public gfx::AnimationDelegate { +class VIEWS_EXPORT MenuClosureAnimationMac + : public gfx::AnimationDelegate, + public base::SupportsWeakPtr<MenuClosureAnimationMac> { public: // After this closure animation is done, |callback| is run to finish // dismissing the menu. If |item| is given, this will animate the item being 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 1ebd33f83c6..b4d8b6fd11f 100644 --- a/chromium/ui/views/controls/menu/menu_closure_animation_mac.mm +++ b/chromium/ui/views/controls/menu/menu_closure_animation_mac.mm @@ -39,7 +39,7 @@ void MenuClosureAnimationMac::Start() { step_ = AnimationStep::kFading; base::ThreadTaskRunnerHandle::Get()->PostTask( FROM_HERE, base::BindOnce(&MenuClosureAnimationMac::AdvanceAnimation, - base::Unretained(this))); + AsWeakPtr())); return; } AdvanceAnimation(); diff --git a/chromium/ui/views/controls/menu/menu_closure_animation_mac_unittest.cc b/chromium/ui/views/controls/menu/menu_closure_animation_mac_unittest.cc new file mode 100644 index 00000000000..ef40b47dc52 --- /dev/null +++ b/chromium/ui/views/controls/menu/menu_closure_animation_mac_unittest.cc @@ -0,0 +1,28 @@ +// 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. + +#include "ui/views/controls/menu/menu_closure_animation_mac.h" + +#include "base/test/bind_test_util.h" +#include "base/test/scoped_task_environment.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "ui/views/test/menu_test_utils.h" + +TEST(MenuClosureAnimationMacTest, DestructCancelsCleanly) { + views::test::DisableMenuClosureAnimations(); + base::test::ScopedTaskEnvironment environment; + + bool called = false; + auto animation = std::make_unique<views::MenuClosureAnimationMac>( + nullptr, nullptr, base::BindLambdaForTesting([&]() { called = true; })); + + animation->Start(); + animation.reset(); + + // If the animation callback runs after the animation is destroyed, this line + // may crash; if not, |called| will get set to true and fail the test below. + views::test::WaitForMenuClosureAnimation(); + + EXPECT_FALSE(called); +} diff --git a/chromium/ui/views/controls/menu/menu_cocoa_watcher_mac.h b/chromium/ui/views/controls/menu/menu_cocoa_watcher_mac.h index 96e55221033..f375ae5cc00 100644 --- a/chromium/ui/views/controls/menu/menu_cocoa_watcher_mac.h +++ b/chromium/ui/views/controls/menu/menu_cocoa_watcher_mac.h @@ -13,20 +13,26 @@ namespace views { -// This class executes a callback when a native menu begins tracking. With -// native menus, each one automatically closes when a new one begins tracking. -// This allows Views menus to tie into this behavior. +// This class executes a callback when a native menu begins tracking, or when a +// new window takes focus. With native menus, each one automatically closes when +// a new one begins tracking, and MenuPreTargetHandlerAura::OnWindowActivated() +// closes menus when new windows take focus. This allows Views menus to have the +// correct behavior. class VIEWS_EXPORT MenuCocoaWatcherMac { public: explicit MenuCocoaWatcherMac(base::OnceClosure callback); ~MenuCocoaWatcherMac(); private: + void ExecuteCallback(); + // The closure to call when the notification comes in. base::OnceClosure callback_; - // The token representing the notification observer. - id observer_token_; + // Tokens representing the notification observers. + id observer_token_other_menu_; + id observer_token_new_window_focus_; + id observer_token_app_change_; DISALLOW_COPY_AND_ASSIGN(MenuCocoaWatcherMac); }; diff --git a/chromium/ui/views/controls/menu/menu_cocoa_watcher_mac.mm b/chromium/ui/views/controls/menu/menu_cocoa_watcher_mac.mm index da563e6a4cd..c5d53055a4e 100644 --- a/chromium/ui/views/controls/menu/menu_cocoa_watcher_mac.mm +++ b/chromium/ui/views/controls/menu/menu_cocoa_watcher_mac.mm @@ -5,6 +5,7 @@ #include "ui/views/controls/menu/menu_cocoa_watcher_mac.h" #import <Cocoa/Cocoa.h> +#include <dispatch/dispatch.h> #import <utility> @@ -12,17 +13,46 @@ namespace views { MenuCocoaWatcherMac::MenuCocoaWatcherMac(base::OnceClosure callback) : callback_(std::move(callback)) { - observer_token_ = [[NSNotificationCenter defaultCenter] + observer_token_other_menu_ = [[NSNotificationCenter defaultCenter] addObserverForName:NSMenuDidBeginTrackingNotification object:[NSApp mainMenu] queue:nil usingBlock:^(NSNotification* notification) { - std::move(this->callback_).Run(); + ExecuteCallback(); }]; + observer_token_new_window_focus_ = [[NSNotificationCenter defaultCenter] + addObserverForName:NSWindowDidBecomeKeyNotification + object:nil + queue:nil + usingBlock:^(NSNotification* notification) { + ExecuteCallback(); + }]; + observer_token_app_change_ = + [[[NSWorkspace sharedWorkspace] notificationCenter] + addObserverForName:NSWorkspaceDidActivateApplicationNotification + object:nil + queue:nil + usingBlock:^(NSNotification* notification) { + ExecuteCallback(); + }]; } MenuCocoaWatcherMac::~MenuCocoaWatcherMac() { - [[NSNotificationCenter defaultCenter] removeObserver:observer_token_]; + [[NSNotificationCenter defaultCenter] + removeObserver:observer_token_other_menu_]; + [[NSNotificationCenter defaultCenter] + removeObserver:observer_token_new_window_focus_]; + [[[NSWorkspace sharedWorkspace] notificationCenter] + removeObserver:observer_token_app_change_]; +} + +void MenuCocoaWatcherMac::ExecuteCallback() { + __block base::OnceClosure callback = std::move(callback_); + dispatch_async(dispatch_get_main_queue(), ^{ + if (callback) { + std::move(callback).Run(); + } + }); } } // namespace views diff --git a/chromium/ui/views/controls/menu/menu_config_mac.mm b/chromium/ui/views/controls/menu/menu_config_mac.mm index 607ed754986..1de2eb9c7ec 100644 --- a/chromium/ui/views/controls/menu/menu_config_mac.mm +++ b/chromium/ui/views/controls/menu/menu_config_mac.mm @@ -33,6 +33,8 @@ void InitMaterialMenuConfig(views::MenuConfig* config) { config->icons_in_label = true; config->corner_radius = 8; config->auxiliary_corner_radius = 4; + config->item_top_margin = 4; + config->item_bottom_margin = 4; } } // namespace diff --git a/chromium/ui/views/controls/menu/menu_controller.cc b/chromium/ui/views/controls/menu/menu_controller.cc index 3ae7da55cf1..bceec6f1721 100644 --- a/chromium/ui/views/controls/menu/menu_controller.cc +++ b/chromium/ui/views/controls/menu/menu_controller.cc @@ -475,7 +475,7 @@ void MenuController::Run(Widget* parent, #if defined(OS_MACOSX) menu_cocoa_watcher_ = std::make_unique<MenuCocoaWatcherMac>(base::BindOnce( - &MenuController::Cancel, base::Unretained(this), ExitType::kAll)); + &MenuController::Cancel, this->AsWeakPtr(), ExitType::kAll)); #endif // Reset current state. @@ -982,7 +982,7 @@ int MenuController::OnDragUpdated(SubmenuView* source, if (menu_item) over_empty_menu = true; } - MenuDelegate::DropPosition drop_position = MenuDelegate::DROP_NONE; + MenuDelegate::DropPosition drop_position = MenuDelegate::DropPosition::kNone; int drop_operation = ui::DragDropTypes::DRAG_NONE; if (menu_item) { gfx::Point menu_item_loc(event.location()); @@ -993,16 +993,16 @@ int MenuController::OnDragUpdated(SubmenuView* source, if (menu_item->HasSubmenu() && (menu_item_loc.y() > kDropBetweenPixels && menu_item_loc.y() < (menu_item_height - kDropBetweenPixels))) { - drop_position = MenuDelegate::DROP_ON; + drop_position = MenuDelegate::DropPosition::kOn; } else { drop_position = (menu_item_loc.y() < menu_item_height / 2) - ? MenuDelegate::DROP_BEFORE - : MenuDelegate::DROP_AFTER; + ? MenuDelegate::DropPosition::kBefore + : MenuDelegate::DropPosition::kAfter; } query_menu_item = menu_item; } else { query_menu_item = menu_item->GetParentMenuItem(); - drop_position = MenuDelegate::DROP_ON; + drop_position = MenuDelegate::DropPosition::kOn; } drop_operation = menu_item->GetDelegate()->GetDropOperation( query_menu_item, event, &drop_position); @@ -1011,7 +1011,7 @@ int MenuController::OnDragUpdated(SubmenuView* source, SetSelection(menu_item, menu_item->HasSubmenu() ? SELECTION_OPEN_SUBMENU : SELECTION_DEFAULT); - if (drop_position == MenuDelegate::DROP_NONE || + if (drop_position == MenuDelegate::DropPosition::kNone || drop_operation == ui::DragDropTypes::DRAG_NONE) menu_item = nullptr; } else { @@ -1027,7 +1027,7 @@ void MenuController::OnDragExited(SubmenuView* source) { if (drop_target_) { StopShowTimer(); - SetDropMenuItem(nullptr, MenuDelegate::DROP_NONE); + SetDropMenuItem(nullptr, MenuDelegate::DropPosition::kNone); } } @@ -1075,14 +1075,14 @@ void MenuController::OnDragEnteredScrollButton(SubmenuView* source, UpdateScrolling(part); // Do this to force the selection to hide. - SetDropMenuItem(source->GetMenuItemAt(0), MenuDelegate::DROP_NONE); + SetDropMenuItem(source->GetMenuItemAt(0), MenuDelegate::DropPosition::kNone); StopCancelAllTimer(); } void MenuController::OnDragExitedScrollButton(SubmenuView* source) { StartCancelAllTimer(); - SetDropMenuItem(nullptr, MenuDelegate::DROP_NONE); + SetDropMenuItem(nullptr, MenuDelegate::DropPosition::kNone); StopScrolling(); } @@ -1138,30 +1138,35 @@ ui::PostDispatchAction MenuController::OnWillDispatchKeyEvent( base::WeakPtr<MenuController> this_ref = AsWeakPtr(); if (event->type() == ui::ET_KEY_PRESSED) { + bool key_handled = false; #if defined(OS_MACOSX) // Special handling for Option-Up and Option-Down, which should behave like // Home and End respectively in menus. if ((event->flags() & ui::EF_ALT_DOWN)) { if (event->key_code() == ui::VKEY_UP) { - OnKeyDown(ui::VKEY_HOME); + key_handled = OnKeyPressed(ui::VKEY_HOME); } else if (event->key_code() == ui::VKEY_DOWN) { - OnKeyDown(ui::VKEY_END); + key_handled = OnKeyPressed(ui::VKEY_END); } else { - OnKeyDown(event->key_code()); + key_handled = OnKeyPressed(event->key_code()); } } else { - OnKeyDown(event->key_code()); + key_handled = OnKeyPressed(event->key_code()); } #else - OnKeyDown(event->key_code()); + key_handled = OnKeyPressed(event->key_code()); #endif + + if (key_handled) + event->StopPropagation(); + // Key events can lead to this being deleted. if (!this_ref) { event->StopPropagation(); return ui::POST_DISPATCH_NONE; } - if (!IsEditableCombobox()) { + if (!IsEditableCombobox() && !event->stopped_propagation()) { // Do not check mnemonics if the Alt or Ctrl modifiers are pressed. For // example Ctrl+<T> is an accelerator, but <T> only is a mnemonic. const int kKeyFlagsMask = ui::EF_CONTROL_DOWN | ui::EF_ALT_DOWN; @@ -1402,16 +1407,17 @@ void MenuController::StartDrag(SubmenuView* source, item->PaintButton(&canvas, MenuItemView::PB_FOR_DRAG); gfx::ImageSkia image(gfx::ImageSkiaRep(canvas.GetBitmap(), raster_scale)); - OSExchangeData data; - item->GetDelegate()->WriteDragData(item, &data); - data.provider().SetDragImage(image, press_loc.OffsetFromOrigin()); + std::unique_ptr<OSExchangeData> data(std::make_unique<OSExchangeData>()); + item->GetDelegate()->WriteDragData(item, data.get()); + data->provider().SetDragImage(image, press_loc.OffsetFromOrigin()); StopScrolling(); int drag_ops = item->GetDelegate()->GetDragOperations(item); did_initiate_drag_ = true; base::WeakPtr<MenuController> this_ref = AsWeakPtr(); // TODO(varunjain): Properly determine and send DRAG_EVENT_SOURCE below. - item->GetWidget()->RunShellDrag(nullptr, data, widget_loc, drag_ops, + item->GetWidget()->RunShellDrag(nullptr, std::move(data), widget_loc, + drag_ops, ui::DragDropTypes::DRAG_EVENT_SOURCE_MOUSE); // MenuController may have been deleted so check before accessing member // variables. @@ -1419,10 +1425,12 @@ void MenuController::StartDrag(SubmenuView* source, did_initiate_drag_ = false; } -void MenuController::OnKeyDown(ui::KeyboardCode key_code) { +bool MenuController::OnKeyPressed(ui::KeyboardCode key_code) { // Do not process while performing drag-and-drop if (for_drop_) - return; + return false; + + bool handled_key_code = false; switch (key_code) { case ui::VKEY_HOME: @@ -1499,6 +1507,7 @@ void MenuController::OnKeyDown(ui::KeyboardCode key_code) { else OpenSubmenuChangeSelectionIfCan(); } else { + handled_key_code = true; if (!SendAcceleratorToHotTrackedView() && pending_state_.item->GetEnabled()) { Accept(pending_state_.item, 0); @@ -1555,6 +1564,7 @@ void MenuController::OnKeyDown(ui::KeyboardCode key_code) { default: break; } + return handled_key_code; } MenuController::MenuController(bool for_drop, @@ -2747,7 +2757,7 @@ void MenuController::SetDropMenuItem(MenuItemView* new_target, if (drop_target_) { drop_target_->GetParentMenuItem()->GetSubmenu()->SetDropMenuItem( - nullptr, MenuDelegate::DROP_NONE); + nullptr, MenuDelegate::DropPosition::kNone); } drop_target_ = new_target; drop_position_ = new_position; diff --git a/chromium/ui/views/controls/menu/menu_controller.h b/chromium/ui/views/controls/menu/menu_controller.h index c65b0ac30b1..1b12cd52e93 100644 --- a/chromium/ui/views/controls/menu/menu_controller.h +++ b/chromium/ui/views/controls/menu/menu_controller.h @@ -350,8 +350,9 @@ class VIEWS_EXPORT MenuController const ui::LocatedEvent* event); void StartDrag(SubmenuView* source, const gfx::Point& location); - // Key processing. - void OnKeyDown(ui::KeyboardCode key_code); + // Handles |key_code| as a keypress. Returns true if OnKeyPressed handled the + // key code. + bool OnKeyPressed(ui::KeyboardCode key_code); // Creates a MenuController. See |for_drop_| member for details on |for_drop|. MenuController(bool for_drop, internal::MenuControllerDelegate* delegate); @@ -677,7 +678,8 @@ class VIEWS_EXPORT MenuController // Drop target. MenuItemView* drop_target_ = nullptr; - MenuDelegate::DropPosition drop_position_ = MenuDelegate::DROP_UNKNOWN; + MenuDelegate::DropPosition drop_position_ = + MenuDelegate::DropPosition::kUnknow; // Owner of child windows. // WARNING: this may be NULL. @@ -701,7 +703,7 @@ class VIEWS_EXPORT MenuController // continually processing whether we can drop, we cache the coordinates. bool valid_drop_coordinates_ = false; gfx::Point drop_pt_; - int last_drop_operation_ = MenuDelegate::DROP_UNKNOWN; + int last_drop_operation_ = ui::DragDropTypes::DRAG_NONE; // If true, we're in the middle of invoking ShowAt on a submenu. bool showing_submenu_ = false; diff --git a/chromium/ui/views/controls/menu/menu_controller_unittest.cc b/chromium/ui/views/controls/menu/menu_controller_unittest.cc index 57031c8b3f9..ce0b8a3f65d 100644 --- a/chromium/ui/views/controls/menu/menu_controller_unittest.cc +++ b/chromium/ui/views/controls/menu/menu_controller_unittest.cc @@ -184,7 +184,7 @@ class TestDragDropClient : public aura::client::DragDropClient { ~TestDragDropClient() override = default; // aura::client::DragDropClient: - int StartDragAndDrop(const ui::OSExchangeData& data, + int StartDragAndDrop(std::unique_ptr<ui::OSExchangeData> data, aura::Window* root_window, aura::Window* source_window, const gfx::Point& screen_location, @@ -205,7 +205,7 @@ class TestDragDropClient : public aura::client::DragDropClient { }; int TestDragDropClient::StartDragAndDrop( - const ui::OSExchangeData& data, + std::unique_ptr<ui::OSExchangeData> data, aura::Window* root_window, aura::Window* source_window, const gfx::Point& screen_location, @@ -1291,7 +1291,7 @@ TEST_F(MenuControllerTest, AsynchronousPerformDrop) { SubmenuView* source = menu_item()->GetSubmenu(); MenuItemView* target = source->GetMenuItemAt(0); - SetDropMenuItem(target, MenuDelegate::DropPosition::DROP_AFTER); + SetDropMenuItem(target, MenuDelegate::DropPosition::kAfter); ui::OSExchangeData drop_data; gfx::PointF location(target->origin()); @@ -1898,14 +1898,7 @@ TEST_F(MenuControllerTest, AsynchronousCancelEvent) { } // Tests that menus without parent widgets do not crash in MenuPreTargetHandler. -// This is generally true, except on Chrome OS running with the window service. -// In that case, a DCHECK fires to ensure menus can consume parents' key events. TEST_F(MenuControllerTest, RunWithoutWidgetDoesntCrash) { -#if defined(OS_CHROMEOS) - if (::features::IsUsingWindowService()) - return; -#endif // OS_CHROMEOS - ExitMenuRun(); MenuController* controller = menu_controller(); controller->Run(nullptr, nullptr, menu_item(), gfx::Rect(), @@ -2257,12 +2250,6 @@ TEST_F(MenuControllerTest, SetSelectionIndices_Buttons_SkipHiddenAndDisabled) { } TEST_F(MenuControllerTest, SetSelectionIndices_NestedButtons) { - class DummyButtonListener : public ButtonListener { - public: - ~DummyButtonListener() override = default; - void ButtonPressed(Button* sender, const ui::Event& event) override {} - }; - MenuItemView* const item1 = menu_item()->GetSubmenu()->GetMenuItemAt(0); MenuItemView* const item2 = menu_item()->GetSubmenu()->GetMenuItemAt(1); MenuItemView* const item3 = menu_item()->GetSubmenu()->GetMenuItemAt(2); @@ -2277,13 +2264,11 @@ TEST_F(MenuControllerTest, SetSelectionIndices_NestedButtons) { container_view->AddChildView(new Label()); // Add two focusable buttons (buttons in menus are always focusable). - Button* const button1 = - new LabelButton(new DummyButtonListener(), base::string16()); + Button* const button1 = new LabelButton(nullptr, base::string16()); button1->SetFocusBehavior(View::FocusBehavior::ALWAYS); button1->GetViewAccessibility().OverrideRole(ax::mojom::Role::kMenuItem); container_view->AddChildView(button1); - Button* const button2 = - new LabelButton(new DummyButtonListener(), base::string16()); + Button* const button2 = new LabelButton(nullptr, base::string16()); button2->GetViewAccessibility().OverrideRole(ax::mojom::Role::kMenuItem); button2->SetFocusBehavior(View::FocusBehavior::ALWAYS); container_view->AddChildView(button2); diff --git a/chromium/ui/views/controls/menu/menu_delegate.h b/chromium/ui/views/controls/menu/menu_delegate.h index 921aef245bf..bdbd9c7e37b 100644 --- a/chromium/ui/views/controls/menu/menu_delegate.h +++ b/chromium/ui/views/controls/menu/menu_delegate.h @@ -45,20 +45,20 @@ class VIEWS_EXPORT MenuDelegate { public: // Used during drag and drop to indicate where the drop indicator should // be rendered. - enum DropPosition { - DROP_UNKNOWN = -1, + enum class DropPosition { + kUnknow = -1, // Indicates a drop is not allowed here. - DROP_NONE, + kNone, // Indicates the drop should occur before the item. - DROP_BEFORE, + kBefore, // Indicates the drop should occur after the item. - DROP_AFTER, + kAfter, // Indicates the drop should occur on the item. - DROP_ON + kOn }; // Used when indicating the style for a given label. diff --git a/chromium/ui/views/controls/menu/menu_image_util.cc b/chromium/ui/views/controls/menu/menu_image_util.cc index d1cc32bc757..80673906f76 100644 --- a/chromium/ui/views/controls/menu/menu_image_util.cc +++ b/chromium/ui/views/controls/menu/menu_image_util.cc @@ -15,16 +15,6 @@ gfx::ImageSkia GetMenuCheckImage(SkColor icon_color) { return gfx::CreateVectorIcon(kMenuCheckIcon, icon_color); } -gfx::ImageSkia GetRadioButtonImage(bool toggled, - bool hovered, - SkColor default_icon_color) { - const gfx::VectorIcon& icon = - toggled ? kMenuRadioSelectedIcon : kMenuRadioEmptyIcon; - SkColor color = - toggled && !hovered ? gfx::kGoogleBlue500 : default_icon_color; - return gfx::CreateVectorIcon(icon, kMenuCheckSize, color); -} - gfx::ImageSkia GetSubmenuArrowImage(SkColor icon_color) { return gfx::CreateVectorIcon(kSubmenuArrowIcon, icon_color); } diff --git a/chromium/ui/views/controls/menu/menu_image_util.h b/chromium/ui/views/controls/menu/menu_image_util.h index bda82266bbf..10713e0bc13 100644 --- a/chromium/ui/views/controls/menu/menu_image_util.h +++ b/chromium/ui/views/controls/menu/menu_image_util.h @@ -17,14 +17,6 @@ constexpr int kSubmenuArrowSize = 8; // Returns the Menu Check box image (always checked). gfx::ImageSkia GetMenuCheckImage(SkColor icon_color); -// Return the RadioButton image for given state. |toggled| is true when -// the radio option is active, |hovered| describes the menu higlight/selection -// state, and |default_icon_color| is the base color that should be used for -// the icon (which may be ignored based on the other two flags). -gfx::ImageSkia GetRadioButtonImage(bool toggled, - bool hovered, - SkColor default_icon_color); - // Returns the image for submenu arrow for current RTL setting. gfx::ImageSkia GetSubmenuArrowImage(SkColor icon_color); diff --git a/chromium/ui/views/controls/menu/menu_item_view.cc b/chromium/ui/views/controls/menu/menu_item_view.cc index 1e27d88823a..c8a32b58cf7 100644 --- a/chromium/ui/views/controls/menu/menu_item_view.cc +++ b/chromium/ui/views/controls/menu/menu_item_view.cc @@ -40,6 +40,7 @@ #include "ui/views/controls/menu/menu_separator.h" #include "ui/views/controls/menu/submenu_view.h" #include "ui/views/controls/separator.h" +#include "ui/views/vector_icons.h" #include "ui/views/view_class_properties.h" #include "ui/views/widget/widget.h" @@ -98,9 +99,6 @@ int MenuItemView::item_right_margin_; // static int MenuItemView::pref_menu_height_; -// static -const char MenuItemView::kViewClassName[] = "MenuItemView"; - MenuItemView::MenuItemView(MenuDelegate* delegate) : delegate_(delegate), controller_(nullptr), @@ -765,10 +763,6 @@ MenuItemView::~MenuItemView() { delete item; } -const char* MenuItemView::GetClassName() const { - return kViewClassName; -} - // Calculates all sizes that we can from the OS. // // This is invoked prior to Running a menu. @@ -981,8 +975,14 @@ void MenuItemView::PaintButton(gfx::Canvas* canvas, PaintButtonMode mode) { if (type_ == CHECKBOX && delegate->IsItemChecked(GetCommand())) { radio_check_image_view_->SetImage(GetMenuCheckImage(icon_color)); } else if (type_ == RADIO) { - radio_check_image_view_->SetImage(GetRadioButtonImage( - delegate->IsItemChecked(GetCommand()), render_selection, icon_color)); + const bool toggled = delegate->IsItemChecked(GetCommand()); + const gfx::VectorIcon& radio_icon = + toggled ? kMenuRadioSelectedIcon : kMenuRadioEmptyIcon; + const SkColor radio_icon_color = GetNativeTheme()->GetSystemColor( + toggled ? ui::NativeTheme::kColorId_ProminentButtonColor + : ui::NativeTheme::kColorId_ButtonEnabledColor); + radio_check_image_view_->SetImage( + gfx::CreateVectorIcon(radio_icon, kMenuCheckSize, radio_icon_color)); } // Render the foreground. @@ -1405,4 +1405,8 @@ bool MenuItemView::HasChecksOrRadioButtons() const { [](const auto* item) { return item->HasChecksOrRadioButtons(); }); } +BEGIN_METADATA(MenuItemView) +METADATA_PARENT_CLASS(View) +END_METADATA() + } // namespace views diff --git a/chromium/ui/views/controls/menu/menu_item_view.h b/chromium/ui/views/controls/menu/menu_item_view.h index 6d22433f46e..690d6671e9a 100644 --- a/chromium/ui/views/controls/menu/menu_item_view.h +++ b/chromium/ui/views/controls/menu/menu_item_view.h @@ -74,10 +74,9 @@ class SubmenuView; class VIEWS_EXPORT MenuItemView : public View { public: - friend class MenuController; + METADATA_HEADER(MenuItemView); - // The menu item view's class name. - static const char kViewClassName[]; + friend class MenuController; // ID used to identify menu items. static const int kMenuItemViewID; @@ -384,7 +383,6 @@ class VIEWS_EXPORT MenuItemView : public View { // View: void ChildPreferredSizeChanged(View* child) override; - const char* GetClassName() const override; // Returns the preferred size (and padding) of any children. virtual gfx::Size GetChildPreferredSize() const; diff --git a/chromium/ui/views/controls/menu/menu_item_view_unittest.cc b/chromium/ui/views/controls/menu/menu_item_view_unittest.cc index d33c7bc2a66..e745aec4fea 100644 --- a/chromium/ui/views/controls/menu/menu_item_view_unittest.cc +++ b/chromium/ui/views/controls/menu/menu_item_view_unittest.cc @@ -218,7 +218,7 @@ TEST_F(MenuItemViewLayoutTest, ContainerLayoutRespectsMarginsAndPreferredSize) { const gfx::Size child_size(200, 50); const gfx::Insets child_margins(5, 10); child_view->SetPreferredSize(child_size); - child_view->SetProperty(kMarginsKey, new gfx::Insets(child_margins)); + child_view->SetProperty(kMarginsKey, child_margins); PerformLayout(); @@ -267,7 +267,7 @@ TEST_F(MenuItemViewLayoutTest, ContainerLayoutPassesTrueWidth) { FakeView* child_view = test_item()->AddChildView(std::make_unique<FakeView>(child_size.width())); child_view->SetPreferredSize(child_size); - child_view->SetProperty(kMarginsKey, new gfx::Insets(child_margins)); + child_view->SetProperty(kMarginsKey, child_margins); PerformLayout(); diff --git a/chromium/ui/views/controls/menu/menu_pre_target_handler_aura.cc b/chromium/ui/views/controls/menu/menu_pre_target_handler_aura.cc index 2bfd2942c01..94976c23ef5 100644 --- a/chromium/ui/views/controls/menu/menu_pre_target_handler_aura.cc +++ b/chromium/ui/views/controls/menu/menu_pre_target_handler_aura.cc @@ -6,7 +6,6 @@ #include "ui/aura/env.h" #include "ui/aura/window.h" -#include "ui/base/ui_base_features.h" #include "ui/views/controls/menu/menu_controller.h" #include "ui/views/widget/widget.h" #include "ui/wm/public/activation_client.h" @@ -29,11 +28,7 @@ MenuPreTargetHandlerAura::MenuPreTargetHandlerAura(MenuController* controller, root_->AddObserver(this); } else { // This should only happen in cases like when context menus are shown for - // Windows OS system tray items and there is no parent window. This should - // not be hit on Chrome OS, where Window Service clients need to install a - // pre-target handler on the aura::Env associated with their app window. - DCHECK(!::features::IsUsingWindowService()) - << "MenuPreTargetHandlerAura may not work correctly without an owner."; + // Windows OS system tray items and there is no parent window. } aura::Env::GetInstance()->AddPreTargetHandler( this, ui::EventTarget::Priority::kSystem); diff --git a/chromium/ui/views/controls/menu/menu_runner_impl.cc b/chromium/ui/views/controls/menu/menu_runner_impl.cc index f1e43947259..cc93c948eb8 100644 --- a/chromium/ui/views/controls/menu/menu_runner_impl.cc +++ b/chromium/ui/views/controls/menu/menu_runner_impl.cc @@ -42,8 +42,7 @@ MenuRunnerImpl::MenuRunnerImpl(MenuItemView* menu) delete_after_run_(false), for_drop_(false), controller_(nullptr), - owns_controller_(false), - weak_factory_(this) {} + owns_controller_(false) {} bool MenuRunnerImpl::IsRunning() const { return running_; diff --git a/chromium/ui/views/controls/menu/menu_runner_impl.h b/chromium/ui/views/controls/menu/menu_runner_impl.h index 8617acc7383..79f43820be6 100644 --- a/chromium/ui/views/controls/menu/menu_runner_impl.h +++ b/chromium/ui/views/controls/menu/menu_runner_impl.h @@ -94,7 +94,7 @@ class VIEWS_EXPORT MenuRunnerImpl : public MenuRunnerImplInterface, base::TimeTicks closing_event_time_; // Used to detect deletion of |this| when notifying delegate of success. - base::WeakPtrFactory<MenuRunnerImpl> weak_factory_; + base::WeakPtrFactory<MenuRunnerImpl> weak_factory_{this}; DISALLOW_COPY_AND_ASSIGN(MenuRunnerImpl); }; 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 7ae246a53bd..3e7c6460bc1 100644 --- a/chromium/ui/views/controls/menu/menu_scroll_view_container.cc +++ b/chromium/ui/views/controls/menu/menu_scroll_view_container.cc @@ -358,4 +358,8 @@ BubbleBorder::Arrow MenuScrollViewContainer::BubbleBorderTypeFromAnchor( } } +BEGIN_METADATA(MenuScrollViewContainer) +METADATA_PARENT_CLASS(View) +END_METADATA() + } // namespace views 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 24f01963880..6dbb6b9df89 100644 --- a/chromium/ui/views/controls/menu/menu_scroll_view_container.h +++ b/chromium/ui/views/controls/menu/menu_scroll_view_container.h @@ -20,6 +20,8 @@ class SubmenuView; // the preferred height of the SubmenuView is bigger than our bounds. class MenuScrollViewContainer : public View { public: + METADATA_HEADER(MenuScrollViewContainer); + explicit MenuScrollViewContainer(SubmenuView* content_view); // Returns the buttons for scrolling up/down. diff --git a/chromium/ui/views/controls/menu/menu_separator.cc b/chromium/ui/views/controls/menu/menu_separator.cc index aa27d3c260b..b48d80919ce 100644 --- a/chromium/ui/views/controls/menu/menu_separator.cc +++ b/chromium/ui/views/controls/menu/menu_separator.cc @@ -88,4 +88,8 @@ gfx::Size MenuSeparator::CalculatePreferredSize() const { height); } +BEGIN_METADATA(MenuSeparator) +METADATA_PARENT_CLASS(View) +END_METADATA() + } // namespace views diff --git a/chromium/ui/views/controls/menu/menu_separator.h b/chromium/ui/views/controls/menu/menu_separator.h index 4e91f1707e2..a331914d44a 100644 --- a/chromium/ui/views/controls/menu/menu_separator.h +++ b/chromium/ui/views/controls/menu/menu_separator.h @@ -15,6 +15,8 @@ namespace views { class VIEWS_EXPORT MenuSeparator : public View { public: + METADATA_HEADER(MenuSeparator); + explicit MenuSeparator(ui::MenuSeparatorType type) : type_(type) {} // View overrides. diff --git a/chromium/ui/views/controls/menu/submenu_view.cc b/chromium/ui/views/controls/menu/submenu_view.cc index caa47b1ccfd..ecdc432e9cb 100644 --- a/chromium/ui/views/controls/menu/submenu_view.cc +++ b/chromium/ui/views/controls/menu/submenu_view.cc @@ -34,14 +34,11 @@ constexpr SkColor kDropIndicatorColor = SK_ColorBLACK; namespace views { -// static -const char SubmenuView::kViewClassName[] = "SubmenuView"; - SubmenuView::SubmenuView(MenuItemView* parent) : parent_menu_item_(parent), host_(nullptr), drop_item_(nullptr), - drop_position_(MenuDelegate::DROP_NONE), + drop_position_(MenuDelegate::DropPosition::kNone), scroll_view_container_(nullptr), max_minor_text_width_(0), minimum_preferred_width_(0), @@ -216,12 +213,12 @@ void SubmenuView::PaintChildren(const PaintInfo& paint_info) { bool paint_drop_indicator = false; if (drop_item_) { switch (drop_position_) { - case MenuDelegate::DROP_NONE: - case MenuDelegate::DROP_ON: + case MenuDelegate::DropPosition::kNone: + case MenuDelegate::DropPosition::kOn: break; - case MenuDelegate::DROP_UNKNOWN: - case MenuDelegate::DROP_BEFORE: - case MenuDelegate::DROP_AFTER: + case MenuDelegate::DropPosition::kUnknow: + case MenuDelegate::DropPosition::kBefore: + case MenuDelegate::DropPosition::kAfter: paint_drop_indicator = true; break; } @@ -458,7 +455,8 @@ bool SubmenuView::GetShowSelection(MenuItemView* item) { // Something is being dropped on one of this menus items. Show the // selection if the drop is on the passed in item and the drop position is // ON. - return (drop_item_ == item && drop_position_ == MenuDelegate::DROP_ON); + return (drop_item_ == item && + drop_position_ == MenuDelegate::DropPosition::kOn); } MenuScrollViewContainer* SubmenuView::GetScrollViewContainer() { @@ -482,10 +480,6 @@ void SubmenuView::MenuHostDestroyed() { controller->Cancel(MenuController::ExitType::kDestroyed); } -const char* SubmenuView::GetClassName() const { - return kViewClassName; -} - void SubmenuView::OnBoundsChanged(const gfx::Rect& previous_bounds) { SchedulePaint(); } @@ -496,9 +490,9 @@ void SubmenuView::SchedulePaintForDropIndicator( if (item == nullptr) return; - if (position == MenuDelegate::DROP_ON) { + if (position == MenuDelegate::DropPosition::kOn) { item->SchedulePaint(); - } else if (position != MenuDelegate::DROP_NONE) { + } else if (position != MenuDelegate::DropPosition::kNone) { SchedulePaintInRect(CalculateDropIndicatorBounds(item, position)); } } @@ -506,15 +500,15 @@ void SubmenuView::SchedulePaintForDropIndicator( gfx::Rect SubmenuView::CalculateDropIndicatorBounds( MenuItemView* item, MenuDelegate::DropPosition position) { - DCHECK(position != MenuDelegate::DROP_NONE); + DCHECK(position != MenuDelegate::DropPosition::kNone); gfx::Rect item_bounds = item->bounds(); switch (position) { - case MenuDelegate::DROP_BEFORE: + case MenuDelegate::DropPosition::kBefore: item_bounds.Offset(0, -kDropIndicatorHeight / 2); item_bounds.set_height(kDropIndicatorHeight); return item_bounds; - case MenuDelegate::DROP_AFTER: + case MenuDelegate::DropPosition::kAfter: item_bounds.Offset(0, item_bounds.height() - kDropIndicatorHeight / 2); item_bounds.set_height(kDropIndicatorHeight); return item_bounds; @@ -543,4 +537,8 @@ bool SubmenuView::OnScroll(float dx, float dy) { return false; } +BEGIN_METADATA(SubmenuView) +METADATA_PARENT_CLASS(View) +END_METADATA() + } // namespace views diff --git a/chromium/ui/views/controls/menu/submenu_view.h b/chromium/ui/views/controls/menu/submenu_view.h index 3009bd48f73..84ecd3cd6db 100644 --- a/chromium/ui/views/controls/menu/submenu_view.h +++ b/chromium/ui/views/controls/menu/submenu_view.h @@ -44,10 +44,9 @@ class VIEWS_EXPORT SubmenuView : public View, public PrefixDelegate, public ScrollDelegate { public: - using MenuItems = std::vector<MenuItemView*>; + METADATA_HEADER(SubmenuView); - // The submenu's class name. - static const char kViewClassName[]; + using MenuItems = std::vector<MenuItemView*>; // Creates a SubmenuView for the specified menu item. explicit SubmenuView(MenuItemView* parent); @@ -172,8 +171,6 @@ class VIEWS_EXPORT SubmenuView : public View, } protected: - // Overridden from View: - const char* GetClassName() const override; // View method. Overridden to schedule a paint. We do this so that when // scrolling occurs, everything is repainted correctly. diff --git a/chromium/ui/views/controls/message_box_view.cc b/chromium/ui/views/controls/message_box_view.cc index 73b3997b43f..aa9ce75b487 100644 --- a/chromium/ui/views/controls/message_box_view.cc +++ b/chromium/ui/views/controls/message_box_view.cc @@ -72,9 +72,6 @@ namespace views { /////////////////////////////////////////////////////////////////////////////// // MessageBoxView, public: -// static -const char MessageBoxView::kViewClassName[] = "MessageBoxView"; - MessageBoxView::InitParams::InitParams(const base::string16& message) : options(NO_OPTIONS), message(message), @@ -85,7 +82,8 @@ MessageBoxView::InitParams::InitParams(const base::string16& message) MessageBoxView::InitParams::~InitParams() = default; MessageBoxView::MessageBoxView(const InitParams& params) - : message_width_(params.message_width) { + : inter_row_vertical_spacing_(params.inter_row_vertical_spacing), + message_width_(params.message_width) { Init(params); } @@ -168,20 +166,18 @@ bool MessageBoxView::AcceleratorPressed(const ui::Accelerator& accelerator) { // Don't intercept Ctrl-C if we only use a single message label supporting // text selection. - if (message_labels_.size() == 1u && message_labels_[0]->selectable()) + if (message_labels_.size() == 1u && message_labels_[0]->GetSelectable()) return false; - ui::ScopedClipboardWriter scw(ui::CLIPBOARD_TYPE_COPY_PASTE); - scw.WriteText(std::accumulate( - message_labels_.cbegin(), message_labels_.cend(), base::string16(), - [](base::string16& left, Label* right) { return left + right->text(); })); + ui::ScopedClipboardWriter scw(ui::ClipboardType::kCopyPaste); + scw.WriteText(std::accumulate(message_labels_.cbegin(), + message_labels_.cend(), base::string16(), + [](base::string16& left, Label* right) { + return left + right->GetText(); + })); return true; } -const char* MessageBoxView::GetClassName() const { - return kViewClassName; -} - /////////////////////////////////////////////////////////////////////////////// // MessageBoxView, private: @@ -192,8 +188,8 @@ void MessageBoxView::Init(const InitParams& params) { // We explicitly set insets on the message contents instead of the scroll view // so that the scroll view borders are not capped by dialog insets. message_contents->SetBorder(CreateEmptyBorder(GetHorizontalInsets(provider))); - message_contents->SetLayoutManager( - std::make_unique<views::BoxLayout>(views::BoxLayout::kVertical)); + message_contents->SetLayoutManager(std::make_unique<views::BoxLayout>( + views::BoxLayout::Orientation::kVertical)); auto add_label = [&message_contents, this]( const base::string16& text, bool multi_line, gfx::HorizontalAlignment alignment) { @@ -232,15 +228,12 @@ void MessageBoxView::Init(const InitParams& params) { prompt_field_ = AddChildView(std::move(prompt_field)); } - inter_row_vertical_spacing_ = params.inter_row_vertical_spacing; - ResetLayoutManager(); } void MessageBoxView::ResetLayoutManager() { // Initialize the Grid Layout Manager used for this dialog box. - GridLayout* layout = - SetLayoutManager(std::make_unique<views::GridLayout>(this)); + GridLayout* layout = SetLayoutManager(std::make_unique<views::GridLayout>()); // Add the column set for the message displayed at the top of the dialog box. constexpr int kMessageViewColumnSetId = 0; @@ -262,27 +255,27 @@ void MessageBoxView::ResetLayoutManager() { } layout->StartRow(0, kMessageViewColumnSetId); - layout->AddView(scroll_view_); + layout->AddExistingView(scroll_view_); views::DialogContentType trailing_content_type = views::TEXT; if (prompt_field_) { layout->AddPaddingRow(0, inter_row_vertical_spacing_); layout->StartRow(0, kExtraViewColumnSetId); - layout->AddView(prompt_field_); + layout->AddExistingView(prompt_field_); trailing_content_type = views::CONTROL; } if (checkbox_) { layout->AddPaddingRow(0, inter_row_vertical_spacing_); layout->StartRow(0, kExtraViewColumnSetId); - layout->AddView(checkbox_); + layout->AddExistingView(checkbox_); trailing_content_type = views::TEXT; } if (link_) { layout->AddPaddingRow(0, inter_row_vertical_spacing_); layout->StartRow(0, kExtraViewColumnSetId); - layout->AddView(link_); + layout->AddExistingView(link_); trailing_content_type = views::TEXT; } @@ -303,4 +296,8 @@ gfx::Insets MessageBoxView::GetHorizontalInsets( return horizontal_insets; } +BEGIN_METADATA(MessageBoxView) +METADATA_PARENT_CLASS(View) +END_METADATA() + } // namespace views diff --git a/chromium/ui/views/controls/message_box_view.h b/chromium/ui/views/controls/message_box_view.h index 48262a52ba8..9660518cbbf 100644 --- a/chromium/ui/views/controls/message_box_view.h +++ b/chromium/ui/views/controls/message_box_view.h @@ -29,8 +29,7 @@ class Textfield; // and Cancel buttons. class VIEWS_EXPORT MessageBoxView : public View { public: - // Internal class name. - static const char kViewClassName[]; + METADATA_HEADER(MessageBoxView); enum Options { NO_OPTIONS = 0, @@ -94,7 +93,6 @@ class VIEWS_EXPORT MessageBoxView : public View { const ViewHierarchyChangedDetails& details) override; // Handles Ctrl-C and writes the message in the system clipboard. bool AcceleratorPressed(const ui::Accelerator& accelerator) override; - const char* GetClassName() const override; private: // Sets up the layout manager and initializes the message labels and prompt @@ -124,10 +122,10 @@ class VIEWS_EXPORT MessageBoxView : public View { Link* link_ = nullptr; // Spacing between rows in the grid layout. - int inter_row_vertical_spacing_ = 0; + const int inter_row_vertical_spacing_ = 0; // Maximum width of the message label. - int message_width_; + int message_width_ = 0; DISALLOW_COPY_AND_ASSIGN(MessageBoxView); }; diff --git a/chromium/ui/views/controls/native/native_view_host.cc b/chromium/ui/views/controls/native/native_view_host.cc index 9d2c42e2ef4..d6116fdbdd0 100644 --- a/chromium/ui/views/controls/native/native_view_host.cc +++ b/chromium/ui/views/controls/native/native_view_host.cc @@ -14,7 +14,6 @@ namespace views { // static -const char NativeViewHost::kViewClassName[] = "NativeViewHost"; const char kWidgetNativeViewHostKey[] = "WidgetNativeViewHost"; //////////////////////////////////////////////////////////////////////////////// @@ -195,10 +194,6 @@ void NativeViewHost::ViewHierarchyChanged( } } -const char* NativeViewHost::GetClassName() const { - return kViewClassName; -} - void NativeViewHost::OnFocus() { if (native_view_) native_wrapper_->SetFocus(); @@ -256,4 +251,8 @@ void NativeViewHost::ClearFocus() { } } +BEGIN_METADATA(NativeViewHost) +METADATA_PARENT_CLASS(View) +END_METADATA() + } // namespace views diff --git a/chromium/ui/views/controls/native/native_view_host.h b/chromium/ui/views/controls/native/native_view_host.h index c64341390fc..2a6cfc54d86 100644 --- a/chromium/ui/views/controls/native/native_view_host.h +++ b/chromium/ui/views/controls/native/native_view_host.h @@ -28,8 +28,7 @@ extern const char kWidgetNativeViewHostKey[]; // the platform-specific work of manipulating the underlying OS widget type. class VIEWS_EXPORT NativeViewHost : public View { public: - // The NativeViewHost's class name. - static const char kViewClassName[]; + METADATA_HEADER(NativeViewHost); NativeViewHost(); ~NativeViewHost() override; @@ -104,7 +103,6 @@ class VIEWS_EXPORT NativeViewHost : public View { void OnVisibleBoundsChanged() override; void ViewHierarchyChanged( const ViewHierarchyChangedDetails& details) override; - const char* GetClassName() const override; private: friend class test::NativeViewHostTestBase; 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 6e97d8e28d7..b8837382ab2 100644 --- a/chromium/ui/views/controls/native/native_view_host_aura.cc +++ b/chromium/ui/views/controls/native/native_view_host_aura.cc @@ -24,7 +24,6 @@ #include "ui/views/view_class_properties.h" #include "ui/views/view_constants_aura.h" #include "ui/views/widget/widget.h" -#include "ui/wm/core/window_util.h" namespace views { @@ -271,15 +270,8 @@ void NativeViewHostAura::OnWindowBoundsChanged( const gfx::Rect& old_bounds, const gfx::Rect& new_bounds, ui::PropertyChangeReason reason) { - if (mask_) { - // Having a mask means this layer has a render surface of its own. This - // means we want this layer snapped as the render surface uses this layer - // (its primary layer) to snap to the physical pixel grid. - // See https://crbug.com/843250 for more details. - wm::SnapWindowToPixelBoundary(window); - + if (mask_) mask_->layer()->SetBounds(gfx::Rect(host_->native_view()->bounds().size())); - } } void NativeViewHostAura::OnWindowDestroying(aura::Window* window) { @@ -346,13 +338,6 @@ void NativeViewHostAura::InstallMask() { if (!mask_) return; if (host_->native_view()) { - // Setting a mask triggers this layer to have a render surface of its own. - // This means we cannot skip computing its subpixel offset positioning as - // the render surface uses this layer (its primary layer) to snap to the - // physical pixel grid. - // See https://crbug.com/843250 for more details. - wm::SnapWindowToPixelBoundary(host_->native_view()); - mask_->layer()->SetBounds(gfx::Rect(host_->native_view()->bounds().size())); host_->native_view()->layer()->SetMaskLayer(mask_->layer()); } diff --git a/chromium/ui/views/controls/native/native_view_host_mac.h b/chromium/ui/views/controls/native/native_view_host_mac.h index 04e4e620f72..01b34f7f6a8 100644 --- a/chromium/ui/views/controls/native/native_view_host_mac.h +++ b/chromium/ui/views/controls/native/native_view_host_mac.h @@ -18,7 +18,7 @@ class ViewsHostableView; namespace views { -class BridgedNativeWidgetHostImpl; +class NativeWidgetMacNSWindowHost; class NativeViewHost; // Mac implementation of NativeViewHostWrapper. @@ -30,8 +30,7 @@ class NativeViewHostMac : public NativeViewHostWrapper, // ViewsHostableView::Host: ui::Layer* GetUiLayer() const override; - remote_cocoa::mojom::BridgeFactory* GetRemoteCocoaApplication() - const override; + remote_cocoa::mojom::Application* GetRemoteCocoaApplication() const override; uint64_t GetNSViewId() const override; void OnHostableViewDestroying() override; @@ -57,8 +56,8 @@ class NativeViewHostMac : public NativeViewHostWrapper, void SetParentAccessible(gfx::NativeViewAccessible) override; private: - // Return the BridgedNativeWidgetHostImpl for this hosted view. - BridgedNativeWidgetHostImpl* GetBridgedNativeWidgetHost() const; + // Return the NativeWidgetMacNSWindowHost for this hosted view. + NativeWidgetMacNSWindowHost* GetNSWindowHost() const; // Our associated NativeViewHost. Owns this. NativeViewHost* host_; diff --git a/chromium/ui/views/controls/native/native_view_host_mac.mm b/chromium/ui/views/controls/native/native_view_host_mac.mm index 9604ab93a9b..be0e09c0953 100644 --- a/chromium/ui/views/controls/native/native_view_host_mac.mm +++ b/chromium/ui/views/controls/native/native_view_host_mac.mm @@ -8,7 +8,7 @@ #include "base/mac/foundation_util.h" #import "ui/accessibility/platform/ax_platform_node_mac.h" -#import "ui/views/cocoa/bridged_native_widget_host_impl.h" +#import "ui/views/cocoa/native_widget_mac_ns_window_host.h" #include "ui/views/controls/native/native_view_host.h" #include "ui/views/widget/native_widget_mac.h" #include "ui/views/widget/widget.h" @@ -40,9 +40,8 @@ NativeViewHostMac::NativeViewHostMac(NativeViewHost* host) : host_(host) { NativeViewHostMac::~NativeViewHostMac() { } -BridgedNativeWidgetHostImpl* NativeViewHostMac::GetBridgedNativeWidgetHost() - const { - return BridgedNativeWidgetHostImpl::GetFromNativeWindow( +NativeWidgetMacNSWindowHost* NativeViewHostMac::GetNSWindowHost() const { + return NativeWidgetMacNSWindowHost::GetFromNativeWindow( host_->GetWidget()->GetNativeWindow()); } @@ -53,19 +52,19 @@ ui::Layer* NativeViewHostMac::GetUiLayer() const { return host_->layer(); } -remote_cocoa::mojom::BridgeFactory* -NativeViewHostMac::GetRemoteCocoaApplication() const { - if (auto* bridge_host = GetBridgedNativeWidgetHost()) { - if (bridge_host->bridge_factory_host()) - return bridge_host->bridge_factory_host()->GetFactory(); +remote_cocoa::mojom::Application* NativeViewHostMac::GetRemoteCocoaApplication() + const { + if (auto* window_host = GetNSWindowHost()) { + if (auto* application_host = window_host->application_host()) + return application_host->GetApplication(); } return nullptr; } uint64_t NativeViewHostMac::GetNSViewId() const { - auto* bridge_host = GetBridgedNativeWidgetHost(); - if (bridge_host) - return bridge_host->GetRootViewNSViewId(); + auto* window_host = GetNSWindowHost(); + if (window_host) + return window_host->GetRootViewNSViewId(); return 0; } @@ -88,13 +87,13 @@ void NativeViewHostMac::AttachNativeView() { } EnsureNativeViewHasNoChildWidgets(native_view_); - auto* bridge_host = GetBridgedNativeWidgetHost(); - CHECK(bridge_host); + auto* window_host = GetNSWindowHost(); + CHECK(window_host); // TODO(https://crbug.com/933679): This is lifted out the ViewsHostableAttach // call below because of crashes being observed in the field. NSView* superview = - bridge_host->native_widget_mac()->GetNativeView().GetNativeNSView(); + window_host->native_widget_mac()->GetNativeView().GetNativeNSView(); [superview addSubview:native_view_]; if (native_view_hostable_) { @@ -105,7 +104,7 @@ void NativeViewHostMac::AttachNativeView() { host_->parent()->GetNativeViewAccessible()); } - bridge_host->SetAssociationForView(host_, native_view_); + window_host->SetAssociationForView(host_, native_view_); } void NativeViewHostMac::NativeViewDetaching(bool destroyed) { @@ -131,10 +130,10 @@ void NativeViewHostMac::NativeViewDetaching(bool destroyed) { } EnsureNativeViewHasNoChildWidgets(native_view_); - auto* bridge_host = GetBridgedNativeWidgetHost(); - // BridgedNativeWidgetImpl can be null when Widget is closing. - if (bridge_host) - bridge_host->ClearAssociationForView(host_); + auto* window_host = GetNSWindowHost(); + // NativeWidgetNSWindowBridge can be null when Widget is closing. + if (window_host) + window_host->ClearAssociationForView(host_); native_view_.reset(); } diff --git a/chromium/ui/views/controls/progress_bar.cc b/chromium/ui/views/controls/progress_bar.cc index deb1d931973..e37b7c973bf 100644 --- a/chromium/ui/views/controls/progress_bar.cc +++ b/chromium/ui/views/controls/progress_bar.cc @@ -42,9 +42,6 @@ void AddPossiblyRoundRectToPath(const gfx::Rect& rectangle, } // namespace -// static -const char ProgressBar::kViewClassName[] = "ProgressBar"; - ProgressBar::ProgressBar(int preferred_height, bool allow_round_corner) : preferred_height_(preferred_height), allow_round_corner_(allow_round_corner) { @@ -65,10 +62,6 @@ gfx::Size ProgressBar::CalculatePreferredSize() const { return pref_size; } -const char* ProgressBar::GetClassName() const { - return kViewClassName; -} - void ProgressBar::OnPaint(gfx::Canvas* canvas) { if (IsIndeterminate()) return OnPaintIndeterminate(canvas); @@ -212,4 +205,8 @@ void ProgressBar::OnPaintIndeterminate(gfx::Canvas* canvas) { canvas->DrawPath(slice_path, slice_flags); } +BEGIN_METADATA(ProgressBar) +METADATA_PARENT_CLASS(View) +END_METADATA() + } // namespace views diff --git a/chromium/ui/views/controls/progress_bar.h b/chromium/ui/views/controls/progress_bar.h index 58909de1e7c..131451fb0f9 100644 --- a/chromium/ui/views/controls/progress_bar.h +++ b/chromium/ui/views/controls/progress_bar.h @@ -20,6 +20,8 @@ namespace views { // Progress bar is a control that indicates progress visually. class VIEWS_EXPORT ProgressBar : public View, public gfx::AnimationDelegate { public: + METADATA_HEADER(ProgressBar); + // The preferred height parameter makes it easier to use a ProgressBar with // layout managers that size to preferred size. explicit ProgressBar(int preferred_height = 5, @@ -29,7 +31,6 @@ class VIEWS_EXPORT ProgressBar : public View, public gfx::AnimationDelegate { // Overridden from View: void GetAccessibleNodeData(ui::AXNodeData* node_data) override; gfx::Size CalculatePreferredSize() const override; - const char* GetClassName() const override; void OnPaint(gfx::Canvas* canvas) override; double current_value() const { return current_value_; } @@ -49,8 +50,6 @@ class VIEWS_EXPORT ProgressBar : public View, public gfx::AnimationDelegate { int preferred_height() const { return preferred_height_; } private: - static const char kViewClassName[]; - // gfx::AnimationDelegate: void AnimationProgressed(const gfx::Animation* animation) override; void AnimationEnded(const gfx::Animation* animation) override; diff --git a/chromium/ui/views/controls/resize_area.cc b/chromium/ui/views/controls/resize_area.cc index 51c8f95502c..e0045d3f9e7 100644 --- a/chromium/ui/views/controls/resize_area.cc +++ b/chromium/ui/views/controls/resize_area.cc @@ -12,8 +12,6 @@ namespace views { -const char ResizeArea::kViewClassName[] = "ResizeArea"; - ResizeArea::ResizeArea(ResizeAreaDelegate* delegate) : delegate_(delegate), initial_position_(0) { @@ -21,10 +19,6 @@ ResizeArea::ResizeArea(ResizeAreaDelegate* delegate) ResizeArea::~ResizeArea() = default; -const char* ResizeArea::GetClassName() const { - return kViewClassName; -} - gfx::NativeCursor ResizeArea::GetCursor(const ui::MouseEvent& event) { return GetEnabled() ? GetNativeEastWestResizeCursor() : gfx::kNullCursor; } @@ -85,4 +79,8 @@ void ResizeArea::SetInitialPosition(int event_x) { initial_position_ = point.x(); } +BEGIN_METADATA(ResizeArea) +METADATA_PARENT_CLASS(View) +END_METADATA() + } // namespace views diff --git a/chromium/ui/views/controls/resize_area.h b/chromium/ui/views/controls/resize_area.h index 31dc6e35235..77567910b90 100644 --- a/chromium/ui/views/controls/resize_area.h +++ b/chromium/ui/views/controls/resize_area.h @@ -17,13 +17,12 @@ class ResizeAreaDelegate; // An invisible area that acts like a horizontal resizer. class VIEWS_EXPORT ResizeArea : public View { public: - static const char kViewClassName[]; + METADATA_HEADER(ResizeArea); explicit ResizeArea(ResizeAreaDelegate* delegate); ~ResizeArea() override; // views::View: - const char* GetClassName() const override; gfx::NativeCursor GetCursor(const ui::MouseEvent& event) override; void OnGestureEvent(ui::GestureEvent* event) override; bool OnMousePressed(const ui::MouseEvent& event) override; diff --git a/chromium/ui/views/controls/resize_area_unittest.cc b/chromium/ui/views/controls/resize_area_unittest.cc index 8a593488d67..0e14fca5681 100644 --- a/chromium/ui/views/controls/resize_area_unittest.cc +++ b/chromium/ui/views/controls/resize_area_unittest.cc @@ -9,7 +9,6 @@ #include "base/bind.h" #include "build/build_config.h" #include "testing/gtest/include/gtest/gtest.h" -#include "ui/aura/window.h" #include "ui/events/test/event_generator.h" #include "ui/views/controls/resize_area_delegate.h" #include "ui/views/test/views_test_base.h" @@ -17,6 +16,10 @@ #include "ui/views/widget/widget.h" #include "ui/views/widget/widget_utils.h" +#if !defined(OS_MACOSX) +#include "ui/aura/window.h" +#endif + namespace { // Constants used by the ResizeAreaTest.SuccessfulGestureDrag test to simulate // a gesture drag by |kGestureScrollDistance| resulting from diff --git a/chromium/ui/views/controls/scroll_view.cc b/chromium/ui/views/controls/scroll_view.cc index a7739588d67..f6616623ce7 100644 --- a/chromium/ui/views/controls/scroll_view.cc +++ b/chromium/ui/views/controls/scroll_view.cc @@ -24,8 +24,6 @@ namespace views { -const char ScrollView::kViewClassName[] = "ScrollView"; - namespace { class ScrollCornerView : public View { @@ -114,8 +112,6 @@ class ScrollView::Viewport : public View { explicit Viewport(ScrollView* scroll_view) : scroll_view_(scroll_view) {} ~Viewport() override = default; - const char* GetClassName() const override { return "ScrollView::Viewport"; } - void ScrollRectToVisible(const gfx::Rect& rect) override { if (children().empty() || !parent()) return; @@ -279,9 +275,12 @@ void ScrollView::SetHeader(std::nullptr_t) { } void ScrollView::SetBackgroundColor(SkColor color) { + if (background_color_data_.color == color) + return; background_color_data_.color = color; use_color_id_ = false; UpdateBackground(); + OnPropertyChanged(&background_color_data_, kPropertyEffectsPaint); } void ScrollView::SetBackgroundThemeColorId(ui::NativeTheme::ColorId color_id) { @@ -298,6 +297,20 @@ gfx::Rect ScrollView::GetVisibleRect() const { contents_viewport_->height()); } +void ScrollView::SetHideHorizontalScrollBar(bool visible) { + if (hide_horizontal_scrollbar_ == visible) + return; + hide_horizontal_scrollbar_ = visible; + OnPropertyChanged(&hide_horizontal_scrollbar_, kPropertyEffectsPaint); +} + +void ScrollView::SetDrawOverflowIndicator(bool draw_overflow_indicator) { + if (draw_overflow_indicator_ == draw_overflow_indicator) + return; + draw_overflow_indicator_ = draw_overflow_indicator; + OnPropertyChanged(&draw_overflow_indicator, kPropertyEffectsPaint); +} + void ScrollView::ClipHeightTo(int min_height, int max_height) { min_height_ = min_height; max_height_ = max_height; @@ -336,6 +349,7 @@ void ScrollView::SetHasFocusIndicator(bool has_focus_indicator) { focus_ring_->SchedulePaint(); SchedulePaint(); + OnPropertyChanged(&has_focus_indicator, kPropertyEffectsPaint); } gfx::Size ScrollView::CalculatePreferredSize() const { @@ -623,10 +637,6 @@ void ScrollView::OnGestureEvent(ui::GestureEvent* event) { } } -const char* ScrollView::GetClassName() const { - return kViewClassName; -} - void ScrollView::OnThemeChanged() { UpdateBorder(); if (use_color_id_) @@ -953,6 +963,16 @@ void ScrollView::UpdateOverflowIndicatorVisibility( offset.x() < horiz_sb_->GetMaxPosition() && draw_overflow_indicator_); } +BEGIN_METADATA(ScrollView) +METADATA_PARENT_CLASS(View) +ADD_READONLY_PROPERTY_METADATA(ScrollView, int, MinHeight) +ADD_READONLY_PROPERTY_METADATA(ScrollView, int, MaxHeight) +ADD_PROPERTY_METADATA(ScrollView, SkColor, BackgroundColor) +ADD_PROPERTY_METADATA(ScrollView, bool, DrawOverflowIndicator) +ADD_PROPERTY_METADATA(ScrollView, bool, HasFocusIndicator) +ADD_PROPERTY_METADATA(ScrollView, bool, HideHorizontalScrollBar) +END_METADATA() + // VariableRowHeightScrollHelper ---------------------------------------------- VariableRowHeightScrollHelper::VariableRowHeightScrollHelper( diff --git a/chromium/ui/views/controls/scroll_view.h b/chromium/ui/views/controls/scroll_view.h index e83fd367c52..cebafcdf217 100644 --- a/chromium/ui/views/controls/scroll_view.h +++ b/chromium/ui/views/controls/scroll_view.h @@ -43,7 +43,7 @@ class Separator; class VIEWS_EXPORT ScrollView : public View, public ScrollBarController { public: - static const char kViewClassName[]; + METADATA_HEADER(ScrollView); ScrollView(); @@ -77,6 +77,10 @@ class VIEWS_EXPORT ScrollView : public View, public ScrollBarController { } void SetHeader(std::nullptr_t); + int GetMaxHeight() const { return max_height_; } + + int GetMinHeight() const { return min_height_; } + // The background color can be configured in two distinct ways: // . By way of SetBackgroundThemeColorId(). This is the default and when // called the background color comes from the theme (and changes if the @@ -84,19 +88,21 @@ class VIEWS_EXPORT ScrollView : public View, public ScrollBarController { // . By way of setting an explicit color, i.e. SetBackgroundColor(). Use // SK_ColorTRANSPARENT if you don't want any color, but be warned this // produces awful results when layers are used with subpixel rendering. + SkColor GetBackgroundColor() const; void SetBackgroundColor(SkColor color); + void SetBackgroundThemeColorId(ui::NativeTheme::ColorId color_id); // Returns the visible region of the content View. gfx::Rect GetVisibleRect() const; - void set_hide_horizontal_scrollbar(bool visible) { - hide_horizontal_scrollbar_ = visible; - } + bool GetUseColorId() const { return use_color_id_; } - void set_draw_overflow_indicator(bool draw_overflow_indicator) { - draw_overflow_indicator_ = draw_overflow_indicator; - } + bool GetHideHorizontalScrollBar() const { return hide_horizontal_scrollbar_; } + void SetHideHorizontalScrollBar(bool visible); + + bool GetDrawOverflowIndicator() const { return draw_overflow_indicator_; } + void SetDrawOverflowIndicator(bool draw_overflow_indicator); // Turns this scroll view into a bounded scroll view, with a fixed height. // By default, a ScrollView will stretch to fill its outer container. @@ -119,7 +125,8 @@ class VIEWS_EXPORT ScrollView : public View, public ScrollBarController { void SetHorizontalScrollBar(ScrollBar* horiz_sb); void SetVerticalScrollBar(ScrollBar* vert_sb); - // Sets whether this ScrollView has a focus indicator or not. + // Gets/Sets whether this ScrollView has a focus indicator or not. + bool GetHasFocusIndicator() const { return draw_focus_indicator_; } void SetHasFocusIndicator(bool has_focus_indicator); // View overrides: @@ -130,7 +137,6 @@ class VIEWS_EXPORT ScrollView : public View, public ScrollBarController { bool OnMouseWheel(const ui::MouseWheelEvent& e) override; void OnScrollEvent(ui::ScrollEvent* event) override; void OnGestureEvent(ui::GestureEvent* event) override; - const char* GetClassName() const override; void OnThemeChanged() override; // ScrollBarController overrides: @@ -208,7 +214,6 @@ class VIEWS_EXPORT ScrollView : public View, public ScrollBarController { void UpdateBorder(); void UpdateBackground(); - SkColor GetBackgroundColor() const; // Positions each overflow indicator against their respective content edge. void PositionOverflowIndicators(); diff --git a/chromium/ui/views/controls/scroll_view_unittest.cc b/chromium/ui/views/controls/scroll_view_unittest.cc index 9a2125e09fc..685261b3d1c 100644 --- a/chromium/ui/views/controls/scroll_view_unittest.cc +++ b/chromium/ui/views/controls/scroll_view_unittest.cc @@ -1514,7 +1514,7 @@ TEST_F(ScrollViewTest, IgnoreOverlapWithHiddenHorizontalScroll) { /* horizontal */ false, /* overlaps_content */ false, kThickness)); // Also, let's turn off horizontal scroll bar. - scroll_view_->set_hide_horizontal_scrollbar(true); + scroll_view_->SetHideHorizontalScrollBar(true); View* contents = InstallContents(); contents->SetBoundsRect(gfx::Rect(0, 0, 300, 300)); diff --git a/chromium/ui/views/controls/scrollbar/base_scroll_bar_button.cc b/chromium/ui/views/controls/scrollbar/base_scroll_bar_button.cc index d9adbdd3658..122d1c13caa 100644 --- a/chromium/ui/views/controls/scrollbar/base_scroll_bar_button.cc +++ b/chromium/ui/views/controls/scrollbar/base_scroll_bar_button.cc @@ -42,4 +42,8 @@ void BaseScrollBarButton::RepeaterNotifyClick() { Button::NotifyClick(event); } +BEGIN_METADATA(BaseScrollBarButton) +METADATA_PARENT_CLASS(Button) +END_METADATA() + } // namespace views diff --git a/chromium/ui/views/controls/scrollbar/base_scroll_bar_button.h b/chromium/ui/views/controls/scrollbar/base_scroll_bar_button.h index 245d60f0861..052f5f1f62f 100644 --- a/chromium/ui/views/controls/scrollbar/base_scroll_bar_button.h +++ b/chromium/ui/views/controls/scrollbar/base_scroll_bar_button.h @@ -24,6 +24,8 @@ namespace views { /////////////////////////////////////////////////////////////////////////////// class VIEWS_EXPORT BaseScrollBarButton : public Button { public: + METADATA_HEADER(BaseScrollBarButton); + explicit BaseScrollBarButton(ButtonListener* listener); ~BaseScrollBarButton() override; diff --git a/chromium/ui/views/controls/scrollbar/base_scroll_bar_thumb.cc b/chromium/ui/views/controls/scrollbar/base_scroll_bar_thumb.cc index f6f78dbde7c..3221a12abf4 100644 --- a/chromium/ui/views/controls/scrollbar/base_scroll_bar_thumb.cc +++ b/chromium/ui/views/controls/scrollbar/base_scroll_bar_thumb.cc @@ -130,4 +130,8 @@ bool BaseScrollBarThumb::IsHorizontal() const { return scroll_bar_->IsHorizontal(); } +BEGIN_METADATA(BaseScrollBarThumb) +METADATA_PARENT_CLASS(View) +END_METADATA() + } // namespace views diff --git a/chromium/ui/views/controls/scrollbar/base_scroll_bar_thumb.h b/chromium/ui/views/controls/scrollbar/base_scroll_bar_thumb.h index a8f29dafe80..e43da4686c0 100644 --- a/chromium/ui/views/controls/scrollbar/base_scroll_bar_thumb.h +++ b/chromium/ui/views/controls/scrollbar/base_scroll_bar_thumb.h @@ -29,6 +29,8 @@ class ScrollBar; /////////////////////////////////////////////////////////////////////////////// class VIEWS_EXPORT BaseScrollBarThumb : public View { public: + METADATA_HEADER(BaseScrollBarThumb); + explicit BaseScrollBarThumb(ScrollBar* scroll_bar); ~BaseScrollBarThumb() override; diff --git a/chromium/ui/views/controls/scrollbar/cocoa_scroll_bar.h b/chromium/ui/views/controls/scrollbar/cocoa_scroll_bar.h index a839959b93a..42c1d11cc95 100644 --- a/chromium/ui/views/controls/scrollbar/cocoa_scroll_bar.h +++ b/chromium/ui/views/controls/scrollbar/cocoa_scroll_bar.h @@ -24,6 +24,8 @@ class VIEWS_EXPORT CocoaScrollBar : public ScrollBar, public ui::ImplicitAnimationObserver, public gfx::AnimationDelegate { public: + METADATA_HEADER(CocoaScrollBar); + explicit CocoaScrollBar(bool horizontal); ~CocoaScrollBar() override; diff --git a/chromium/ui/views/controls/scrollbar/cocoa_scroll_bar.mm b/chromium/ui/views/controls/scrollbar/cocoa_scroll_bar.mm index a3cc7582a40..8d59d8936d4 100644 --- a/chromium/ui/views/controls/scrollbar/cocoa_scroll_bar.mm +++ b/chromium/ui/views/controls/scrollbar/cocoa_scroll_bar.mm @@ -551,4 +551,8 @@ base::RetainingOneShotTimer* ScrollBar::GetHideTimerForTesting( return &static_cast<CocoaScrollBar*>(scroll_bar)->hide_scrollbar_timer_; } +BEGIN_METADATA(CocoaScrollBar) +METADATA_PARENT_CLASS(ScrollBar) +END_METADATA() + } // namespace views diff --git a/chromium/ui/views/controls/scrollbar/overlay_scroll_bar.cc b/chromium/ui/views/controls/scrollbar/overlay_scroll_bar.cc index 1e2940afc0c..06f70c996b9 100644 --- a/chromium/ui/views/controls/scrollbar/overlay_scroll_bar.cc +++ b/chromium/ui/views/controls/scrollbar/overlay_scroll_bar.cc @@ -192,4 +192,8 @@ void OverlayScrollBar::StartHideCountdown() { base::BindOnce(&OverlayScrollBar::Hide, base::Unretained(this))); } +BEGIN_METADATA(OverlayScrollBar) +METADATA_PARENT_CLASS(ScrollBar) +END_METADATA() + } // namespace views diff --git a/chromium/ui/views/controls/scrollbar/overlay_scroll_bar.h b/chromium/ui/views/controls/scrollbar/overlay_scroll_bar.h index 3ae0c0bee4a..d39b6094b9b 100644 --- a/chromium/ui/views/controls/scrollbar/overlay_scroll_bar.h +++ b/chromium/ui/views/controls/scrollbar/overlay_scroll_bar.h @@ -15,6 +15,8 @@ namespace views { // The transparent scrollbar which overlays its contents. class VIEWS_EXPORT OverlayScrollBar : public ScrollBar { public: + METADATA_HEADER(OverlayScrollBar); + explicit OverlayScrollBar(bool horizontal); ~OverlayScrollBar() override; diff --git a/chromium/ui/views/controls/scrollbar/scroll_bar.cc b/chromium/ui/views/controls/scrollbar/scroll_bar.cc index 2679f2adbdc..b272d268bab 100644 --- a/chromium/ui/views/controls/scrollbar/scroll_bar.cc +++ b/chromium/ui/views/controls/scrollbar/scroll_bar.cc @@ -455,4 +455,12 @@ base::Optional<int> ScrollBar::GetDesiredScrollOffset(ScrollAmount amount) { return base::nullopt; } } + +BEGIN_METADATA(ScrollBar) +METADATA_PARENT_CLASS(View) +ADD_READONLY_PROPERTY_METADATA(ScrollBar, int, MaxPosition) +ADD_READONLY_PROPERTY_METADATA(ScrollBar, int, MinPosition) +ADD_READONLY_PROPERTY_METADATA(ScrollBar, int, Position) +END_METADATA() + } // namespace views diff --git a/chromium/ui/views/controls/scrollbar/scroll_bar.h b/chromium/ui/views/controls/scrollbar/scroll_bar.h index de7b9449d62..d2b54f5854e 100644 --- a/chromium/ui/views/controls/scrollbar/scroll_bar.h +++ b/chromium/ui/views/controls/scrollbar/scroll_bar.h @@ -75,6 +75,8 @@ class VIEWS_EXPORT ScrollBar : public View, public ContextMenuController, public ui::SimpleMenuModel::Delegate { public: + METADATA_HEADER(ScrollBar); + // An enumeration of different amounts of incremental scroll, representing // events sent from different parts of the UI/keyboard. enum class ScrollAmount { diff --git a/chromium/ui/views/controls/scrollbar/scroll_bar_views.cc b/chromium/ui/views/controls/scrollbar/scroll_bar_views.cc index 37c2fd7bbed..bef64bf018c 100644 --- a/chromium/ui/views/controls/scrollbar/scroll_bar_views.cc +++ b/chromium/ui/views/controls/scrollbar/scroll_bar_views.cc @@ -31,7 +31,6 @@ class ScrollBarButton : public BaseScrollBarButton { ~ScrollBarButton() override; gfx::Size CalculatePreferredSize() const override; - const char* GetClassName() const override { return "ScrollBarButton"; } protected: void PaintButtonContents(gfx::Canvas* canvas) override; @@ -51,7 +50,6 @@ class ScrollBarThumb : public BaseScrollBarThumb { ~ScrollBarThumb() override; gfx::Size CalculatePreferredSize() const override; - const char* GetClassName() const override { return "ScrollBarThumb"; } protected: void OnPaint(gfx::Canvas* canvas) override; @@ -197,8 +195,6 @@ ui::NativeTheme::State ScrollBarThumb::GetNativeThemeState() const { //////////////////////////////////////////////////////////////////////////////// // ScrollBarViews, public: -const char ScrollBarViews::kViewClassName[] = "ScrollBarViews"; - ScrollBarViews::ScrollBarViews(bool horizontal) : ScrollBar(horizontal) { using Type = ScrollBarButton::Type; SetThumb(new ScrollBarThumb(this)); @@ -280,10 +276,6 @@ gfx::Size ScrollBarViews::CalculatePreferredSize() const { IsHorizontal() ? GetThickness() : 0); } -const char* ScrollBarViews::GetClassName() const { - return kViewClassName; -} - int ScrollBarViews::GetThickness() const { const ui::NativeTheme* theme = GetNativeTheme(); return IsHorizontal() ? GetHorizontalScrollBarHeight(theme) @@ -339,4 +331,8 @@ int ScrollBarViews::GetHorizontalScrollBarHeight(const ui::NativeTheme* theme) { return std::max(track_size.height(), button_size.height()); } +BEGIN_METADATA(ScrollBarViews) +METADATA_PARENT_CLASS(ScrollBar) +END_METADATA() + } // namespace views diff --git a/chromium/ui/views/controls/scrollbar/scroll_bar_views.h b/chromium/ui/views/controls/scrollbar/scroll_bar_views.h index 847a7811c25..b7f1ada8dce 100644 --- a/chromium/ui/views/controls/scrollbar/scroll_bar_views.h +++ b/chromium/ui/views/controls/scrollbar/scroll_bar_views.h @@ -22,7 +22,7 @@ namespace views { // Views implementation for the scrollbar. class VIEWS_EXPORT ScrollBarViews : public ScrollBar, public ButtonListener { public: - static const char kViewClassName[]; + METADATA_HEADER(ScrollBarViews); // Creates new scrollbar, either horizontal or vertical. explicit ScrollBarViews(bool horizontal); @@ -35,7 +35,6 @@ class VIEWS_EXPORT ScrollBarViews : public ScrollBar, public ButtonListener { void Layout() override; void OnPaint(gfx::Canvas* canvas) override; gfx::Size CalculatePreferredSize() const override; - const char* GetClassName() const override; // ScrollBar overrides: int GetThickness() const override; diff --git a/chromium/ui/views/controls/separator.cc b/chromium/ui/views/controls/separator.cc index 4e1c570782b..4a9d55853d5 100644 --- a/chromium/ui/views/controls/separator.cc +++ b/chromium/ui/views/controls/separator.cc @@ -11,23 +11,36 @@ namespace views { // static -const char Separator::kViewClassName[] = "Separator"; - -// static const int Separator::kThickness = 1; Separator::Separator() = default; Separator::~Separator() = default; +SkColor Separator::GetColor() const { + if (overridden_color_ == true) + return overridden_color_.value(); + return 0; +} + void Separator::SetColor(SkColor color) { + if (overridden_color_ == color) + return; + overridden_color_ = color; - SchedulePaint(); + OnPropertyChanged(&overridden_color_, kPropertyEffectsPaint); +} + +int Separator::GetPreferredHeight() const { + return preferred_height_; } void Separator::SetPreferredHeight(int height) { + if (preferred_height_ == height) + return; + preferred_height_ = height; - PreferredSizeChanged(); + OnPropertyChanged(&preferred_height_, kPropertyEffectsPreferredSizeChanged); } //////////////////////////////////////////////////////////////////////////////// @@ -63,8 +76,10 @@ void Separator::OnPaint(gfx::Canvas* canvas) { View::OnPaint(canvas); } -const char* Separator::GetClassName() const { - return kViewClassName; -} +BEGIN_METADATA(Separator) +METADATA_PARENT_CLASS(View) +ADD_PROPERTY_METADATA(Separator, SkColor, Color) +ADD_PROPERTY_METADATA(Separator, int, PreferredHeight) +END_METADATA() } // namespace views diff --git a/chromium/ui/views/controls/separator.h b/chromium/ui/views/controls/separator.h index b0674fcbe3c..f547479c86e 100644 --- a/chromium/ui/views/controls/separator.h +++ b/chromium/ui/views/controls/separator.h @@ -17,8 +17,7 @@ namespace views { // other views. class VIEWS_EXPORT Separator : public View { public: - // The separator's class name. - static const char kViewClassName[]; + METADATA_HEADER(Separator); // The separator's thickness in dip. static const int kThickness; @@ -26,15 +25,16 @@ class VIEWS_EXPORT Separator : public View { Separator(); ~Separator() override; + SkColor GetColor() const; void SetColor(SkColor color); + int GetPreferredHeight() const; void SetPreferredHeight(int height); // Overridden from View: gfx::Size CalculatePreferredSize() const override; void GetAccessibleNodeData(ui::AXNodeData* node_data) override; void OnPaint(gfx::Canvas* canvas) override; - const char* GetClassName() const override; private: int preferred_height_ = kThickness; diff --git a/chromium/ui/views/controls/slider.cc b/chromium/ui/views/controls/slider.cc index 75432a2c8a4..183868535c0 100644 --- a/chromium/ui/views/controls/slider.cc +++ b/chromium/ui/views/controls/slider.cc @@ -59,9 +59,6 @@ constexpr int kSlideHighlightChangeDurationMs = 150; } // namespace -// static -const char Slider::kViewClassName[] = "Slider"; - Slider::Slider(SliderListener* listener) : listener_(listener), highlight_animation_(this), @@ -79,13 +76,34 @@ Slider::Slider(SliderListener* listener) Slider::~Slider() = default; +float Slider::GetValue() const { + return value_; +} + void Slider::SetValue(float value) { SetValueInternal(value, VALUE_CHANGED_BY_API); } -void Slider::UpdateState(bool control_on) { - is_active_ = control_on; - SchedulePaint(); +bool Slider::GetEnableAccessibilityEvents() const { + return accessibility_events_enabled_; +} + +void Slider::SetEnableAccessibilityEvents(bool enabled) { + if (accessibility_events_enabled_ == enabled) + return; + accessibility_events_enabled_ = enabled; + OnPropertyChanged(&accessibility_events_enabled_, kPropertyEffectsNone); +} + +bool Slider::GetIsActive() const { + return is_active_; +} + +void Slider::SetIsActive(bool is_active) { + if (is_active == is_active_) + return; + is_active_ = is_active; + OnPropertyChanged(&is_active_, kPropertyEffectsPaint); } float Slider::GetAnimatingValue() const{ @@ -143,8 +161,9 @@ void Slider::SetValueInternal(float value, SliderChangeReason reason) { move_animation_->SetSlideDuration(kSlideValueChangeDurationMs); move_animation_->Show(); } + OnPropertyChanged(&value_, kPropertyEffectsNone); } else { - SchedulePaint(); + OnPropertyChanged(&value_, kPropertyEffectsPaint); } if (accessibility_events_enabled_) { @@ -196,10 +215,6 @@ void Slider::OnSliderDragEnded() { listener_->SliderDragEnded(this); } -const char* Slider::GetClassName() const { - return kViewClassName; -} - gfx::Size Slider::CalculatePreferredSize() const { constexpr int kSizeMajor = 200; constexpr int kSizeMinor = 40; @@ -360,4 +375,11 @@ void Slider::OnGestureEvent(ui::GestureEvent* event) { } } +BEGIN_METADATA(Slider) +METADATA_PARENT_CLASS(View) +ADD_PROPERTY_METADATA(Slider, float, Value) +ADD_PROPERTY_METADATA(Slider, bool, EnableAccessibilityEvents) +ADD_PROPERTY_METADATA(Slider, bool, IsActive) +END_METADATA() + } // namespace views diff --git a/chromium/ui/views/controls/slider.h b/chromium/ui/views/controls/slider.h index 64869aa37c4..7ecf9eb0fba 100644 --- a/chromium/ui/views/controls/slider.h +++ b/chromium/ui/views/controls/slider.h @@ -44,21 +44,20 @@ class VIEWS_EXPORT SliderListener { class VIEWS_EXPORT Slider : public View, public gfx::AnimationDelegate { public: - // Internal class name. - static const char kViewClassName[]; + METADATA_HEADER(Slider); explicit Slider(SliderListener* listener); ~Slider() override; - float value() const { return value_; } + float GetValue() const; void SetValue(float value); - void set_enable_accessibility_events(bool enabled) { - accessibility_events_enabled_ = enabled; - } + bool GetEnableAccessibilityEvents() const; + void SetEnableAccessibilityEvents(bool enabled); - // Update UI based on control on/off state. - void UpdateState(bool control_on); + // Gets/Sets IsActive state + bool GetIsActive() const; + void SetIsActive(bool is_active); protected: // Returns the current position of the thumb on the slider. @@ -92,7 +91,6 @@ class VIEWS_EXPORT Slider : public View, public gfx::AnimationDelegate { void OnSliderDragEnded(); // views::View: - const char* GetClassName() const override; gfx::Size CalculatePreferredSize() const override; bool OnMousePressed(const ui::MouseEvent& event) override; bool OnMouseDragged(const ui::MouseEvent& event) override; diff --git a/chromium/ui/views/controls/slider_unittest.cc b/chromium/ui/views/controls/slider_unittest.cc index f5775d9069c..b2844dc4a0d 100644 --- a/chromium/ui/views/controls/slider_unittest.cc +++ b/chromium/ui/views/controls/slider_unittest.cc @@ -233,20 +233,20 @@ void SliderTest::ClickAt(int x, int y) { TEST_F(SliderTest, UpdateFromClickHorizontal) { ClickAt(0, 0); - EXPECT_EQ(0.0f, slider()->value()); + EXPECT_EQ(0.0f, slider()->GetValue()); ClickAt(max_x(), 0); - EXPECT_EQ(1.0f, slider()->value()); + EXPECT_EQ(1.0f, slider()->GetValue()); } TEST_F(SliderTest, UpdateFromClickRTLHorizontal) { base::i18n::SetICUDefaultLocale("he"); ClickAt(0, 0); - EXPECT_EQ(1.0f, slider()->value()); + EXPECT_EQ(1.0f, slider()->GetValue()); ClickAt(max_x(), 0); - EXPECT_EQ(0.0f, slider()->value()); + EXPECT_EQ(0.0f, slider()->GetValue()); } // No touch on desktop Mac. Tracked in http://crbug.com/445520. @@ -257,17 +257,17 @@ TEST_F(SliderTest, SliderValueForTapGesture) { // Tap below the minimum. slider()->SetValue(0.5); event_generator()->GestureTapAt(gfx::Point(0, 0)); - EXPECT_FLOAT_EQ(0, slider()->value()); + EXPECT_FLOAT_EQ(0, slider()->GetValue()); // Tap above the maximum. slider()->SetValue(0.5); event_generator()->GestureTapAt(gfx::Point(max_x(), max_y())); - EXPECT_FLOAT_EQ(1, slider()->value()); + EXPECT_FLOAT_EQ(1, slider()->GetValue()); // Tap somwhere in the middle. slider()->SetValue(0.5); event_generator()->GestureTapAt(gfx::Point(0.75 * max_x(), 0.75 * max_y())); - EXPECT_NEAR(0.75, slider()->value(), 0.03); + EXPECT_NEAR(0.75, slider()->GetValue(), 0.03); } // Test the slider location after a scroll gesture. @@ -277,14 +277,14 @@ TEST_F(SliderTest, SliderValueForScrollGesture) { event_generator()->GestureScrollSequence( gfx::Point(0.5 * max_x(), 0.5 * max_y()), gfx::Point(0, 0), base::TimeDelta::FromMilliseconds(10), 5 /* steps */); - EXPECT_EQ(0, slider()->value()); + EXPECT_EQ(0, slider()->GetValue()); // Scroll above the maximum. slider()->SetValue(0.5); event_generator()->GestureScrollSequence( gfx::Point(0.5 * max_x(), 0.5 * max_y()), gfx::Point(max_x(), max_y()), base::TimeDelta::FromMilliseconds(10), 5 /* steps */); - EXPECT_EQ(1, slider()->value()); + EXPECT_EQ(1, slider()->GetValue()); // Scroll somewhere in the middle. slider()->SetValue(0.25); @@ -292,7 +292,7 @@ TEST_F(SliderTest, SliderValueForScrollGesture) { gfx::Point(0.25 * max_x(), 0.25 * max_y()), gfx::Point(0.75 * max_x(), 0.75 * max_y()), base::TimeDelta::FromMilliseconds(10), 5 /* steps */); - EXPECT_NEAR(0.75, slider()->value(), 0.03); + EXPECT_NEAR(0.75, slider()->GetValue(), 0.03); } // Test the slider location by adjusting it using keyboard. @@ -301,38 +301,38 @@ TEST_F(SliderTest, SliderValueForKeyboard) { slider()->SetValue(value); slider()->RequestFocus(); event_generator()->PressKey(ui::VKEY_RIGHT, 0); - EXPECT_GT(slider()->value(), value); + EXPECT_GT(slider()->GetValue(), value); slider()->SetValue(value); event_generator()->PressKey(ui::VKEY_LEFT, 0); - EXPECT_LT(slider()->value(), value); + EXPECT_LT(slider()->GetValue(), value); slider()->SetValue(value); event_generator()->PressKey(ui::VKEY_UP, 0); - EXPECT_GT(slider()->value(), value); + EXPECT_GT(slider()->GetValue(), value); slider()->SetValue(value); event_generator()->PressKey(ui::VKEY_DOWN, 0); - EXPECT_LT(slider()->value(), value); + EXPECT_LT(slider()->GetValue(), value); // RTL reverse left/right but not up/down. base::i18n::SetICUDefaultLocale("he"); EXPECT_TRUE(base::i18n::IsRTL()); event_generator()->PressKey(ui::VKEY_RIGHT, 0); - EXPECT_LT(slider()->value(), value); + EXPECT_LT(slider()->GetValue(), value); slider()->SetValue(value); event_generator()->PressKey(ui::VKEY_LEFT, 0); - EXPECT_GT(slider()->value(), value); + EXPECT_GT(slider()->GetValue(), value); slider()->SetValue(value); event_generator()->PressKey(ui::VKEY_UP, 0); - EXPECT_GT(slider()->value(), value); + EXPECT_GT(slider()->GetValue(), value); slider()->SetValue(value); event_generator()->PressKey(ui::VKEY_DOWN, 0); - EXPECT_LT(slider()->value(), value); + EXPECT_LT(slider()->GetValue(), value); } // Verifies the correct SliderListener events are raised for a tap gesture. diff --git a/chromium/ui/views/controls/styled_label.cc b/chromium/ui/views/controls/styled_label.cc index ee21d283095..0d27d16f5ef 100644 --- a/chromium/ui/views/controls/styled_label.cc +++ b/chromium/ui/views/controls/styled_label.cc @@ -116,9 +116,6 @@ bool StyledLabel::StyleRange::operator<( // StyledLabel ---------------------------------------------------------------- -// static -const char StyledLabel::kViewClassName[] = "StyledLabel"; - StyledLabel::StyledLabel(const base::string16& text, StyledLabelListener* listener) : specified_line_height_(0), @@ -133,11 +130,18 @@ StyledLabel::StyledLabel(const base::string16& text, StyledLabel::~StyledLabel() = default; +const base::string16& StyledLabel::GetText() const { + return text_; +} + void StyledLabel::SetText(const base::string16& text) { + if (text_ == text) + return; + text_ = text; style_ranges_.clear(); RemoveAllChildViews(true); - PreferredSizeChanged(); + OnPropertyChanged(&text_, kPropertyEffectsPreferredSizeChanged); } gfx::FontList StyledLabel::GetDefaultFontList() const { @@ -163,12 +167,20 @@ void StyledLabel::AddCustomView(std::unique_ptr<View> custom_view) { custom_views_.insert(std::move(custom_view)); } +int StyledLabel::GetTextContext() const { + return text_context_; +} + void StyledLabel::SetTextContext(int text_context) { if (text_context_ == text_context) return; text_context_ = text_context; - PreferredSizeChanged(); + OnPropertyChanged(&text_context_, kPropertyEffectsPreferredSizeChanged); +} + +int StyledLabel::GetDefaultTextStyle() const { + return default_text_style_; } void StyledLabel::SetDefaultTextStyle(int text_style) { @@ -176,12 +188,24 @@ void StyledLabel::SetDefaultTextStyle(int text_style) { return; default_text_style_ = text_style; - PreferredSizeChanged(); + OnPropertyChanged(&default_text_style_, kPropertyEffectsPreferredSizeChanged); +} + +int StyledLabel::GetLineHeight() const { + return specified_line_height_; } void StyledLabel::SetLineHeight(int line_height) { + if (specified_line_height_ == line_height) + return; + specified_line_height_ = line_height; - PreferredSizeChanged(); + OnPropertyChanged(&specified_line_height_, + kPropertyEffectsPreferredSizeChanged); +} + +SkColor StyledLabel::GetDisplayedOnBackgroundColor() const { + return displayed_on_background_color_; } void StyledLabel::SetDisplayedOnBackgroundColor(SkColor color) { @@ -197,6 +221,19 @@ void StyledLabel::SetDisplayedOnBackgroundColor(SkColor color) { (child->GetClassName() == Link::kViewClassName)); static_cast<Label*>(child)->SetBackgroundColor(color); } + OnPropertyChanged(&displayed_on_background_color_, kPropertyEffectsNone); +} + +bool StyledLabel::GetAutoColorReadabilityEnabled() const { + return auto_color_readability_enabled_; +} + +void StyledLabel::SetAutoColorReadabilityEnabled(bool auto_color_readability) { + if (auto_color_readability_enabled_ == auto_color_readability) + return; + + auto_color_readability_enabled_ = auto_color_readability; + OnPropertyChanged(&auto_color_readability, kPropertyEffectsNone); } void StyledLabel::SizeToFit(int max_width) { @@ -206,17 +243,13 @@ void StyledLabel::SizeToFit(int max_width) { SetSize(CalculateAndDoLayout(max_width, true)); } -const char* StyledLabel::GetClassName() const { - return kViewClassName; -} - void StyledLabel::GetAccessibleNodeData(ui::AXNodeData* node_data) { if (text_context_ == style::CONTEXT_DIALOG_TITLE) node_data->role = ax::mojom::Role::kTitleBar; else node_data->role = ax::mojom::Role::kStaticText; - node_data->SetName(text()); + node_data->SetName(GetText()); } gfx::Size StyledLabel::CalculatePreferredSize() const { @@ -528,4 +561,14 @@ void StyledLabel::AdvanceOneLine(int* line_number, offset->set_x(0); } +BEGIN_METADATA(StyledLabel) +ADD_PROPERTY_METADATA(StyledLabel, base::string16, Text) +ADD_PROPERTY_METADATA(StyledLabel, int, TextContext) +ADD_PROPERTY_METADATA(StyledLabel, int, DefaultTextStyle) +ADD_PROPERTY_METADATA(StyledLabel, int, LineHeight) +ADD_PROPERTY_METADATA(StyledLabel, bool, AutoColorReadabilityEnabled) +ADD_PROPERTY_METADATA(StyledLabel, SkColor, DisplayedOnBackgroundColor) +METADATA_PARENT_CLASS(View) +END_METADATA() + } // namespace views diff --git a/chromium/ui/views/controls/styled_label.h b/chromium/ui/views/controls/styled_label.h index e4ca7ccfaa7..38592c1795a 100644 --- a/chromium/ui/views/controls/styled_label.h +++ b/chromium/ui/views/controls/styled_label.h @@ -34,8 +34,7 @@ class StyledLabelListener; // In this case, leading whitespace is ignored. class VIEWS_EXPORT StyledLabel : public View, public LinkListener { public: - // Internal class name. - static const char kViewClassName[]; + METADATA_HEADER(StyledLabel); // TestApi is used for tests to get internal implementation details. class VIEWS_EXPORT TestApi { @@ -90,10 +89,9 @@ class VIEWS_EXPORT StyledLabel : public View, public LinkListener { ~StyledLabel() override; // Sets the text to be displayed, and clears any previous styling. + const base::string16& GetText() const; void SetText(const base::string16& text); - const base::string16& text() const { return text_; } - // Returns the font list that results from the default text context and style // for ranges. This can be used as the basis for a range |custom_font|. gfx::FontList GetDefaultFontList() const; @@ -105,30 +103,30 @@ class VIEWS_EXPORT StyledLabel : public View, public LinkListener { // Passes ownership of a custom view for use by RangeStyleInfo structs. void AddCustomView(std::unique_ptr<View> custom_view); - // Set the context of this text. All ranges have the same context. + // Get/Set the context of this text. All ranges have the same context. // |text_context| must be a value from views::style::TextContext. + int GetTextContext() const; void SetTextContext(int text_context); // Set the default text style. // |text_style| must be a value from views::style::TextStyle. + int GetDefaultTextStyle() const; void SetDefaultTextStyle(int text_style); // Get or set the distance in pixels between baselines of multi-line text. // Default is 0, indicating the distance between lines should be the standard // one for the label's text, font list, and platform. + int GetLineHeight() const; void SetLineHeight(int height); - // Sets the color of the background on which the label is drawn. This won't - // be explicitly drawn, but the label will force the text color to be + // Gets/Sets the color of the background on which the label is drawn. This + // won't be explicitly drawn, but the label will force the text color to be // readable over it. + SkColor GetDisplayedOnBackgroundColor() const; void SetDisplayedOnBackgroundColor(SkColor color); - SkColor displayed_on_background_color() const { - return displayed_on_background_color_; - } - void set_auto_color_readability_enabled(bool auto_color_readability) { - auto_color_readability_enabled_ = auto_color_readability; - } + bool GetAutoColorReadabilityEnabled() const; + void SetAutoColorReadabilityEnabled(bool auto_color_readability); // Resizes the label so its width is set to the width of the longest line and // its height deduced accordingly. @@ -139,7 +137,6 @@ class VIEWS_EXPORT StyledLabel : public View, public LinkListener { void SizeToFit(int max_width); // View: - const char* GetClassName() const override; void GetAccessibleNodeData(ui::AXNodeData* node_data) override; gfx::Size CalculatePreferredSize() const override; int GetHeightForWidth(int w) const override; diff --git a/chromium/ui/views/controls/styled_label_unittest.cc b/chromium/ui/views/controls/styled_label_unittest.cc index 310bf9f02d8..8d9c27b4e3c 100644 --- a/chromium/ui/views/controls/styled_label_unittest.cc +++ b/chromium/ui/views/controls/styled_label_unittest.cc @@ -84,7 +84,8 @@ TEST_F(StyledLabelTest, TrailingWhitespaceiIgnored) { styled()->Layout(); ASSERT_EQ(1u, styled()->children().size()); - EXPECT_EQ(ASCIIToUTF16("This is a test block of text"), LabelAt(0)->text()); + EXPECT_EQ(ASCIIToUTF16("This is a test block of text"), + LabelAt(0)->GetText()); } TEST_F(StyledLabelTest, RespectLeadingWhitespace) { @@ -96,7 +97,7 @@ TEST_F(StyledLabelTest, RespectLeadingWhitespace) { ASSERT_EQ(1u, styled()->children().size()); EXPECT_EQ(ASCIIToUTF16(" This is a test block of text"), - LabelAt(0)->text()); + LabelAt(0)->GetText()); } TEST_F(StyledLabelTest, RespectLeadingSpacesInNonFirstLine) { @@ -106,7 +107,7 @@ TEST_F(StyledLabelTest, RespectLeadingSpacesInNonFirstLine) { styled()->SetBounds(0, 0, 1000, 1000); styled()->Layout(); ASSERT_EQ(2u, styled()->children().size()); - EXPECT_EQ(ASCIIToUTF16(indented_line), LabelAt(1)->text()); + EXPECT_EQ(ASCIIToUTF16(indented_line), LabelAt(1)->GetText()); } TEST_F(StyledLabelTest, CorrectWrapAtNewline) { @@ -120,9 +121,9 @@ TEST_F(StyledLabelTest, CorrectWrapAtNewline) { styled()->SetBounds(0, 0, label_preferred_size.width(), 1000); styled()->Layout(); ASSERT_EQ(2u, styled()->children().size()); - EXPECT_EQ(ASCIIToUTF16(first_line), LabelAt(0)->text()); + EXPECT_EQ(ASCIIToUTF16(first_line), LabelAt(0)->GetText()); const auto* label_1 = LabelAt(1); - EXPECT_EQ(ASCIIToUTF16(second_line), label_1->text()); + EXPECT_EQ(ASCIIToUTF16(second_line), label_1->GetText()); EXPECT_EQ(styled()->GetHeightForWidth(1000), label_1->bounds().bottom()); } @@ -137,7 +138,7 @@ TEST_F(StyledLabelTest, FirstLineNotEmptyWhenLeadingWhitespaceTooLong) { styled()->Layout(); ASSERT_EQ(1u, styled()->children().size()); - EXPECT_EQ(ASCIIToUTF16("a"), LabelAt(0)->text()); + EXPECT_EQ(ASCIIToUTF16("a"), LabelAt(0)->GetText()); EXPECT_EQ(label_preferred_size.height(), styled()->GetHeightForWidth(label_preferred_size.width() / 2)); } @@ -198,9 +199,9 @@ TEST_F(StyledLabelTest, WrapLongWords) { EXPECT_EQ(gfx::Point(), label_0->origin()); EXPECT_EQ(gfx::Point(0, styled()->height() / 2), label_1->origin()); - EXPECT_FALSE(label_0->text().empty()); - EXPECT_FALSE(label_1->text().empty()); - EXPECT_EQ(ASCIIToUTF16(text), label_0->text() + label_1->text()); + EXPECT_FALSE(label_0->GetText().empty()); + EXPECT_FALSE(label_1->GetText().empty()); + EXPECT_EQ(ASCIIToUTF16(text), label_0->GetText() + label_1->GetText()); } TEST_F(StyledLabelTest, CreateLinks) { @@ -383,26 +384,26 @@ TEST_F(StyledLabelTest, Color) { // Obtain the default text color for a label. Label* label = new Label(ASCIIToUTF16(text)); container->AddChildView(label); - const SkColor kDefaultTextColor = label->enabled_color(); + const SkColor kDefaultTextColor = label->GetEnabledColor(); // Obtain the default text color for a link. Link* link = new Link(ASCIIToUTF16(text_link)); container->AddChildView(link); - const SkColor kDefaultLinkColor = link->enabled_color(); + const SkColor kDefaultLinkColor = link->GetEnabledColor(); - EXPECT_EQ(SK_ColorBLUE, LabelAt(0)->enabled_color()); + EXPECT_EQ(SK_ColorBLUE, LabelAt(0)->GetEnabledColor()); EXPECT_EQ(kDefaultLinkColor, - LabelAt(1, Link::kViewClassName)->enabled_color()); - EXPECT_EQ(kDefaultTextColor, LabelAt(2)->enabled_color()); + LabelAt(1, Link::kViewClassName)->GetEnabledColor()); + EXPECT_EQ(kDefaultTextColor, LabelAt(2)->GetEnabledColor()); // Test adjusted color readability. styled()->SetDisplayedOnBackgroundColor(SK_ColorBLACK); styled()->Layout(); label->SetBackgroundColor(SK_ColorBLACK); - const SkColor kAdjustedTextColor = label->enabled_color(); + const SkColor kAdjustedTextColor = label->GetEnabledColor(); EXPECT_NE(kAdjustedTextColor, kDefaultTextColor); - EXPECT_EQ(kAdjustedTextColor, LabelAt(2)->enabled_color()); + EXPECT_EQ(kAdjustedTextColor, LabelAt(2)->GetEnabledColor()); widget->CloseNow(); } @@ -478,10 +479,10 @@ TEST_F(StyledLabelTest, SetTextContextAndDefaultStyle) { styled()->Layout(); ASSERT_EQ(1u, styled()->children().size()); Label* sublabel = LabelAt(0); - EXPECT_EQ(style::CONTEXT_DIALOG_TITLE, sublabel->text_context()); + EXPECT_EQ(style::CONTEXT_DIALOG_TITLE, sublabel->GetTextContext()); - EXPECT_NE(SK_ColorBLACK, label.enabled_color()); // Sanity check, - EXPECT_EQ(label.enabled_color(), sublabel->enabled_color()); + EXPECT_NE(SK_ColorBLACK, label.GetEnabledColor()); // Sanity check, + EXPECT_EQ(label.GetEnabledColor(), sublabel->GetEnabledColor()); } TEST_F(StyledLabelTest, LineHeight) { diff --git a/chromium/ui/views/controls/tabbed_pane/tabbed_pane.cc b/chromium/ui/views/controls/tabbed_pane/tabbed_pane.cc index 7cc8c5f06c8..905c9144c6a 100644 --- a/chromium/ui/views/controls/tabbed_pane/tabbed_pane.cc +++ b/chromium/ui/views/controls/tabbed_pane/tabbed_pane.cc @@ -4,7 +4,7 @@ #include "ui/views/controls/tabbed_pane/tabbed_pane.h" -#include <memory> +#include <utility> #include "base/i18n/rtl.h" #include "base/logging.h" @@ -69,9 +69,6 @@ constexpr int kBorderThickness = 2; } // namespace -// static -const char TabbedPane::kViewClassName[] = "TabbedPane"; - // A subclass of Tab that implements the Harmony visual styling. class MdTab : public Tab { public: @@ -124,23 +121,23 @@ class MdTabStrip : public TabStrip, public gfx::AnimationDelegate { DISALLOW_COPY_AND_ASSIGN(MdTabStrip); }; -// static -const char Tab::kViewClassName[] = "Tab"; Tab::Tab(TabbedPane* tabbed_pane, const base::string16& title, View* contents) : tabbed_pane_(tabbed_pane), - title_(new Label(title, style::CONTEXT_LABEL, style::STYLE_TAB_ACTIVE)), state_(State::kActive), contents_(contents) { // Calculate the size while the font list is bold. - preferred_title_size_ = title_->GetPreferredSize(); + auto title_label = std::make_unique<Label>(title, style::CONTEXT_LABEL, + style::STYLE_TAB_ACTIVE); + title_ = title_label.get(); + preferred_title_size_ = title_label->GetPreferredSize(); const bool is_vertical = tabbed_pane_->GetOrientation() == TabbedPane::Orientation::kVertical; const bool is_highlight_style = tabbed_pane_->GetStyle() == TabbedPane::TabStripStyle::kHighlight; if (is_vertical) - title_->SetHorizontalAlignment(gfx::HorizontalAlignment::ALIGN_LEFT); + title_label->SetHorizontalAlignment(gfx::HorizontalAlignment::ALIGN_LEFT); if (is_highlight_style && is_vertical) { constexpr int kTabVerticalPadding = 8; @@ -156,8 +153,8 @@ Tab::Tab(TabbedPane* tabbed_pane, const base::string16& title, View* contents) SetLayoutManager(std::make_unique<FillLayout>()); SetState(State::kInactive); // Calculate the size while the font list is normal and set the max size. - preferred_title_size_.SetToMax(title_->GetPreferredSize()); - AddChildView(title_); + preferred_title_size_.SetToMax(title_label->GetPreferredSize()); + AddChildView(std::move(title_label)); // Use leaf so that name is spoken by screen reader without exposing the // children. @@ -177,6 +174,25 @@ void Tab::SetSelected(bool selected) { #endif } +const base::string16& Tab::GetTitleText() const { + return title_->GetText(); +} + +void Tab::SetTitleText(const base::string16& text) { + title_->SetText(text); + + // Active and inactive states use different font sizes. Find the largest size + // and reserve that amount of space. + State old_state = state_; + SetState(State::kActive); + preferred_title_size_ = GetPreferredSize(); + SetState(State::kInactive); + preferred_title_size_.SetToMax(GetPreferredSize()); + SetState(old_state); + + InvalidateLayout(); +} + void Tab::OnStateChanged() { ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance(); const bool is_highlight_mode = @@ -254,10 +270,6 @@ gfx::Size Tab::CalculatePreferredSize() const { return size; } -const char* Tab::GetClassName() const { - return kViewClassName; -} - void Tab::SetState(State state) { if (state == state_) return; @@ -297,7 +309,7 @@ void Tab::OnPaint(gfx::Canvas* canvas) { void Tab::GetAccessibleNodeData(ui::AXNodeData* data) { data->role = ax::mojom::Role::kTab; - data->SetName(title()->text()); + data->SetName(title()->GetText()); data->AddBoolAttribute(ax::mojom::BoolAttribute::kSelected, selected()); } @@ -340,6 +352,10 @@ bool Tab::OnKeyPressed(const ui::KeyEvent& event) { tabbed_pane_->MoveSelectionBy(key == ui::VKEY_DOWN ? 1 : -1); } +BEGIN_METADATA(Tab) +METADATA_PARENT_CLASS(View) +END_METADATA() + MdTab::MdTab(TabbedPane* tabbed_pane, const base::string16& title, View* contents) @@ -388,10 +404,8 @@ void MdTab::OnFocus() { // Do not draw focus ring in kHighlight mode. if (tabbed_pane()->GetStyle() != TabbedPane::TabStripStyle::kHighlight) { SetBorder(CreateSolidBorder( - GetInsets().top(), - SkColorSetA(GetNativeTheme()->GetSystemColor( - ui::NativeTheme::kColorId_FocusedBorderColor), - 0x66))); + GetInsets().top(), GetNativeTheme()->GetSystemColor( + ui::NativeTheme::kColorId_FocusedBorderColor))); } // When the tab gains focus, send an accessibility event indicating that the @@ -412,7 +426,6 @@ void MdTab::OnBlur() { // static constexpr size_t TabStrip::kNoSelectedTab; -const char TabStrip::kViewClassName[] = "TabStrip"; TabStrip::TabStrip(TabbedPane::Orientation orientation, TabbedPane::TabStripStyle style) @@ -421,14 +434,15 @@ TabStrip::TabStrip(TabbedPane::Orientation orientation, if (orientation == TabbedPane::Orientation::kHorizontal) { constexpr int kTabStripLeadingEdgePadding = 9; layout = std::make_unique<BoxLayout>( - BoxLayout::kHorizontal, gfx::Insets(0, kTabStripLeadingEdgePadding)); + BoxLayout::Orientation::kHorizontal, + gfx::Insets(0, kTabStripLeadingEdgePadding)); layout->set_cross_axis_alignment(BoxLayout::CrossAxisAlignment::kEnd); } else { constexpr int kTabStripEdgePadding = 8; constexpr int kTabSpacing = 8; layout = std::make_unique<BoxLayout>( - BoxLayout::kVertical, gfx::Insets(kTabStripEdgePadding, 0, 0, 0), - kTabSpacing); + BoxLayout::Orientation::kVertical, + gfx::Insets(kTabStripEdgePadding, 0, 0, 0), kTabSpacing); layout->set_cross_axis_alignment(BoxLayout::CrossAxisAlignment::kStart); } layout->set_main_axis_alignment(BoxLayout::MainAxisAlignment::kStart); @@ -442,10 +456,6 @@ TabStrip::~TabStrip() = default; void TabStrip::OnSelectedTabChanged(Tab* from_tab, Tab* to_tab) {} -const char* TabStrip::GetClassName() const { - return kViewClassName; -} - void TabStrip::OnPaintBorder(gfx::Canvas* canvas) { // Do not draw border line in kHighlight mode. if (style_ == TabbedPane::TabStripStyle::kHighlight) @@ -541,11 +551,39 @@ Tab* TabStrip::GetTabAtDeltaFromSelected(int delta) const { num_children); } +TabbedPane::Orientation TabStrip::GetOrientation() const { + return orientation_; +} + +TabbedPane::TabStripStyle TabStrip::GetStyle() const { + return style_; +} + +DEFINE_ENUM_CONVERTERS(TabbedPane::Orientation, + {TabbedPane::Orientation::kHorizontal, + base::ASCIIToUTF16("HORIZONTAL")}, + {TabbedPane::Orientation::kVertical, + base::ASCIIToUTF16("VERTICAL")}) + +DEFINE_ENUM_CONVERTERS(TabbedPane::TabStripStyle, + {TabbedPane::TabStripStyle::kBorder, + base::ASCIIToUTF16("BORDER")}, + {TabbedPane::TabStripStyle::kHighlight, + base::ASCIIToUTF16("HIGHLIGHT")}) + +BEGIN_METADATA(TabStrip) +METADATA_PARENT_CLASS(View) +ADD_READONLY_PROPERTY_METADATA(TabStrip, int, SelectedTabIndex) +ADD_READONLY_PROPERTY_METADATA(TabStrip, TabbedPane::Orientation, Orientation) +ADD_READONLY_PROPERTY_METADATA(TabStrip, TabbedPane::TabStripStyle, Style) +END_METADATA() + MdTabStrip::MdTabStrip(TabbedPane::Orientation orientation, TabbedPane::TabStripStyle style) : TabStrip(orientation, style) { if (orientation == TabbedPane::Orientation::kHorizontal) { - auto layout = std::make_unique<BoxLayout>(BoxLayout::kHorizontal); + auto layout = + std::make_unique<BoxLayout>(BoxLayout::Orientation::kHorizontal); layout->set_main_axis_alignment(BoxLayout::MainAxisAlignment::kCenter); layout->set_cross_axis_alignment(BoxLayout::CrossAxisAlignment::kStretch); layout->SetDefaultFlex(1); @@ -568,7 +606,7 @@ void MdTabStrip::OnSelectedTabChanged(Tab* from_tab, Tab* to_tab) { DCHECK(!from_tab->selected()); DCHECK(to_tab->selected()); - if (orientation() == TabbedPane::Orientation::kHorizontal) { + if (GetOrientation() == TabbedPane::Orientation::kHorizontal) { animating_from_ = gfx::Range(from_tab->GetMirroredX(), from_tab->GetMirroredX() + from_tab->width()); animating_to_ = gfx::Range(to_tab->GetMirroredX(), @@ -586,13 +624,13 @@ void MdTabStrip::OnSelectedTabChanged(Tab* from_tab, Tab* to_tab) { void MdTabStrip::OnPaintBorder(gfx::Canvas* canvas) { // Do not draw border line in kHighlight mode. - if (style() == TabbedPane::TabStripStyle::kHighlight) + if (GetStyle() == TabbedPane::TabStripStyle::kHighlight) return; constexpr int kUnselectedBorderThickness = 1; constexpr int kSelectedBorderThickness = 2; const bool is_horizontal = - orientation() == TabbedPane::Orientation::kHorizontal; + GetOrientation() == TabbedPane::Orientation::kHorizontal; int max_cross_axis; @@ -673,8 +711,10 @@ void MdTabStrip::OnPaintBorder(gfx::Canvas* canvas) { rect = gfx::Rect(max_cross_axis - kSelectedBorderThickness, min_main_axis, kSelectedBorderThickness, max_main_axis - min_main_axis); } - canvas->FillRect(rect, GetNativeTheme()->GetSystemColor( - ui::NativeTheme::kColorId_FocusedBorderColor)); + canvas->FillRect( + rect, SkColorSetA(GetNativeTheme()->GetSystemColor( + ui::NativeTheme::kColorId_FocusedBorderColor), + SK_AlphaOPAQUE)); } void MdTabStrip::AnimationProgressed(const gfx::Animation* animation) { @@ -687,13 +727,11 @@ void MdTabStrip::AnimationEnded(const gfx::Animation* animation) { } TabbedPane::TabbedPane(TabbedPane::Orientation orientation, - TabbedPane::TabStripStyle style) - : contents_(new View()) { + TabbedPane::TabStripStyle style) { DCHECK(orientation != TabbedPane::Orientation::kHorizontal || style != TabbedPane::TabStripStyle::kHighlight); - tab_strip_ = new MdTabStrip(orientation, style); - AddChildView(tab_strip_); - AddChildView(contents_); + tab_strip_ = AddChildView(std::make_unique<MdTabStrip>(orientation, style)); + contents_ = AddChildView(std::make_unique<View>()); } TabbedPane::~TabbedPane() = default; @@ -707,19 +745,18 @@ size_t TabbedPane::GetTabCount() { return contents_->children().size(); } -void TabbedPane::AddTab(const base::string16& title, View* contents) { - AddTabAtIndex(tab_strip_->children().size(), title, contents); -} - -void TabbedPane::AddTabAtIndex(size_t index, - const base::string16& title, - View* contents) { +void TabbedPane::AddTabInternal(size_t index, + const base::string16& title, + std::unique_ptr<View> contents) { DCHECK_LE(index, GetTabCount()); contents->SetVisible(false); + contents->GetViewAccessibility().OverrideName(title); + contents->GetViewAccessibility().OverrideRole(ax::mojom::Role::kTab); - tab_strip_->AddChildViewAt(new MdTab(this, title, contents), - static_cast<int>(index)); - contents_->AddChildViewAt(contents, static_cast<int>(index)); + tab_strip_->AddChildViewAt( + std::make_unique<MdTab>(this, title, contents.get()), + static_cast<int>(index)); + contents_->AddChildViewAt(std::move(contents), static_cast<int>(index)); if (!GetSelectedTab()) SelectTabAt(index); @@ -770,11 +807,15 @@ gfx::Size TabbedPane::CalculatePreferredSize() const { } TabbedPane::Orientation TabbedPane::GetOrientation() const { - return tab_strip_->orientation(); + return tab_strip_->GetOrientation(); } TabbedPane::TabStripStyle TabbedPane::GetStyle() const { - return tab_strip_->style(); + return tab_strip_->GetStyle(); +} + +Tab* TabbedPane::GetTabAt(size_t index) { + return tab_strip_->GetTabAtIndex(index); } Tab* TabbedPane::GetSelectedTab() { @@ -823,12 +864,15 @@ bool TabbedPane::AcceleratorPressed(const ui::Accelerator& accelerator) { return MoveSelectionBy(accelerator.IsShiftDown() ? -1 : 1); } -const char* TabbedPane::GetClassName() const { - return kViewClassName; -} - void TabbedPane::GetAccessibleNodeData(ui::AXNodeData* node_data) { node_data->role = ax::mojom::Role::kTabList; + const Tab* const selected_tab = GetSelectedTab(); + if (selected_tab) + node_data->SetName(selected_tab->GetTitleText()); } +BEGIN_METADATA(TabbedPane) +METADATA_PARENT_CLASS(View) +END_METADATA() + } // namespace views diff --git a/chromium/ui/views/controls/tabbed_pane/tabbed_pane.h b/chromium/ui/views/controls/tabbed_pane/tabbed_pane.h index 7d0ab36fe8e..ec2d831f64f 100644 --- a/chromium/ui/views/controls/tabbed_pane/tabbed_pane.h +++ b/chromium/ui/views/controls/tabbed_pane/tabbed_pane.h @@ -5,6 +5,8 @@ #ifndef UI_VIEWS_CONTROLS_TABBED_PANE_TABBED_PANE_H_ #define UI_VIEWS_CONTROLS_TABBED_PANE_TABBED_PANE_H_ +#include <memory> + #include "base/compiler_specific.h" #include "base/macros.h" #include "base/strings/string16.h" @@ -28,6 +30,8 @@ class TabbedPaneTest; // may require additional polish. class VIEWS_EXPORT TabbedPane : public View { public: + METADATA_HEADER(TabbedPane); + // The orientation of the tab alignment. enum class Orientation { kHorizontal, @@ -40,9 +44,6 @@ class VIEWS_EXPORT TabbedPane : public View { kHighlight, // Highlight background and text of the selected tab. }; - // Internal class name. - static const char kViewClassName[]; - explicit TabbedPane(Orientation orientation = Orientation::kHorizontal, TabStripStyle style = TabStripStyle::kBorder); ~TabbedPane() override; @@ -60,12 +61,22 @@ class VIEWS_EXPORT TabbedPane : public View { // Adds a new tab at the end of this TabbedPane with the specified |title|. // |contents| is the view displayed when the tab is selected and is owned by // the TabbedPane. - void AddTab(const base::string16& title, View* contents); + template <typename T> + T* AddTab(const base::string16& title, std::unique_ptr<T> contents) { + return AddTabAtIndex(GetTabCount(), title, std::move(contents)); + } // Adds a new tab at |index| with |title|. |contents| is the view displayed // when the tab is selected and is owned by the TabbedPane. If the tabbed pane // is currently empty, the new tab is selected. - void AddTabAtIndex(size_t index, const base::string16& title, View* contents); + template <typename T> + T* AddTabAtIndex(size_t index, + const base::string16& title, + std::unique_ptr<T> contents) { + T* result = contents.get(); + AddTabInternal(index, title, std::move(contents)); + return result; + } // Selects the tab at |index|, which must be valid. void SelectTabAt(size_t index); @@ -75,7 +86,6 @@ class VIEWS_EXPORT TabbedPane : public View { // Overridden from View: gfx::Size CalculatePreferredSize() const override; - const char* GetClassName() const override; // Gets the orientation of the tab alignment. Orientation GetOrientation() const; @@ -83,6 +93,9 @@ class VIEWS_EXPORT TabbedPane : public View { // Gets the style of the tab strip. TabStripStyle GetStyle() const; + // Returns the tab at the given index. + Tab* GetTabAt(size_t index); + private: friend class FocusTraversalTest; friend class Tab; @@ -90,6 +103,13 @@ class VIEWS_EXPORT TabbedPane : public View { friend class test::TabbedPaneTest; friend class test::TabbedPaneAccessibilityMacTest; + // Adds a new tab at |index| with |title|. |contents| is the view displayed + // when the tab is selected and is owned by the TabbedPane. If the tabbed pane + // is currently empty, the new tab is selected. + void AddTabInternal(size_t index, + const base::string16& title, + std::unique_ptr<View> contents); + // Get the Tab (the tabstrip view, not its content) at the selected index. Tab* GetSelectedTab(); @@ -114,17 +134,16 @@ class VIEWS_EXPORT TabbedPane : public View { // The tab strip and contents container. The child indices of these members // correspond to match each Tab with its respective content View. - TabStrip* tab_strip_; - View* contents_; + TabStrip* tab_strip_ = nullptr; + View* contents_ = nullptr; DISALLOW_COPY_AND_ASSIGN(TabbedPane); }; // The tab view shown in the tab strip. -class Tab : public View { +class VIEWS_EXPORT Tab : public View { public: - // Internal class name. - static const char kViewClassName[]; + METADATA_HEADER(Tab); Tab(TabbedPane* tabbed_pane, const base::string16& title, View* contents); ~Tab() override; @@ -134,13 +153,15 @@ class Tab : public View { bool selected() const { return contents_->GetVisible(); } void SetSelected(bool selected); + const base::string16& GetTitleText() const; + void SetTitleText(const base::string16& text); + // Overridden from View: bool OnMousePressed(const ui::MouseEvent& event) override; void OnMouseEntered(const ui::MouseEvent& event) override; void OnMouseExited(const ui::MouseEvent& event) override; void OnGestureEvent(ui::GestureEvent* event) override; gfx::Size CalculatePreferredSize() const override; - const char* GetClassName() const override; void GetAccessibleNodeData(ui::AXNodeData* node_data) override; bool HandleAccessibleAction(const ui::AXActionData& action_data) override; void OnFocus() override; @@ -168,7 +189,7 @@ class Tab : public View { void OnPaint(gfx::Canvas* canvas) override; TabbedPane* tabbed_pane_; - Label* title_; + Label* title_ = nullptr; gfx::Size preferred_title_size_; State state_; // The content view associated with this tab. @@ -180,12 +201,11 @@ class Tab : public View { // The tab strip shown above/left of the tab contents. class TabStrip : public View { public: + METADATA_HEADER(TabStrip); + // The return value of GetSelectedTabIndex() when no tab is selected. static constexpr size_t kNoSelectedTab = size_t{-1}; - // Internal class name. - static const char kViewClassName[]; - TabStrip(TabbedPane::Orientation orientation, TabbedPane::TabStripStyle style); ~TabStrip() override; @@ -196,7 +216,6 @@ class TabStrip : public View { virtual void OnSelectedTabChanged(Tab* from_tab, Tab* to_tab); // Overridden from View: - const char* GetClassName() const override; void OnPaintBorder(gfx::Canvas* canvas) override; Tab* GetSelectedTab() const; @@ -204,9 +223,9 @@ class TabStrip : public View { Tab* GetTabAtIndex(size_t index) const; size_t GetSelectedTabIndex() const; - TabbedPane::Orientation orientation() const { return orientation_; } + TabbedPane::Orientation GetOrientation() const; - TabbedPane::TabStripStyle style() const { return style_; } + TabbedPane::TabStripStyle GetStyle() const; private: // The orientation of the tab alignment. diff --git a/chromium/ui/views/controls/tabbed_pane/tabbed_pane_accessibility_mac_unittest.mm b/chromium/ui/views/controls/tabbed_pane/tabbed_pane_accessibility_mac_unittest.mm index bdd006460ef..2dc681afc0a 100644 --- a/chromium/ui/views/controls/tabbed_pane/tabbed_pane_accessibility_mac_unittest.mm +++ b/chromium/ui/views/controls/tabbed_pane/tabbed_pane_accessibility_mac_unittest.mm @@ -49,15 +49,16 @@ class TabbedPaneAccessibilityMacTest : public WidgetTest { WidgetTest::SetUp(); widget_ = CreateTopLevelPlatformWidget(); widget_->SetBounds(gfx::Rect(50, 50, 100, 100)); - tabbed_pane_ = new TabbedPane(); - tabbed_pane_->SetSize(gfx::Size(100, 100)); + auto tabbed_pane = std::make_unique<TabbedPane>(); + tabbed_pane->SetSize(gfx::Size(100, 100)); // Create two tabs and position/size them. - tabbed_pane_->AddTab(base::ASCIIToUTF16("Tab 1"), new View()); - tabbed_pane_->AddTab(base::ASCIIToUTF16("Tab 2"), new View()); - tabbed_pane_->Layout(); + tabbed_pane->AddTab(base::ASCIIToUTF16("Tab 1"), std::make_unique<View>()); + tabbed_pane->AddTab(base::ASCIIToUTF16("Tab 2"), std::make_unique<View>()); + tabbed_pane->Layout(); - widget_->GetContentsView()->AddChildView(tabbed_pane_); + tabbed_pane_ = + widget_->GetContentsView()->AddChildView(std::move(tabbed_pane)); widget_->Show(); } 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 f2e3f28a39d..b92e074af5b 100644 --- a/chromium/ui/views/controls/tabbed_pane/tabbed_pane_unittest.cc +++ b/chromium/ui/views/controls/tabbed_pane/tabbed_pane_unittest.cc @@ -13,6 +13,7 @@ #include "ui/accessibility/ax_enums.mojom.h" #include "ui/accessibility/ax_node_data.h" #include "ui/events/keycodes/keyboard_code_conversion.h" +#include "ui/views/accessibility/view_accessibility.h" #include "ui/views/test/test_views.h" #include "ui/views/test/views_test_base.h" #include "ui/views/widget/widget.h" @@ -27,6 +28,18 @@ base::string16 DefaultTabTitle() { return ASCIIToUTF16("tab"); } +base::string16 GetAccessibleName(View* view) { + ui::AXNodeData ax_node_data; + view->GetViewAccessibility().GetAccessibleNodeData(&ax_node_data); + return ax_node_data.GetString16Attribute(ax::mojom::StringAttribute::kName); +} + +ax::mojom::Role GetAccessibleRole(View* view) { + ui::AXNodeData ax_node_data; + view->GetViewAccessibility().GetAccessibleNodeData(&ax_node_data); + return ax_node_data.role; +} + } // namespace class TabbedPaneTest : public ViewsTestBase { @@ -37,6 +50,21 @@ class TabbedPaneTest : public ViewsTestBase { ViewsTestBase::SetUp(); tabbed_pane_ = std::make_unique<TabbedPane>(); tabbed_pane_->set_owned_by_client(); + + // Create a widget so that accessibility data will be returned correctly. + widget_ = std::make_unique<Widget>(); + Widget::InitParams params = + CreateParams(Widget::InitParams::TYPE_WINDOW_FRAMELESS); + params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; + params.bounds = gfx::Rect(0, 0, 650, 650); + widget_->Init(params); + widget_->SetContentsView(tabbed_pane_.get()); + } + + void TearDown() override { + tabbed_pane_.reset(); + widget_.reset(); + ViewsTestBase::TearDown(); } protected: @@ -60,6 +88,7 @@ class TabbedPaneTest : public ViewsTestBase { ui::UsLayoutKeyboardCodeToDomCode(keyboard_code), 0)); } + std::unique_ptr<Widget> widget_; std::unique_ptr<TabbedPane> tabbed_pane_; private: @@ -96,10 +125,12 @@ TEST_F(TabbedPaneTest, TabStripHighlightStyle) { // width of horizontal tab strips for preferred size calculations. Tab titles // are elided to fit the actual width. TEST_F(TabbedPaneTest, SizeAndLayout) { - View* child1 = new StaticSizedView(gfx::Size(20, 10)); - tabbed_pane_->AddTab(ASCIIToUTF16("tab1 with very long text"), child1); - View* child2 = new StaticSizedView(gfx::Size(5, 5)); - tabbed_pane_->AddTab(ASCIIToUTF16("tab2 with very long text"), child2); + View* child1 = tabbed_pane_->AddTab( + ASCIIToUTF16("tab1 with very long text"), + std::make_unique<StaticSizedView>(gfx::Size(20, 10))); + View* child2 = + tabbed_pane_->AddTab(ASCIIToUTF16("tab2 with very long text"), + std::make_unique<StaticSizedView>(gfx::Size(5, 5))); tabbed_pane_->SelectTabAt(0); // |tabbed_pane_| width should match the largest child in horizontal mode. @@ -127,10 +158,11 @@ TEST_F(TabbedPaneTest, SizeAndLayout) { TEST_F(TabbedPaneTest, SizeAndLayoutInVerticalOrientation) { MakeTabbedPane(TabbedPane::Orientation::kVertical, TabbedPane::TabStripStyle::kBorder); - View* child1 = new StaticSizedView(gfx::Size(20, 10)); - tabbed_pane_->AddTab(ASCIIToUTF16("tab1"), child1); - View* child2 = new StaticSizedView(gfx::Size(5, 5)); - tabbed_pane_->AddTab(ASCIIToUTF16("tab2"), child2); + 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. @@ -157,8 +189,7 @@ TEST_F(TabbedPaneTest, SizeAndLayoutInVerticalOrientation) { TEST_F(TabbedPaneTest, AddAndSelect) { // Add several tabs; only the first should be selected automatically. for (size_t i = 0; i < 3; ++i) { - View* tab = new View(); - tabbed_pane_->AddTab(DefaultTabTitle(), tab); + tabbed_pane_->AddTab(DefaultTabTitle(), std::make_unique<View>()); EXPECT_EQ(i + 1, tabbed_pane_->GetTabCount()); EXPECT_EQ(0u, tabbed_pane_->GetSelectedTabIndex()); } @@ -170,8 +201,8 @@ TEST_F(TabbedPaneTest, AddAndSelect) { } // Add a tab at index 0, it should not be selected automatically. - View* tab0 = new View(); - tabbed_pane_->AddTabAtIndex(0, ASCIIToUTF16("tab0"), tab0); + View* tab0 = tabbed_pane_->AddTabAtIndex(0, ASCIIToUTF16("tab0"), + std::make_unique<View>()); EXPECT_NE(tab0, GetSelectedTabContentView()); EXPECT_NE(0u, tabbed_pane_->GetSelectedTabIndex()); } @@ -179,8 +210,7 @@ TEST_F(TabbedPaneTest, AddAndSelect) { TEST_F(TabbedPaneTest, ArrowKeyBindings) { // Add several tabs; only the first should be selected automatically. for (size_t i = 0; i < 3; ++i) { - View* tab = new View(); - tabbed_pane_->AddTab(DefaultTabTitle(), tab); + tabbed_pane_->AddTab(DefaultTabTitle(), std::make_unique<View>()); EXPECT_EQ(i + 1, tabbed_pane_->GetTabCount()); } @@ -215,7 +245,7 @@ TEST_F(TabbedPaneTest, SelectTabWithAccessibleAction) { constexpr size_t kNumTabs = 3; for (size_t i = 0; i < kNumTabs; ++i) { - tabbed_pane_->AddTab(DefaultTabTitle(), new View()); + tabbed_pane_->AddTab(DefaultTabTitle(), std::make_unique<View>()); } // Check the first tab is selected. EXPECT_EQ(0u, tabbed_pane_->GetSelectedTabIndex()); @@ -249,5 +279,37 @@ TEST_F(TabbedPaneTest, SelectTabWithAccessibleAction) { widget->CloseNow(); } +TEST_F(TabbedPaneTest, 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())); + tabbed_pane_->SelectTabAt(1); + EXPECT_EQ(kSecondTitle, GetAccessibleName(tabbed_pane_.get())); +} + +TEST_F(TabbedPaneTest, AccessiblePaneContentsTitleTracksTabTitle) { + const base::string16 kFirstTitle = ASCIIToUTF16("Tab1"); + const base::string16 kSecondTitle = ASCIIToUTF16("Tab2"); + View* const tab1_contents = + tabbed_pane_->AddTab(kFirstTitle, std::make_unique<View>()); + View* const tab2_contents = + tabbed_pane_->AddTab(kSecondTitle, std::make_unique<View>()); + EXPECT_EQ(kFirstTitle, GetAccessibleName(tab1_contents)); + EXPECT_EQ(kSecondTitle, GetAccessibleName(tab2_contents)); +} + +TEST_F(TabbedPaneTest, AccessiblePaneContentsRoleIsTab) { + const base::string16 kFirstTitle = ASCIIToUTF16("Tab1"); + const base::string16 kSecondTitle = ASCIIToUTF16("Tab2"); + View* const tab1_contents = + tabbed_pane_->AddTab(kFirstTitle, std::make_unique<View>()); + View* const tab2_contents = + tabbed_pane_->AddTab(kSecondTitle, std::make_unique<View>()); + EXPECT_EQ(ax::mojom::Role::kTab, GetAccessibleRole(tab1_contents)); + EXPECT_EQ(ax::mojom::Role::kTab, GetAccessibleRole(tab2_contents)); +} + } // namespace test } // namespace views diff --git a/chromium/ui/views/controls/textfield/textfield.cc b/chromium/ui/views/controls/textfield/textfield.cc index e0e651b3163..c2b7981007a 100644 --- a/chromium/ui/views/controls/textfield/textfield.cc +++ b/chromium/ui/views/controls/textfield/textfield.cc @@ -17,8 +17,6 @@ #include "build/build_config.h" #include "ui/accessibility/ax_action_data.h" #include "ui/accessibility/ax_node_data.h" -#include "ui/aura/client/aura_constants.h" -#include "ui/aura/window.h" #include "ui/base/clipboard/scoped_clipboard_writer.h" #include "ui/base/cursor/cursor.h" #include "ui/base/default_style.h" @@ -73,6 +71,7 @@ #endif #if defined(OS_CHROMEOS) +#include "ui/aura/window.h" #include "ui/wm/core/ime_util_chromeos.h" #endif @@ -250,9 +249,6 @@ bool IsControlKeyModifier(int flags) { } // namespace // static -const char Textfield::kViewClassName[] = "Textfield"; - -// static base::TimeDelta Textfield::GetCaretBlinkInterval() { #if defined(OS_WIN) static const size_t system_value = ::GetCaretBlinkTime(); @@ -278,14 +274,14 @@ const gfx::FontList& Textfield::GetDefaultFontList() { Textfield::Textfield() : model_(new TextfieldModel(this)), placeholder_text_draw_flags_(gfx::Canvas::DefaultCanvasTextAlignment()), - selection_controller_(this), - weak_ptr_factory_(this) { + 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_); GetRenderText()->SetFontList(GetDefaultFontList()); UpdateBorder(); @@ -637,10 +633,6 @@ gfx::Size Textfield::GetMinimumSize() const { return minimum_size; } -const char* Textfield::GetClassName() const { - return kViewClassName; -} - void Textfield::SetBorder(std::unique_ptr<Border> b) { use_focus_ring_ = false; if (focus_ring_) @@ -1175,6 +1167,10 @@ void Textfield::OnCompositionTextConfirmedOrCleared() { GetInputMethod()->CancelComposition(this); } +void Textfield::OnTextChanged() { + OnPropertyChanged(&model_ + kTextProperty, kPropertyEffectsPaint); +} + //////////////////////////////////////////////////////////////////////////////// // Textfield, ContextMenuController overrides: @@ -1712,7 +1708,7 @@ bool Textfield::IsTextEditCommandEnabled(ui::TextEditCommand command) const { return readable && model_->HasSelection(); case ui::TextEditCommand::PASTE: ui::Clipboard::GetForCurrentThread()->ReadText( - ui::CLIPBOARD_TYPE_COPY_PASTE, &result); + ui::ClipboardType::kCopyPaste, &result); return editable && !result.empty(); case ui::TextEditCommand::SELECT_ALL: return !text().empty() && GetSelectedRange().length() != text().length(); @@ -1810,7 +1806,7 @@ gfx::Point Textfield::GetLastClickRootLocation() const { base::string16 Textfield::GetSelectionClipboardText() const { base::string16 selection_clipboard_text; - ui::Clipboard::GetForCurrentThread()->ReadText(ui::CLIPBOARD_TYPE_SELECTION, + ui::Clipboard::GetForCurrentThread()->ReadText(ui::ClipboardType::kSelection, &selection_clipboard_text); return selection_clipboard_text; } @@ -2039,6 +2035,12 @@ bool Textfield::ShouldShowPlaceholderText() const { return text().empty() && !GetPlaceholderText().empty(); } +views::PropertyChangedSubscription Textfield::AddTextChangedCallback( + views::PropertyChangedCallback callback) { + return AddPropertyChangedCallback(&model_ + kTextProperty, + std::move(callback)); +} + //////////////////////////////////////////////////////////////////////////////// // Textfield, private: @@ -2111,10 +2113,10 @@ bool Textfield::PasteSelectionClipboard() { void Textfield::UpdateSelectionClipboard() { #if defined(OS_LINUX) && !defined(OS_CHROMEOS) if (text_input_type_ != ui::TEXT_INPUT_TYPE_PASSWORD) { - ui::ScopedClipboardWriter(ui::CLIPBOARD_TYPE_SELECTION) + ui::ScopedClipboardWriter(ui::ClipboardType::kSelection) .WriteText(GetSelectedText()); if (controller_) - controller_->OnAfterCutOrCopy(ui::CLIPBOARD_TYPE_SELECTION); + controller_->OnAfterCutOrCopy(ui::ClipboardType::kSelection); } #endif } @@ -2270,7 +2272,7 @@ bool Textfield::Cut() { if (!read_only() && text_input_type_ != ui::TEXT_INPUT_TYPE_PASSWORD && model_->Cut()) { if (controller_) - controller_->OnAfterCutOrCopy(ui::CLIPBOARD_TYPE_COPY_PASTE); + controller_->OnAfterCutOrCopy(ui::ClipboardType::kCopyPaste); return true; } return false; @@ -2279,7 +2281,7 @@ bool Textfield::Cut() { bool Textfield::Copy() { if (text_input_type_ != ui::TEXT_INPUT_TYPE_PASSWORD && model_->Copy()) { if (controller_) - controller_->OnAfterCutOrCopy(ui::CLIPBOARD_TYPE_COPY_PASTE); + controller_->OnAfterCutOrCopy(ui::ClipboardType::kCopyPaste); return true; } return false; @@ -2390,4 +2392,8 @@ void Textfield::OnEnabledChanged() { GetInputMethod()->OnTextInputTypeChanged(this); } +BEGIN_METADATA(Textfield) +METADATA_PARENT_CLASS(View) +END_METADATA() + } // namespace views diff --git a/chromium/ui/views/controls/textfield/textfield.h b/chromium/ui/views/controls/textfield/textfield.h index 0a88abfda2e..363d2182cfb 100644 --- a/chromium/ui/views/controls/textfield/textfield.h +++ b/chromium/ui/views/controls/textfield/textfield.h @@ -71,8 +71,13 @@ class VIEWS_EXPORT Textfield : public View, public ui::TouchEditable, public ui::TextInputClient { public: - // The textfield's class name. - static const char kViewClassName[]; + METADATA_HEADER(Textfield); + + // An enum giving different model properties unique keys for the + // OnPropertyChanged call. + enum ModelPropertyKey { + kTextProperty = 1, + }; // Returns the text cursor blink time, or 0 for no blinking. static base::TimeDelta GetCaretBlinkInterval(); @@ -242,6 +247,7 @@ class VIEWS_EXPORT Textfield : public View, // Set the accessible name of the text field. If the textfield has a visible // label, use SetAssociatedLabel() instead. void SetAccessibleName(const base::string16& name); + const base::string16& accessible_name() const { return accessible_name_; } // If the accessible name should be the same as the labelling view's text, // use this. It will set the accessible label relationship and copy the @@ -260,7 +266,6 @@ class VIEWS_EXPORT Textfield : public View, int GetBaseline() const override; gfx::Size CalculatePreferredSize() const override; gfx::Size GetMinimumSize() const override; - const char* GetClassName() const override; void SetBorder(std::unique_ptr<Border> b) override; gfx::NativeCursor GetCursor(const ui::MouseEvent& event) override; bool OnMousePressed(const ui::MouseEvent& event) override; @@ -294,6 +299,7 @@ class VIEWS_EXPORT Textfield : public View, // TextfieldModel::Delegate overrides: void OnCompositionTextConfirmedOrCleared() override; + void OnTextChanged() override; // ContextMenuController overrides: void ShowContextMenuForViewImpl(View* source, @@ -385,6 +391,9 @@ class VIEWS_EXPORT Textfield : public View, bool is_composition_committed) override; #endif + views::PropertyChangedSubscription AddTextChangedCallback( + views::PropertyChangedCallback callback); + protected: // Inserts or appends a character in response to an IME operation. virtual void DoInsertChar(base::char16 ch); @@ -668,7 +677,7 @@ class VIEWS_EXPORT Textfield : public View, base::Unretained(this))); // Used to bind callback functions to this object. - base::WeakPtrFactory<Textfield> weak_ptr_factory_; + base::WeakPtrFactory<Textfield> weak_ptr_factory_{this}; DISALLOW_COPY_AND_ASSIGN(Textfield); }; diff --git a/chromium/ui/views/controls/textfield/textfield_model.cc b/chromium/ui/views/controls/textfield/textfield_model.cc index 5b9f9db1e54..853914d0d50 100644 --- a/chromium/ui/views/controls/textfield/textfield_model.cc +++ b/chromium/ui/views/controls/textfield/textfield_model.cc @@ -522,8 +522,8 @@ bool TextfieldModel::Redo() { bool TextfieldModel::Cut() { if (!HasCompositionText() && HasSelection() && !render_text_->obscured()) { - ui::ScopedClipboardWriter( - ui::CLIPBOARD_TYPE_COPY_PASTE).WriteText(GetSelectedText()); + ui::ScopedClipboardWriter(ui::ClipboardType::kCopyPaste) + .WriteText(GetSelectedText()); // A trick to let undo/redo handle cursor correctly. // Undoing CUT moves the cursor to the end of the change rather // than beginning, unlike Delete/Backspace. @@ -539,8 +539,8 @@ bool TextfieldModel::Cut() { bool TextfieldModel::Copy() { if (!HasCompositionText() && HasSelection() && !render_text_->obscured()) { - ui::ScopedClipboardWriter( - ui::CLIPBOARD_TYPE_COPY_PASTE).WriteText(GetSelectedText()); + ui::ScopedClipboardWriter(ui::ClipboardType::kCopyPaste) + .WriteText(GetSelectedText()); return true; } return false; @@ -548,7 +548,7 @@ bool TextfieldModel::Copy() { bool TextfieldModel::Paste() { base::string16 text; - ui::Clipboard::GetForCurrentThread()->ReadText(ui::CLIPBOARD_TYPE_COPY_PASTE, + ui::Clipboard::GetForCurrentThread()->ReadText(ui::ClipboardType::kCopyPaste, &text); if (text.empty()) return false; @@ -641,7 +641,7 @@ void TextfieldModel::SetCompositionText( size_t cursor = GetCursorPosition(); base::string16 new_text = text(); - render_text_->SetText(new_text.insert(cursor, composition.text)); + SetRenderTextText(new_text.insert(cursor, composition.text)); composition_range_ = gfx::Range(cursor, cursor + composition.text.length()); // Don't render IME spans with thickness "kNone". if (composition.ime_text_spans.size() > 0 && @@ -669,7 +669,11 @@ void TextfieldModel::SetCompositionText( } void TextfieldModel::SetCompositionFromExistingText(const gfx::Range& range) { - DCHECK(!HasCompositionText()); + if (range.is_empty() || !gfx::Range(0, text().length()).Contains(range)) { + ClearComposition(); + return; + } + composition_range_ = range; render_text_->SetCompositionRange(range); } @@ -693,7 +697,7 @@ void TextfieldModel::CancelCompositionText() { gfx::Range range = composition_range_; ClearComposition(); base::string16 new_text = text(); - render_text_->SetText(new_text.erase(range.start(), range.length())); + SetRenderTextText(new_text.erase(range.start(), range.length())); render_text_->SetCursorPosition(range.start()); if (delegate_) delegate_->OnCompositionTextConfirmedOrCleared(); @@ -839,9 +843,9 @@ void TextfieldModel::ModifyText(size_t delete_from, base::string16 old_text = text(); ClearComposition(); if (delete_from != delete_to) - render_text_->SetText(old_text.erase(delete_from, delete_to - delete_from)); + SetRenderTextText(old_text.erase(delete_from, delete_to - delete_from)); if (!new_text.empty()) - render_text_->SetText(old_text.insert(new_text_insert_at, new_text)); + SetRenderTextText(old_text.insert(new_text_insert_at, new_text)); if (selection.start() == selection.end()) { render_text_->SetCursorPosition(selection.start()); } else { @@ -849,6 +853,12 @@ void TextfieldModel::ModifyText(size_t delete_from, } } +void TextfieldModel::SetRenderTextText(const base::string16& text) { + render_text_->SetText(text); + if (delegate_) + delegate_->OnTextChanged(); +} + // static void TextfieldModel::ClearKillBuffer() { SetKillBuffer(base::string16()); diff --git a/chromium/ui/views/controls/textfield/textfield_model.h b/chromium/ui/views/controls/textfield/textfield_model.h index c2391a35645..9efa1f36e03 100644 --- a/chromium/ui/views/controls/textfield/textfield_model.h +++ b/chromium/ui/views/controls/textfield/textfield_model.h @@ -52,6 +52,9 @@ class VIEWS_EXPORT TextfieldModel { // Called when the current composition text is confirmed or cleared. virtual void OnCompositionTextConfirmedOrCleared() = 0; + // Called any time that the text property is modified in TextfieldModel + virtual void OnTextChanged() {} + protected: virtual ~Delegate(); }; @@ -221,6 +224,8 @@ class VIEWS_EXPORT TextfieldModel { // Puts the text in the specified range into composition mode. // This method should not be called with composition text or an invalid range. + // The provided range is checked against the string's length, if |range| is + // out of bounds, the composition will be cleared. void SetCompositionFromExistingText(const gfx::Range& range); // Converts current composition text into final content. @@ -284,6 +289,9 @@ class VIEWS_EXPORT TextfieldModel { size_t new_text_insert_at, gfx::Range selection); + // Calls render_text->SetText() and delegate's callback. + void SetRenderTextText(const base::string16& text); + void ClearComposition(); // Clears the kill buffer. Used to clear global state between tests. diff --git a/chromium/ui/views/controls/textfield/textfield_model_unittest.cc b/chromium/ui/views/controls/textfield/textfield_model_unittest.cc index 27a7566b866..38174b5ec8f 100644 --- a/chromium/ui/views/controls/textfield/textfield_model_unittest.cc +++ b/chromium/ui/views/controls/textfield/textfield_model_unittest.cc @@ -591,7 +591,7 @@ TEST_F(TextfieldModelTest, Clipboard) { ui::Clipboard* clipboard = ui::Clipboard::GetForCurrentThread(); const base::string16 initial_clipboard_text = base::ASCIIToUTF16("initial text"); - ui::ScopedClipboardWriter(ui::CLIPBOARD_TYPE_COPY_PASTE) + ui::ScopedClipboardWriter(ui::ClipboardType::kCopyPaste) .WriteText(initial_clipboard_text); base::string16 clipboard_text; @@ -601,14 +601,14 @@ TEST_F(TextfieldModelTest, Clipboard) { // Cut with an empty selection should do nothing. model.MoveCursor(gfx::LINE_BREAK, gfx::CURSOR_RIGHT, gfx::SELECTION_NONE); EXPECT_FALSE(model.Cut()); - clipboard->ReadText(ui::CLIPBOARD_TYPE_COPY_PASTE, &clipboard_text); + clipboard->ReadText(ui::ClipboardType::kCopyPaste, &clipboard_text); EXPECT_EQ(initial_clipboard_text, clipboard_text); EXPECT_STR_EQ("HELLO WORLD", model.text()); EXPECT_EQ(11U, model.GetCursorPosition()); // Copy with an empty selection should do nothing. model.Copy(); - clipboard->ReadText(ui::CLIPBOARD_TYPE_COPY_PASTE, &clipboard_text); + clipboard->ReadText(ui::ClipboardType::kCopyPaste, &clipboard_text); EXPECT_EQ(initial_clipboard_text, clipboard_text); EXPECT_STR_EQ("HELLO WORLD", model.text()); EXPECT_EQ(11U, model.GetCursorPosition()); @@ -617,7 +617,7 @@ TEST_F(TextfieldModelTest, Clipboard) { model.render_text()->SetObscured(true); model.SelectAll(false); EXPECT_FALSE(model.Cut()); - clipboard->ReadText(ui::CLIPBOARD_TYPE_COPY_PASTE, &clipboard_text); + clipboard->ReadText(ui::ClipboardType::kCopyPaste, &clipboard_text); EXPECT_EQ(initial_clipboard_text, clipboard_text); EXPECT_STR_EQ("HELLO WORLD", model.text()); EXPECT_STR_EQ("HELLO WORLD", model.GetSelectedText()); @@ -625,7 +625,7 @@ TEST_F(TextfieldModelTest, Clipboard) { // Copy on obscured (password) text should do nothing. model.SelectAll(false); EXPECT_FALSE(model.Copy()); - clipboard->ReadText(ui::CLIPBOARD_TYPE_COPY_PASTE, &clipboard_text); + clipboard->ReadText(ui::ClipboardType::kCopyPaste, &clipboard_text); EXPECT_EQ(initial_clipboard_text, clipboard_text); EXPECT_STR_EQ("HELLO WORLD", model.text()); EXPECT_STR_EQ("HELLO WORLD", model.GetSelectedText()); @@ -635,7 +635,7 @@ TEST_F(TextfieldModelTest, Clipboard) { model.MoveCursor(gfx::LINE_BREAK, gfx::CURSOR_RIGHT, gfx::SELECTION_NONE); model.MoveCursor(gfx::WORD_BREAK, gfx::CURSOR_LEFT, gfx::SELECTION_RETAIN); EXPECT_TRUE(model.Cut()); - clipboard->ReadText(ui::CLIPBOARD_TYPE_COPY_PASTE, &clipboard_text); + clipboard->ReadText(ui::ClipboardType::kCopyPaste, &clipboard_text); EXPECT_STR_EQ("WORLD", clipboard_text); EXPECT_STR_EQ("HELLO ", model.text()); EXPECT_EQ(6U, model.GetCursorPosition()); @@ -643,7 +643,7 @@ TEST_F(TextfieldModelTest, Clipboard) { // Copy with non-empty selection. model.SelectAll(false); EXPECT_TRUE(model.Copy()); - clipboard->ReadText(ui::CLIPBOARD_TYPE_COPY_PASTE, &clipboard_text); + clipboard->ReadText(ui::ClipboardType::kCopyPaste, &clipboard_text); EXPECT_STR_EQ("HELLO ", clipboard_text); EXPECT_STR_EQ("HELLO ", model.text()); EXPECT_EQ(6U, model.GetCursorPosition()); @@ -1741,7 +1741,7 @@ TEST_F(TextfieldModelTest, UndoRedo_CompositionText) { TEST_F(TextfieldModelTest, Clipboard_WhiteSpaceStringTest) { // Test 1 // Clipboard text with a leading tab should be pasted with the tab stripped. - ui::ScopedClipboardWriter(ui::CLIPBOARD_TYPE_COPY_PASTE) + ui::ScopedClipboardWriter(ui::ClipboardType::kCopyPaste) .WriteText(base::ASCIIToUTF16("\tB")); TextfieldModel model(nullptr); @@ -1760,7 +1760,7 @@ TEST_F(TextfieldModelTest, Clipboard_WhiteSpaceStringTest) { // Test 2 // Clipboard text with multiple leading tabs and spaces should be pasted with // all tabs and spaces stripped. - ui::ScopedClipboardWriter(ui::CLIPBOARD_TYPE_COPY_PASTE) + ui::ScopedClipboardWriter(ui::ClipboardType::kCopyPaste) .WriteText(base::ASCIIToUTF16("\t\t\t B")); model.Append(base::ASCIIToUTF16("HELLO WORLD")); @@ -1777,7 +1777,7 @@ TEST_F(TextfieldModelTest, Clipboard_WhiteSpaceStringTest) { // Test 3 // Clipboard text with multiple tabs separating the words should be pasted // with one space replacing all tabs. - ui::ScopedClipboardWriter(ui::CLIPBOARD_TYPE_COPY_PASTE) + ui::ScopedClipboardWriter(ui::ClipboardType::kCopyPaste) .WriteText(base::ASCIIToUTF16("FOO \t\t BAR")); model.Append(base::ASCIIToUTF16("HELLO WORLD")); @@ -1795,7 +1795,7 @@ TEST_F(TextfieldModelTest, Clipboard_WhiteSpaceStringTest) { // Clipboard text with multiple leading tabs and multiple tabs separating // the words should be pasted with the leading tabs stripped and one space // replacing the intermediate tabs. - ui::ScopedClipboardWriter(ui::CLIPBOARD_TYPE_COPY_PASTE) + ui::ScopedClipboardWriter(ui::ClipboardType::kCopyPaste) .WriteText(base::ASCIIToUTF16("\t\tFOO \t\t BAR")); EXPECT_TRUE(model.Paste()); @@ -1808,7 +1808,7 @@ TEST_F(TextfieldModelTest, Clipboard_WhiteSpaceStringTest) { // Test 5 // Clipboard text with multiple trailing tabs should be pasted with all // trailing tabs stripped. - ui::ScopedClipboardWriter(ui::CLIPBOARD_TYPE_COPY_PASTE) + ui::ScopedClipboardWriter(ui::ClipboardType::kCopyPaste) .WriteText(base::ASCIIToUTF16("FOO BAR\t\t\t")); EXPECT_TRUE(model.Paste()); EXPECT_STR_EQ("FOO BAR", model.text()); @@ -1820,7 +1820,7 @@ TEST_F(TextfieldModelTest, Clipboard_WhiteSpaceStringTest) { // Test 6 // Clipboard text with only spaces and tabs should be pasted as a single // space. - ui::ScopedClipboardWriter(ui::CLIPBOARD_TYPE_COPY_PASTE) + ui::ScopedClipboardWriter(ui::ClipboardType::kCopyPaste) .WriteText(base::ASCIIToUTF16(" \t\t")); EXPECT_TRUE(model.Paste()); EXPECT_STR_EQ(" ", model.text()); @@ -1832,7 +1832,7 @@ TEST_F(TextfieldModelTest, Clipboard_WhiteSpaceStringTest) { // Test 7 // Clipboard text with lots of spaces between words should have all spaces // replaced with a single space. - ui::ScopedClipboardWriter(ui::CLIPBOARD_TYPE_COPY_PASTE) + ui::ScopedClipboardWriter(ui::ClipboardType::kCopyPaste) .WriteText(base::ASCIIToUTF16("FOO BAR")); EXPECT_TRUE(model.Paste()); EXPECT_STR_EQ("FOO BAR", model.text()); @@ -1954,6 +1954,9 @@ TEST_F(TextfieldModelTest, SetCompositionFromExistingText) { TextfieldModel model(nullptr); model.SetText(base::ASCIIToUTF16("abcde")); + model.SetCompositionFromExistingText(gfx::Range(0, 1)); + EXPECT_TRUE(model.HasCompositionText()); + model.SetCompositionFromExistingText(gfx::Range(1, 3)); EXPECT_TRUE(model.HasCompositionText()); @@ -1963,4 +1966,28 @@ TEST_F(TextfieldModelTest, SetCompositionFromExistingText) { EXPECT_STR_EQ("a123de", model.text()); } +TEST_F(TextfieldModelTest, SetCompositionFromExistingText_Empty) { + TextfieldModel model(nullptr); + model.SetText(base::ASCIIToUTF16("abc")); + + model.SetCompositionFromExistingText(gfx::Range(0, 2)); + EXPECT_TRUE(model.HasCompositionText()); + + model.SetCompositionFromExistingText(gfx::Range(1, 1)); + EXPECT_FALSE(model.HasCompositionText()); + EXPECT_STR_EQ("abc", model.text()); +} + +TEST_F(TextfieldModelTest, SetCompositionFromExistingText_OutOfBounds) { + TextfieldModel model(nullptr); + model.SetText(base::string16()); + + model.SetCompositionFromExistingText(gfx::Range(0, 2)); + EXPECT_FALSE(model.HasCompositionText()); + + model.SetText(base::ASCIIToUTF16("abc")); + model.SetCompositionFromExistingText(gfx::Range(1, 4)); + EXPECT_FALSE(model.HasCompositionText()); +} + } // namespace views diff --git a/chromium/ui/views/controls/textfield/textfield_unittest.cc b/chromium/ui/views/controls/textfield/textfield_unittest.cc index 108b0f746e2..1cd9cb3d45d 100644 --- a/chromium/ui/views/controls/textfield/textfield_unittest.cc +++ b/chromium/ui/views/controls/textfield/textfield_unittest.cc @@ -11,7 +11,6 @@ #include <string> #include <vector> -#include "base/bind_helpers.h" #include "base/command_line.h" #include "base/format_macros.h" #include "base/i18n/rtl.h" @@ -22,7 +21,6 @@ #include "base/strings/utf_string_conversions.h" #include "build/build_config.h" #include "ui/accessibility/ax_node_data.h" -#include "ui/aura/window.h" #include "ui/base/clipboard/clipboard.h" #include "ui/base/clipboard/scoped_clipboard_writer.h" #include "ui/base/clipboard/test/test_clipboard.h" @@ -67,6 +65,7 @@ #endif #if defined(OS_CHROMEOS) +#include "ui/aura/window.h" #include "ui/wm/core/ime_util_chromeos.h" #endif @@ -150,7 +149,7 @@ ui::EventDispatchDetails MockInputMethod::DispatchKeyEvent(ui::KeyEvent* key) { // which trigger the appropriate NSResponder action messages for composition. #if defined(OS_MACOSX) if (key->is_char()) - return DispatchKeyEventPostIME(key, base::NullCallback()); + return DispatchKeyEventPostIME(key); #endif // Checks whether the key event is from EventGenerator on Windows which will @@ -170,9 +169,9 @@ ui::EventDispatchDetails MockInputMethod::DispatchKeyEvent(ui::KeyEvent* key) { ui::KeyEvent mock_key(ui::ET_KEY_PRESSED, ui::VKEY_PROCESSKEY, key->flags()); - dispatch_details = DispatchKeyEventPostIME(&mock_key, base::NullCallback()); + dispatch_details = DispatchKeyEventPostIME(&mock_key); } else { - dispatch_details = DispatchKeyEventPostIME(key, base::NullCallback()); + dispatch_details = DispatchKeyEventPostIME(key); } if (key->handled() || dispatch_details.dispatcher_destroyed) @@ -417,7 +416,7 @@ class TextfieldTest : public ViewsTestBase, public TextfieldController { ui::ClipboardType GetAndResetCopiedToClipboard() { ui::ClipboardType clipboard_type = copied_to_clipboard_; - copied_to_clipboard_ = ui::CLIPBOARD_TYPE_LAST; + copied_to_clipboard_ = ui::ClipboardType::kMaxValue; return clipboard_type; } @@ -731,7 +730,7 @@ class TextfieldTest : public ViewsTestBase, public TextfieldController { menu->IsEnabledAt(menu_index++ /* CUT */)); EXPECT_EQ(textfield_has_selection, menu->IsEnabledAt(menu_index++ /* COPY */)); - EXPECT_NE(GetClipboardText(ui::CLIPBOARD_TYPE_COPY_PASTE).empty(), + EXPECT_NE(GetClipboardText(ui::ClipboardType::kCopyPaste).empty(), menu->IsEnabledAt(menu_index++ /* PASTE */)); EXPECT_EQ(textfield_has_selection, menu->IsEnabledAt(menu_index++ /* DELETE */)); @@ -804,7 +803,7 @@ class TextfieldTest : public ViewsTestBase, public TextfieldController { // Position of the mouse for synthetic mouse events. gfx::Point mouse_position_; - ui::ClipboardType copied_to_clipboard_ = ui::CLIPBOARD_TYPE_LAST; + ui::ClipboardType copied_to_clipboard_ = ui::ClipboardType::kMaxValue; std::unique_ptr<ui::test::EventGenerator> event_generator_; private: @@ -1284,7 +1283,7 @@ TEST_F(TextfieldTest, PasswordTest) { EXPECT_STR_EQ("password", textfield_->text()); EXPECT_TRUE(last_contents_.empty()); model_->SelectAll(false); - SetClipboardText(ui::CLIPBOARD_TYPE_COPY_PASTE, "foo"); + SetClipboardText(ui::ClipboardType::kCopyPaste, "foo"); // Cut and copy should be disabled. EXPECT_FALSE(textfield_->IsCommandIdEnabled(IDS_APP_CUT)); @@ -1294,7 +1293,7 @@ TEST_F(TextfieldTest, PasswordTest) { textfield_->ExecuteCommand(IDS_APP_COPY, 0); SendKeyEvent(ui::VKEY_C, false, true); SendAlternateCopy(); - EXPECT_STR_EQ("foo", GetClipboardText(ui::CLIPBOARD_TYPE_COPY_PASTE)); + EXPECT_STR_EQ("foo", GetClipboardText(ui::ClipboardType::kCopyPaste)); EXPECT_STR_EQ("password", textfield_->text()); // [Shift]+[Delete] should just delete without copying text to the clipboard. textfield_->SelectAll(false); @@ -1305,7 +1304,7 @@ TEST_F(TextfieldTest, PasswordTest) { textfield_->ExecuteCommand(IDS_APP_PASTE, 0); SendKeyEvent(ui::VKEY_V, false, true); SendAlternatePaste(); - EXPECT_STR_EQ("foo", GetClipboardText(ui::CLIPBOARD_TYPE_COPY_PASTE)); + EXPECT_STR_EQ("foo", GetClipboardText(ui::ClipboardType::kCopyPaste)); EXPECT_STR_EQ("foofoofoo", textfield_->text()); } @@ -1580,7 +1579,7 @@ TEST_F(TextfieldTest, ContextMenuDisplayTest) { EXPECT_TRUE(textfield_->context_menu_controller()); textfield_->SetText(ASCIIToUTF16("hello world")); - ui::Clipboard::GetForCurrentThread()->Clear(ui::CLIPBOARD_TYPE_COPY_PASTE); + ui::Clipboard::GetForCurrentThread()->Clear(ui::ClipboardType::kCopyPaste); textfield_->ClearEditHistory(); EXPECT_TRUE(GetContextMenuModel()); VerifyTextfieldContextMenuContents(false, false, GetContextMenuModel()); @@ -1595,7 +1594,7 @@ TEST_F(TextfieldTest, ContextMenuDisplayTest) { VerifyTextfieldContextMenuContents(true, true, GetContextMenuModel()); // Exercise the "paste enabled?" check in the verifier. - SetClipboardText(ui::CLIPBOARD_TYPE_COPY_PASTE, "Test"); + SetClipboardText(ui::ClipboardType::kCopyPaste, "Test"); VerifyTextfieldContextMenuContents(true, true, GetContextMenuModel()); } @@ -1966,12 +1965,12 @@ TEST_F(TextfieldTest, ReadOnlyTest) { EXPECT_STR_EQ("read only", textfield_->GetSelectedText()); // Cut should be disabled. - SetClipboardText(ui::CLIPBOARD_TYPE_COPY_PASTE, "Test"); + SetClipboardText(ui::ClipboardType::kCopyPaste, "Test"); EXPECT_FALSE(textfield_->IsCommandIdEnabled(IDS_APP_CUT)); textfield_->ExecuteCommand(IDS_APP_CUT, 0); SendKeyEvent(ui::VKEY_X, false, true); SendAlternateCut(); - EXPECT_STR_EQ("Test", GetClipboardText(ui::CLIPBOARD_TYPE_COPY_PASTE)); + EXPECT_STR_EQ("Test", GetClipboardText(ui::ClipboardType::kCopyPaste)); EXPECT_STR_EQ("read only", textfield_->text()); // Paste should be disabled. @@ -1982,16 +1981,16 @@ TEST_F(TextfieldTest, ReadOnlyTest) { EXPECT_STR_EQ("read only", textfield_->text()); // Copy should work normally. - SetClipboardText(ui::CLIPBOARD_TYPE_COPY_PASTE, "Test"); + SetClipboardText(ui::ClipboardType::kCopyPaste, "Test"); EXPECT_TRUE(textfield_->IsCommandIdEnabled(IDS_APP_COPY)); textfield_->ExecuteCommand(IDS_APP_COPY, 0); - EXPECT_STR_EQ("read only", GetClipboardText(ui::CLIPBOARD_TYPE_COPY_PASTE)); - SetClipboardText(ui::CLIPBOARD_TYPE_COPY_PASTE, "Test"); + EXPECT_STR_EQ("read only", GetClipboardText(ui::ClipboardType::kCopyPaste)); + SetClipboardText(ui::ClipboardType::kCopyPaste, "Test"); SendKeyEvent(ui::VKEY_C, false, true); - EXPECT_STR_EQ("read only", GetClipboardText(ui::CLIPBOARD_TYPE_COPY_PASTE)); - SetClipboardText(ui::CLIPBOARD_TYPE_COPY_PASTE, "Test"); + EXPECT_STR_EQ("read only", GetClipboardText(ui::ClipboardType::kCopyPaste)); + SetClipboardText(ui::ClipboardType::kCopyPaste, "Test"); SendAlternateCopy(); - EXPECT_STR_EQ("read only", GetClipboardText(ui::CLIPBOARD_TYPE_COPY_PASTE)); + EXPECT_STR_EQ("read only", GetClipboardText(ui::ClipboardType::kCopyPaste)); // SetText should work even in read only mode. textfield_->SetText(ASCIIToUTF16(" four five six ")); @@ -2305,70 +2304,70 @@ TEST_F(TextfieldTest, CutCopyPaste) { textfield_->SelectAll(false); EXPECT_TRUE(textfield_->IsCommandIdEnabled(IDS_APP_CUT)); textfield_->ExecuteCommand(IDS_APP_CUT, 0); - EXPECT_STR_EQ("123", GetClipboardText(ui::CLIPBOARD_TYPE_COPY_PASTE)); + EXPECT_STR_EQ("123", GetClipboardText(ui::ClipboardType::kCopyPaste)); EXPECT_STR_EQ("", textfield_->text()); - EXPECT_EQ(ui::CLIPBOARD_TYPE_COPY_PASTE, GetAndResetCopiedToClipboard()); + EXPECT_EQ(ui::ClipboardType::kCopyPaste, GetAndResetCopiedToClipboard()); // Ensure [Ctrl]+[x] cuts and [Ctrl]+[Alt][x] does nothing. textfield_->SetText(ASCIIToUTF16("456")); textfield_->SelectAll(false); SendKeyEvent(ui::VKEY_X, true, false, true, false); - EXPECT_STR_EQ("123", GetClipboardText(ui::CLIPBOARD_TYPE_COPY_PASTE)); + EXPECT_STR_EQ("123", GetClipboardText(ui::ClipboardType::kCopyPaste)); EXPECT_STR_EQ("456", textfield_->text()); - EXPECT_EQ(ui::CLIPBOARD_TYPE_LAST, GetAndResetCopiedToClipboard()); + EXPECT_EQ(ui::ClipboardType::kMaxValue, GetAndResetCopiedToClipboard()); SendKeyEvent(ui::VKEY_X, false, true); - EXPECT_STR_EQ("456", GetClipboardText(ui::CLIPBOARD_TYPE_COPY_PASTE)); + EXPECT_STR_EQ("456", GetClipboardText(ui::ClipboardType::kCopyPaste)); EXPECT_STR_EQ("", textfield_->text()); - EXPECT_EQ(ui::CLIPBOARD_TYPE_COPY_PASTE, GetAndResetCopiedToClipboard()); + EXPECT_EQ(ui::ClipboardType::kCopyPaste, GetAndResetCopiedToClipboard()); // Ensure [Shift]+[Delete] cuts. textfield_->SetText(ASCIIToUTF16("123")); textfield_->SelectAll(false); SendAlternateCut(); - EXPECT_STR_EQ("123", GetClipboardText(ui::CLIPBOARD_TYPE_COPY_PASTE)); + EXPECT_STR_EQ("123", GetClipboardText(ui::ClipboardType::kCopyPaste)); EXPECT_STR_EQ("", textfield_->text()); - EXPECT_EQ(ui::CLIPBOARD_TYPE_COPY_PASTE, GetAndResetCopiedToClipboard()); + EXPECT_EQ(ui::ClipboardType::kCopyPaste, GetAndResetCopiedToClipboard()); // Reset clipboard text. - SetClipboardText(ui::CLIPBOARD_TYPE_COPY_PASTE, ""); + SetClipboardText(ui::ClipboardType::kCopyPaste, ""); // Ensure [Shift]+[Delete] is a no-op in case there is no selection. textfield_->SetText(ASCIIToUTF16("123")); textfield_->SelectRange(gfx::Range(0)); SendAlternateCut(); - EXPECT_STR_EQ("", GetClipboardText(ui::CLIPBOARD_TYPE_COPY_PASTE)); + EXPECT_STR_EQ("", GetClipboardText(ui::ClipboardType::kCopyPaste)); EXPECT_STR_EQ("123", textfield_->text()); - EXPECT_EQ(ui::CLIPBOARD_TYPE_LAST, GetAndResetCopiedToClipboard()); + EXPECT_EQ(ui::ClipboardType::kMaxValue, GetAndResetCopiedToClipboard()); // Ensure IDS_APP_COPY copies. textfield_->SetText(ASCIIToUTF16("789")); textfield_->SelectAll(false); EXPECT_TRUE(textfield_->IsCommandIdEnabled(IDS_APP_COPY)); textfield_->ExecuteCommand(IDS_APP_COPY, 0); - EXPECT_STR_EQ("789", GetClipboardText(ui::CLIPBOARD_TYPE_COPY_PASTE)); - EXPECT_EQ(ui::CLIPBOARD_TYPE_COPY_PASTE, GetAndResetCopiedToClipboard()); + EXPECT_STR_EQ("789", GetClipboardText(ui::ClipboardType::kCopyPaste)); + EXPECT_EQ(ui::ClipboardType::kCopyPaste, GetAndResetCopiedToClipboard()); // Ensure [Ctrl]+[c] copies and [Ctrl]+[Alt][c] does nothing. textfield_->SetText(ASCIIToUTF16("012")); textfield_->SelectAll(false); SendKeyEvent(ui::VKEY_C, true, false, true, false); - EXPECT_STR_EQ("789", GetClipboardText(ui::CLIPBOARD_TYPE_COPY_PASTE)); - EXPECT_EQ(ui::CLIPBOARD_TYPE_LAST, GetAndResetCopiedToClipboard()); + EXPECT_STR_EQ("789", GetClipboardText(ui::ClipboardType::kCopyPaste)); + EXPECT_EQ(ui::ClipboardType::kMaxValue, GetAndResetCopiedToClipboard()); SendKeyEvent(ui::VKEY_C, false, true); - EXPECT_STR_EQ("012", GetClipboardText(ui::CLIPBOARD_TYPE_COPY_PASTE)); - EXPECT_EQ(ui::CLIPBOARD_TYPE_COPY_PASTE, GetAndResetCopiedToClipboard()); + EXPECT_STR_EQ("012", GetClipboardText(ui::ClipboardType::kCopyPaste)); + EXPECT_EQ(ui::ClipboardType::kCopyPaste, GetAndResetCopiedToClipboard()); // Ensure [Ctrl]+[Insert] copies. textfield_->SetText(ASCIIToUTF16("345")); textfield_->SelectAll(false); SendAlternateCopy(); - EXPECT_STR_EQ("345", GetClipboardText(ui::CLIPBOARD_TYPE_COPY_PASTE)); + EXPECT_STR_EQ("345", GetClipboardText(ui::ClipboardType::kCopyPaste)); EXPECT_STR_EQ("345", textfield_->text()); - EXPECT_EQ(ui::CLIPBOARD_TYPE_COPY_PASTE, GetAndResetCopiedToClipboard()); + EXPECT_EQ(ui::ClipboardType::kCopyPaste, GetAndResetCopiedToClipboard()); // Ensure IDS_APP_PASTE, [Ctrl]+[V], and [Shift]+[Insert] pastes; // also ensure that [Ctrl]+[Alt]+[V] does nothing. - SetClipboardText(ui::CLIPBOARD_TYPE_COPY_PASTE, "abc"); + SetClipboardText(ui::ClipboardType::kCopyPaste, "abc"); textfield_->SetText(base::string16()); EXPECT_TRUE(textfield_->IsCommandIdEnabled(IDS_APP_PASTE)); textfield_->ExecuteCommand(IDS_APP_PASTE, 0); @@ -2383,9 +2382,9 @@ TEST_F(TextfieldTest, CutCopyPaste) { // Ensure [Ctrl]+[Shift]+[Insert] is a no-op. textfield_->SelectAll(false); SendKeyEvent(ui::VKEY_INSERT, true, true); - EXPECT_STR_EQ("abc", GetClipboardText(ui::CLIPBOARD_TYPE_COPY_PASTE)); + EXPECT_STR_EQ("abc", GetClipboardText(ui::ClipboardType::kCopyPaste)); EXPECT_STR_EQ("abcabcabc", textfield_->text()); - EXPECT_EQ(ui::CLIPBOARD_TYPE_LAST, GetAndResetCopiedToClipboard()); + EXPECT_EQ(ui::ClipboardType::kMaxValue, GetAndResetCopiedToClipboard()); } TEST_F(TextfieldTest, CutCopyPasteWithEditCommand) { @@ -2864,13 +2863,13 @@ TEST_F(TextfieldTest, SelectionClipboard) { ui::EF_LEFT_MOUSE_BUTTON); textfield_->OnMouseReleased(release); EXPECT_EQ(gfx::Range(1, 3), textfield_->GetSelectedRange()); - EXPECT_STR_EQ("12", GetClipboardText(ui::CLIPBOARD_TYPE_SELECTION)); + EXPECT_STR_EQ("12", GetClipboardText(ui::ClipboardType::kSelection)); // Select-all should update the selection clipboard. SendKeyEvent(ui::VKEY_A, false, true); EXPECT_EQ(gfx::Range(0, 4), textfield_->GetSelectedRange()); - EXPECT_STR_EQ("0123", GetClipboardText(ui::CLIPBOARD_TYPE_SELECTION)); - EXPECT_EQ(ui::CLIPBOARD_TYPE_SELECTION, GetAndResetCopiedToClipboard()); + EXPECT_STR_EQ("0123", GetClipboardText(ui::ClipboardType::kSelection)); + EXPECT_EQ(ui::ClipboardType::kSelection, GetAndResetCopiedToClipboard()); // Shift-click selection modifications should update the clipboard. NonClientMouseClick(); @@ -2887,25 +2886,25 @@ TEST_F(TextfieldTest, SelectionClipboard) { ui::EF_LEFT_MOUSE_BUTTON); textfield_->OnMouseReleased(release_2); EXPECT_EQ(gfx::Range(0, 2), textfield_->GetSelectedRange()); - EXPECT_STR_EQ("01", GetClipboardText(ui::CLIPBOARD_TYPE_SELECTION)); - EXPECT_EQ(ui::CLIPBOARD_TYPE_SELECTION, GetAndResetCopiedToClipboard()); + EXPECT_STR_EQ("01", GetClipboardText(ui::ClipboardType::kSelection)); + EXPECT_EQ(ui::ClipboardType::kSelection, GetAndResetCopiedToClipboard()); // Shift-Left/Right should update the selection clipboard. SendKeyEvent(ui::VKEY_RIGHT, true, false); - EXPECT_STR_EQ("012", GetClipboardText(ui::CLIPBOARD_TYPE_SELECTION)); - EXPECT_EQ(ui::CLIPBOARD_TYPE_SELECTION, GetAndResetCopiedToClipboard()); + EXPECT_STR_EQ("012", GetClipboardText(ui::ClipboardType::kSelection)); + EXPECT_EQ(ui::ClipboardType::kSelection, GetAndResetCopiedToClipboard()); SendKeyEvent(ui::VKEY_LEFT, true, false); - EXPECT_STR_EQ("01", GetClipboardText(ui::CLIPBOARD_TYPE_SELECTION)); - EXPECT_EQ(ui::CLIPBOARD_TYPE_SELECTION, GetAndResetCopiedToClipboard()); + EXPECT_STR_EQ("01", GetClipboardText(ui::ClipboardType::kSelection)); + EXPECT_EQ(ui::ClipboardType::kSelection, GetAndResetCopiedToClipboard()); SendKeyEvent(ui::VKEY_RIGHT, true, true); - EXPECT_STR_EQ("0123", GetClipboardText(ui::CLIPBOARD_TYPE_SELECTION)); - EXPECT_EQ(ui::CLIPBOARD_TYPE_SELECTION, GetAndResetCopiedToClipboard()); + EXPECT_STR_EQ("0123", GetClipboardText(ui::ClipboardType::kSelection)); + EXPECT_EQ(ui::ClipboardType::kSelection, GetAndResetCopiedToClipboard()); // Moving the cursor without a selection should not change the clipboard. SendKeyEvent(ui::VKEY_LEFT, false, false); EXPECT_EQ(gfx::Range(0, 0), textfield_->GetSelectedRange()); - EXPECT_STR_EQ("0123", GetClipboardText(ui::CLIPBOARD_TYPE_SELECTION)); - EXPECT_EQ(ui::CLIPBOARD_TYPE_LAST, GetAndResetCopiedToClipboard()); + EXPECT_STR_EQ("0123", GetClipboardText(ui::ClipboardType::kSelection)); + EXPECT_EQ(ui::ClipboardType::kMaxValue, GetAndResetCopiedToClipboard()); // Middle clicking should paste at the mouse (not cursor) location. // The cursor should be placed at the end of the pasted text. @@ -2915,7 +2914,7 @@ TEST_F(TextfieldTest, SelectionClipboard) { textfield_->OnMousePressed(middle); EXPECT_STR_EQ("01230123", textfield_->text()); EXPECT_EQ(gfx::Range(8, 8), textfield_->GetSelectedRange()); - EXPECT_STR_EQ("0123", GetClipboardText(ui::CLIPBOARD_TYPE_SELECTION)); + EXPECT_STR_EQ("0123", GetClipboardText(ui::ClipboardType::kSelection)); // Middle clicking on an unfocused textfield should focus it and paste. textfield_->GetFocusManager()->ClearFocus(); @@ -2924,27 +2923,27 @@ TEST_F(TextfieldTest, SelectionClipboard) { EXPECT_TRUE(textfield_->HasFocus()); EXPECT_STR_EQ("012301230123", textfield_->text()); EXPECT_EQ(gfx::Range(8, 8), textfield_->GetSelectedRange()); - EXPECT_STR_EQ("0123", GetClipboardText(ui::CLIPBOARD_TYPE_SELECTION)); + EXPECT_STR_EQ("0123", GetClipboardText(ui::ClipboardType::kSelection)); // Middle clicking with an empty selection clipboard should still focus. - SetClipboardText(ui::CLIPBOARD_TYPE_SELECTION, std::string()); + SetClipboardText(ui::ClipboardType::kSelection, std::string()); textfield_->GetFocusManager()->ClearFocus(); EXPECT_FALSE(textfield_->HasFocus()); textfield_->OnMousePressed(middle); EXPECT_TRUE(textfield_->HasFocus()); EXPECT_STR_EQ("012301230123", textfield_->text()); EXPECT_EQ(gfx::Range(4, 4), textfield_->GetSelectedRange()); - EXPECT_TRUE(GetClipboardText(ui::CLIPBOARD_TYPE_SELECTION).empty()); + EXPECT_TRUE(GetClipboardText(ui::ClipboardType::kSelection).empty()); // Middle clicking in the selection should insert the selection clipboard // contents into the middle of the selection, and move the cursor to the end // of the pasted content. - SetClipboardText(ui::CLIPBOARD_TYPE_COPY_PASTE, "foo"); + SetClipboardText(ui::ClipboardType::kCopyPaste, "foo"); textfield_->SelectRange(gfx::Range(2, 6)); textfield_->OnMousePressed(middle); EXPECT_STR_EQ("0123foo01230123", textfield_->text()); EXPECT_EQ(gfx::Range(7, 7), textfield_->GetSelectedRange()); - EXPECT_STR_EQ("foo", GetClipboardText(ui::CLIPBOARD_TYPE_SELECTION)); + EXPECT_STR_EQ("foo", GetClipboardText(ui::ClipboardType::kSelection)); // Double and triple clicking should update the clipboard contents. textfield_->SetText(ASCIIToUTF16("ab cd ef")); @@ -2964,25 +2963,25 @@ TEST_F(TextfieldTest, SelectionClipboard) { textfield_->OnMousePressed(double_click); textfield_->OnMouseReleased(release_word); EXPECT_EQ(gfx::Range(3, 5), textfield_->GetSelectedRange()); - EXPECT_STR_EQ("cd", GetClipboardText(ui::CLIPBOARD_TYPE_SELECTION)); - EXPECT_EQ(ui::CLIPBOARD_TYPE_SELECTION, GetAndResetCopiedToClipboard()); + EXPECT_STR_EQ("cd", GetClipboardText(ui::ClipboardType::kSelection)); + EXPECT_EQ(ui::ClipboardType::kSelection, GetAndResetCopiedToClipboard()); textfield_->OnMousePressed(press_word); textfield_->OnMouseReleased(release_word); EXPECT_EQ(gfx::Range(0, 8), textfield_->GetSelectedRange()); - EXPECT_STR_EQ("ab cd ef", GetClipboardText(ui::CLIPBOARD_TYPE_SELECTION)); - EXPECT_EQ(ui::CLIPBOARD_TYPE_SELECTION, GetAndResetCopiedToClipboard()); + EXPECT_STR_EQ("ab cd ef", GetClipboardText(ui::ClipboardType::kSelection)); + EXPECT_EQ(ui::ClipboardType::kSelection, GetAndResetCopiedToClipboard()); // Selecting a range of text without any user interaction should not change // the clipboard content. textfield_->SelectRange(gfx::Range(0, 3)); EXPECT_STR_EQ("ab ", textfield_->GetSelectedText()); - EXPECT_STR_EQ("ab cd ef", GetClipboardText(ui::CLIPBOARD_TYPE_SELECTION)); - EXPECT_EQ(ui::CLIPBOARD_TYPE_LAST, GetAndResetCopiedToClipboard()); + EXPECT_STR_EQ("ab cd ef", GetClipboardText(ui::ClipboardType::kSelection)); + EXPECT_EQ(ui::ClipboardType::kMaxValue, GetAndResetCopiedToClipboard()); - SetClipboardText(ui::CLIPBOARD_TYPE_SELECTION, "other"); + SetClipboardText(ui::ClipboardType::kSelection, "other"); textfield_->SelectAll(false); - EXPECT_STR_EQ("other", GetClipboardText(ui::CLIPBOARD_TYPE_SELECTION)); - EXPECT_EQ(ui::CLIPBOARD_TYPE_LAST, GetAndResetCopiedToClipboard()); + EXPECT_STR_EQ("other", GetClipboardText(ui::ClipboardType::kSelection)); + EXPECT_EQ(ui::ClipboardType::kMaxValue, GetAndResetCopiedToClipboard()); } // Verify that the selection clipboard is not updated for selections on a @@ -2995,8 +2994,8 @@ TEST_F(TextfieldTest, SelectionClipboard_Password) { // textfield. SendKeyEvent(ui::VKEY_A, false, true); EXPECT_EQ(gfx::Range(0, 4), textfield_->GetSelectedRange()); - EXPECT_STR_EQ("abcd", GetClipboardText(ui::CLIPBOARD_TYPE_SELECTION)); - EXPECT_EQ(ui::CLIPBOARD_TYPE_SELECTION, GetAndResetCopiedToClipboard()); + EXPECT_STR_EQ("abcd", GetClipboardText(ui::ClipboardType::kSelection)); + EXPECT_EQ(ui::ClipboardType::kSelection, GetAndResetCopiedToClipboard()); // Move focus to the next textfield. widget_->GetFocusManager()->AdvanceFocus(false); @@ -3009,20 +3008,20 @@ TEST_F(TextfieldTest, SelectionClipboard_Password) { textfield2->SetTextInputType(ui::TEXT_INPUT_TYPE_PASSWORD); SendKeyEvent(ui::VKEY_A, false, true); EXPECT_EQ(gfx::Range(0, 4), textfield2->GetSelectedRange()); - EXPECT_STR_EQ("abcd", GetClipboardText(ui::CLIPBOARD_TYPE_SELECTION)); - EXPECT_EQ(ui::CLIPBOARD_TYPE_LAST, GetAndResetCopiedToClipboard()); + EXPECT_STR_EQ("abcd", GetClipboardText(ui::ClipboardType::kSelection)); + EXPECT_EQ(ui::ClipboardType::kMaxValue, GetAndResetCopiedToClipboard()); // Shift-Left/Right should not modify the selection clipboard for a password // textfield. SendKeyEvent(ui::VKEY_LEFT, true, false); EXPECT_EQ(gfx::Range(0, 3), textfield2->GetSelectedRange()); - EXPECT_STR_EQ("abcd", GetClipboardText(ui::CLIPBOARD_TYPE_SELECTION)); - EXPECT_EQ(ui::CLIPBOARD_TYPE_LAST, GetAndResetCopiedToClipboard()); + EXPECT_STR_EQ("abcd", GetClipboardText(ui::ClipboardType::kSelection)); + EXPECT_EQ(ui::ClipboardType::kMaxValue, GetAndResetCopiedToClipboard()); SendKeyEvent(ui::VKEY_RIGHT, true, false); EXPECT_EQ(gfx::Range(0, 4), textfield2->GetSelectedRange()); - EXPECT_STR_EQ("abcd", GetClipboardText(ui::CLIPBOARD_TYPE_SELECTION)); - EXPECT_EQ(ui::CLIPBOARD_TYPE_LAST, GetAndResetCopiedToClipboard()); + EXPECT_STR_EQ("abcd", GetClipboardText(ui::ClipboardType::kSelection)); + EXPECT_EQ(ui::ClipboardType::kMaxValue, GetAndResetCopiedToClipboard()); } #endif @@ -3753,4 +3752,28 @@ TEST_F(TextfieldTest, ChangeTextDirectionAndLayoutAlignmentTest) { gfx::HorizontalAlignment::ALIGN_LEFT); } +TEST_F(TextfieldTest, TextChangedCallbackTest) { + InitTextfield(); + + bool text_changed = false; + auto subscription = textfield_->AddTextChangedCallback(base::BindRepeating( + [](bool* text_changed) { *text_changed = true; }, &text_changed)); + + textfield_->SetText(ASCIIToUTF16("abc")); + EXPECT_TRUE(text_changed); + + text_changed = false; + textfield_->AppendText(ASCIIToUTF16("def")); + EXPECT_TRUE(text_changed); + + // Undo should still cause callback. + text_changed = false; + SendKeyEvent(ui::VKEY_Z, false, true); + EXPECT_TRUE(text_changed); + + text_changed = false; + SendKeyEvent(ui::VKEY_BACK); + EXPECT_TRUE(text_changed); +} + } // namespace views diff --git a/chromium/ui/views/controls/throbber.cc b/chromium/ui/views/controls/throbber.cc index 0987bec314f..44bd36d6f6b 100644 --- a/chromium/ui/views/controls/throbber.cc +++ b/chromium/ui/views/controls/throbber.cc @@ -52,12 +52,16 @@ void Throbber::Stop() { SchedulePaint(); } +bool Throbber::GetChecked() const { + return checked_; +} + void Throbber::SetChecked(bool checked) { if (checked == checked_) return; checked_ = checked; - SchedulePaint(); + OnPropertyChanged(&checked_, kPropertyEffectsPaint); } gfx::Size Throbber::CalculatePreferredSize() const { @@ -86,6 +90,11 @@ bool Throbber::IsRunning() const { return timer_.IsRunning(); } +BEGIN_METADATA(Throbber) +METADATA_PARENT_CLASS(View) +ADD_PROPERTY_METADATA(Throbber, bool, Checked) +END_METADATA() + // Smoothed throbber --------------------------------------------------------- // Delay after work starts before starting throbber, in milliseconds. @@ -124,8 +133,36 @@ void SmoothedThrobber::Stop() { &SmoothedThrobber::StopDelayOver); } +int SmoothedThrobber::GetStartDelayMs() const { + return start_delay_ms_; +} + +void SmoothedThrobber::SetStartDelayMs(int start_delay_ms) { + if (start_delay_ms == start_delay_ms_) + return; + start_delay_ms_ = start_delay_ms; + OnPropertyChanged(&start_delay_ms_, kPropertyEffectsNone); +} + +int SmoothedThrobber::GetStopDelayMs() const { + return stop_delay_ms_; +} + +void SmoothedThrobber::SetStopDelayMs(int stop_delay_ms) { + if (stop_delay_ms == stop_delay_ms_) + return; + stop_delay_ms_ = stop_delay_ms; + OnPropertyChanged(&stop_delay_ms_, kPropertyEffectsNone); +} + void SmoothedThrobber::StopDelayOver() { Throbber::Stop(); } +BEGIN_METADATA(SmoothedThrobber) +METADATA_PARENT_CLASS(Throbber) +ADD_PROPERTY_METADATA(SmoothedThrobber, int, StartDelayMs) +ADD_PROPERTY_METADATA(SmoothedThrobber, int, StopDelayMs) +END_METADATA() + } // namespace views diff --git a/chromium/ui/views/controls/throbber.h b/chromium/ui/views/controls/throbber.h index 8ba2b9ff876..2b19d996f40 100644 --- a/chromium/ui/views/controls/throbber.h +++ b/chromium/ui/views/controls/throbber.h @@ -17,6 +17,7 @@ namespace views { class VIEWS_EXPORT Throbber : public View { public: + METADATA_HEADER(Throbber); Throbber(); ~Throbber() override; @@ -24,7 +25,9 @@ class VIEWS_EXPORT Throbber : public View { virtual void Start(); virtual void Stop(); - // Stop spinning and, if checked is true, display a checkmark. + // Gets/Sets checked. For SetChecked, stop spinning and, if + // checked is true, display a checkmark. + bool GetChecked() const; void SetChecked(bool checked); // Overridden from View: @@ -51,14 +54,18 @@ class VIEWS_EXPORT Throbber : public View { // a small amount of work time has passed. class VIEWS_EXPORT SmoothedThrobber : public Throbber { public: + METADATA_HEADER(SmoothedThrobber); SmoothedThrobber(); ~SmoothedThrobber() override; void Start() override; void Stop() override; - void set_start_delay_ms(int value) { start_delay_ms_ = value; } - void set_stop_delay_ms(int value) { stop_delay_ms_ = value; } + int GetStartDelayMs() const; + void SetStartDelayMs(int start_delay_ms); + + int GetStopDelayMs() const; + void SetStopDelayMs(int stop_delay_ms); private: // Called when the startup-delay timer fires diff --git a/chromium/ui/views/controls/tree/tree_view.cc b/chromium/ui/views/controls/tree/tree_view.cc index 560877ca6e3..7cc025f8431 100644 --- a/chromium/ui/views/controls/tree/tree_view.cc +++ b/chromium/ui/views/controls/tree/tree_view.cc @@ -25,6 +25,7 @@ #include "ui/gfx/image/image.h" #include "ui/gfx/image/image_skia_operations.h" #include "ui/gfx/paint_vector_icon.h" +#include "ui/gfx/scoped_canvas.h" #include "ui/gfx/skia_util.h" #include "ui/native_theme/native_theme.h" #include "ui/resources/grit/ui_resources.h" @@ -35,7 +36,6 @@ #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/resources/grit/views_resources.h" #include "ui/views/style/platform_style.h" #include "ui/views/vector_icons.h" @@ -56,11 +56,16 @@ static constexpr int kTextHorizontalPadding = 2; // How much children are indented from their parent. static constexpr int kIndent = 20; -// static -const char TreeView::kViewClassName[] = "TreeView"; - namespace { +void PaintRowIcon(gfx::Canvas* canvas, + const gfx::ImageSkia& icon, + int x, + const gfx::Rect& rect) { + canvas->DrawImageInt(icon, rect.x() + x, + rect.y() + (rect.height() - icon.height()) / 2); +} + bool EventIsDoubleTapOrClick(const ui::LocatedEvent& event) { if (event.type() == ui::ET_GESTURE_TAP) return event.AsGestureEvent()->details().tap_count() == 2; @@ -84,14 +89,11 @@ TreeView::TreeView() } else { // TODO(ellyjones): if the pre-Harmony codepath goes away, merge // closed_icon_ and open_icon_. - closed_icon_ = - *ui::ResourceBundle::GetSharedInstance() - .GetImageNamed((base::i18n::IsRTL() ? IDR_FOLDER_CLOSED_RTL - : IDR_FOLDER_CLOSED)) - .ToImageSkia(); + closed_icon_ = *ui::ResourceBundle::GetSharedInstance() + .GetImageNamed(IDR_FOLDER_CLOSED) + .ToImageSkia(); open_icon_ = *ui::ResourceBundle::GetSharedInstance() - .GetImageNamed((base::i18n::IsRTL() ? IDR_FOLDER_OPEN_RTL - : IDR_FOLDER_OPEN)) + .GetImageNamed(IDR_FOLDER_OPEN) .ToImageSkia(); } text_offset_ = closed_icon_.width() + kImagePadding + kImagePadding + @@ -142,8 +144,8 @@ void TreeView::SetModel(TreeModel* model) { root_.set_is_expanded(true); if (root_shown_) selected_node_ = &root_; - else if (root_.child_count()) - selected_node_ = root_.GetChild(0); + else if (!root_.children().empty()) + selected_node_ = root_.children().front().get(); } DrawnNodesChanged(); } @@ -440,22 +442,18 @@ void TreeView::GetAccessibleNodeData(ui::AXNodeData* node_data) { node_data->SetName(selected_node_->model_node()->GetTitle()); } -const char* TreeView::GetClassName() const { - return kViewClassName; -} - void TreeView::TreeNodesAdded(TreeModel* model, TreeModelNode* parent, - int start, - int count) { + size_t start, + size_t count) { InternalNode* parent_node = GetInternalNodeForModelNode(parent, DONT_CREATE_IF_NOT_LOADED); if (!parent_node || !parent_node->loaded_children()) return; const auto& children = model_->GetChildren(parent); - for (int i = start; i < start + count; ++i) { + for (size_t i = start; i < start + count; ++i) { auto child = std::make_unique<InternalNode>(); - ConfigureInternalNode(children[size_t{i}], child.get()); + ConfigureInternalNode(children[i], child.get()); parent_node->Add(std::move(child), i); } if (IsExpanded(parent)) @@ -464,15 +462,15 @@ void TreeView::TreeNodesAdded(TreeModel* model, void TreeView::TreeNodesRemoved(TreeModel* model, TreeModelNode* parent, - int start, - int count) { + size_t start, + size_t count) { InternalNode* parent_node = GetInternalNodeForModelNode(parent, DONT_CREATE_IF_NOT_LOADED); if (!parent_node || !parent_node->loaded_children()) return; bool reset_selection = false; - for (int i = 0; i < count; ++i) { - InternalNode* child_removing = parent_node->GetChild(start); + for (size_t i = 0; i < count; ++i) { + InternalNode* child_removing = parent_node->children()[start].get(); if (selected_node_ && selected_node_->HasAncestor(child_removing)) reset_selection = true; parent_node->Remove(start); @@ -690,13 +688,13 @@ bool TreeView::OnClickOrTap(const ui::LocatedEvent& event) { } void TreeView::LoadChildren(InternalNode* node) { - DCHECK_EQ(0, node->child_count()); + DCHECK(node->children().empty()); DCHECK(!node->loaded_children()); node->set_loaded_children(true); for (auto* model_child : model_->GetChildren(node->model_node())) { std::unique_ptr<InternalNode> child = std::make_unique<InternalNode>(); ConfigureInternalNode(model_child, child.get()); - node->Add(std::move(child), node->child_count()); + node->Add(std::move(child)); } } @@ -776,8 +774,8 @@ void TreeView::PaintRows(gfx::Canvas* canvas, if (!node->is_expanded()) return; depth++; - for (int i = 0; i < node->child_count() && *row < max_row; ++i) - PaintRows(canvas, min_row, max_row, node->GetChild(i), depth, row); + for (size_t i = 0; i < node->children().size() && *row < max_row; ++i) + PaintRows(canvas, min_row, max_row, node->children()[i].get(), depth, row); } void TreeView::PaintRow(gfx::Canvas* canvas, @@ -867,22 +865,24 @@ void TreeView::PaintExpandControl(gfx::Canvas* canvas, void TreeView::PaintNodeIcon(gfx::Canvas* canvas, InternalNode* node, const gfx::Rect& bounds) { - gfx::ImageSkia icon; int icon_index = model_->GetIconIndex(node->model_node()); - if (icon_index != -1) - icon = icons_[icon_index]; - else if (node->is_expanded()) - icon = open_icon_; - else - icon = closed_icon_; - int icon_x = kArrowRegionSize + kImagePadding + - (open_icon_.width() - icon.width()) / 2; - if (base::i18n::IsRTL()) - icon_x = bounds.right() - icon_x - open_icon_.width(); - else - icon_x += bounds.x(); - canvas->DrawImageInt(icon, icon_x, - bounds.y() + (bounds.height() - icon.height()) / 2); + int icon_x = kArrowRegionSize + kImagePadding; + if (icon_index == -1) { + // Flip just the |bounds| region of |canvas|. + gfx::ScopedCanvas scoped_canvas(canvas); + canvas->Translate(gfx::Vector2d(bounds.x(), 0)); + scoped_canvas.FlipIfRTL(bounds.width()); + // Now paint the icon local to that flipped region. + PaintRowIcon(canvas, node->is_expanded() ? open_icon_ : closed_icon_, + icon_x, + gfx::Rect(0, bounds.y(), bounds.width(), bounds.height())); + } else { + const gfx::ImageSkia& icon = icons_[icon_index]; + icon_x += (open_icon_.width() - icon.width()) / 2; + if (base::i18n::IsRTL()) + icon_x = bounds.width() - icon_x - icon.width(); + PaintRowIcon(canvas, icon, icon_x, bounds); + } } TreeView::InternalNode* TreeView::GetInternalNodeForModelNode( @@ -899,8 +899,9 @@ TreeView::InternalNode* TreeView::GetInternalNodeForModelNode( return nullptr; LoadChildren(parent_internal_node); } - return parent_internal_node->GetChild( - model_->GetIndexOf(parent_internal_node->model_node(), model_node)); + size_t index = + model_->GetIndexOf(parent_internal_node->model_node(), model_node); + return parent_internal_node->children()[index].get(); } gfx::Rect TreeView::GetBoundsForNode(InternalNode* node) { @@ -967,11 +968,11 @@ int TreeView::GetRowForInternalNode(InternalNode* node, int* depth) { int row = -1; InternalNode* tmp_node = node; while (tmp_node->parent()) { - int index_in_parent = tmp_node->parent()->GetIndexOf(tmp_node); + size_t index_in_parent = tmp_node->parent()->GetIndexOf(tmp_node); (*depth)++; row++; // For node. - for (int i = 0; i < index_in_parent; ++i) - row += tmp_node->parent()->GetChild(i)->NumExpandedNodes(); + for (size_t i = 0; i < index_in_parent; ++i) + row += tmp_node->parent()->children()[i]->NumExpandedNodes(); tmp_node = tmp_node->parent(); } if (root_shown_) { @@ -1014,10 +1015,9 @@ TreeView::InternalNode* TreeView::GetNodeByRowImpl(InternalNode* node, (*current_row)++; if (node->is_expanded()) { current_depth++; - for (int i = 0; i < node->child_count(); ++i) { + for (const auto& child : node->children()) { InternalNode* result = GetNodeByRowImpl( - node->GetChild(i), target_row, current_depth, current_row, - node_depth); + child.get(), target_row, current_depth, current_row, node_depth); if (result) return result; } @@ -1031,7 +1031,7 @@ void TreeView::IncrementSelection(IncrementType type) { if (!GetSelectedNode()) { // If nothing is selected select the first or last node. - if (!root_.child_count()) + if (root_.children().empty()) return; if (type == INCREMENT_PREVIOUS) { int row_count = GetRowCount(); @@ -1042,7 +1042,7 @@ void TreeView::IncrementSelection(IncrementType type) { } else if (root_shown_) { SetSelectedNode(root_.model_node()); } else { - SetSelectedNode(root_.GetChild(0)->model_node()); + SetSelectedNode(root_.children().front()->model_node()); } return; } @@ -1069,8 +1069,8 @@ void TreeView::ExpandOrSelectChild() { if (selected_node_) { if (!selected_node_->is_expanded()) Expand(selected_node_->model_node()); - else if (selected_node_->child_count()) - SetSelectedNode(selected_node_->GetChild(0)->model_node()); + else if (!selected_node_->children().empty()) + SetSelectedNode(selected_node_->children().front()->model_node()); } } @@ -1146,8 +1146,8 @@ int TreeView::InternalNode::NumExpandedNodes() const { int result = 1; // For this. if (!is_expanded_) return result; - for (int i = 0; i < child_count(); ++i) - result += GetChild(i)->NumExpandedNodes(); + for (const auto& child : children()) + result += child->NumExpandedNodes(); return result; } @@ -1157,11 +1157,15 @@ int TreeView::InternalNode::GetMaxWidth(TreeView* tree, int indent, int depth) { int max_width = (has_icon ? text_width_ : kArrowRegionSize) + indent * depth; if (!is_expanded_) return max_width; - for (int i = 0; i < child_count(); ++i) { + for (const auto& child : children()) { max_width = - std::max(max_width, GetChild(i)->GetMaxWidth(tree, indent, depth + 1)); + std::max(max_width, child->GetMaxWidth(tree, indent, depth + 1)); } return max_width; } +BEGIN_METADATA(TreeView) +METADATA_PARENT_CLASS(View) +END_METADATA() + } // namespace views diff --git a/chromium/ui/views/controls/tree/tree_view.h b/chromium/ui/views/controls/tree/tree_view.h index 649a244dbfa..facb792fac1 100644 --- a/chromium/ui/views/controls/tree/tree_view.h +++ b/chromium/ui/views/controls/tree/tree_view.h @@ -43,8 +43,7 @@ class VIEWS_EXPORT TreeView : public View, public FocusChangeListener, public PrefixDelegate { public: - // The tree view's class name. - static const char kViewClassName[]; + METADATA_HEADER(TreeView); TreeView(); ~TreeView() override; @@ -139,17 +138,16 @@ class VIEWS_EXPORT TreeView : public View, void ShowContextMenu(const gfx::Point& p, ui::MenuSourceType source_type) override; void GetAccessibleNodeData(ui::AXNodeData* node_data) override; - const char* GetClassName() const override; // TreeModelObserver overrides: void TreeNodesAdded(ui::TreeModel* model, ui::TreeModelNode* parent, - int start, - int count) override; + size_t start, + size_t count) override; void TreeNodesRemoved(ui::TreeModel* model, ui::TreeModelNode* parent, - int start, - int count) override; + size_t start, + size_t count) override; void TreeNodeChanged(ui::TreeModel* model, ui::TreeModelNode* model_node) override; diff --git a/chromium/ui/views/controls/tree/tree_view_unittest.cc b/chromium/ui/views/controls/tree/tree_view_unittest.cc index d37796e9d68..37bffe1d376 100644 --- a/chromium/ui/views/controls/tree/tree_view_unittest.cc +++ b/chromium/ui/views/controls/tree/tree_view_unittest.cc @@ -4,6 +4,7 @@ #include "ui/views/controls/tree/tree_view.h" +#include <numeric> #include <string> #include "base/macros.h" @@ -47,9 +48,7 @@ class TreeViewTest : public ViewsTestBase { } protected: - TestNode* Add(TestNode* parent, - int index, - const std::string& title); + TestNode* Add(TestNode* parent, size_t index, const std::string& title); std::string TreeViewContentsAsString(); @@ -77,7 +76,7 @@ class TreeViewTest : public ViewsTestBase { }; TestNode* TreeViewTest::Add(TestNode* parent, - int index, + size_t index, const std::string& title) { std::unique_ptr<TestNode> new_node = std::make_unique<TestNode>(); new_node->SetTitle(ASCIIToUTF16(title)); @@ -125,10 +124,10 @@ TestNode* TreeViewTest::GetNodeByTitleImpl(TestNode* node, const base::string16& title) { if (node->GetTitle() == title) return node; - for (int i = 0; i < node->child_count(); ++i) { - TestNode* child = GetNodeByTitleImpl(node->GetChild(i), title); - if (child) - return child; + for (auto& child : node->children()) { + TestNode* matching_node = GetNodeByTitleImpl(child.get(), title); + if (matching_node) + return matching_node; } return nullptr; } @@ -136,14 +135,14 @@ TestNode* TreeViewTest::GetNodeByTitleImpl(TestNode* node, std::string TreeViewTest::InternalNodeAsString( TreeView::InternalNode* node) { std::string result = base::UTF16ToASCII(node->model_node()->GetTitle()); - if (node->is_expanded() && node->child_count()) { - result += " ["; - for (int i = 0; i < node->child_count(); ++i) { - if (i > 0) - result += " "; - result += InternalNodeAsString(node->GetChild(i)); - } - result += "]"; + if (node->is_expanded() && !node->children().empty()) { + result += std::accumulate( + node->children().cbegin() + 1, node->children().cend(), + " [" + InternalNodeAsString(node->children().front().get()), + [this](const std::string& str, const auto& child) { + return str + " " + InternalNodeAsString(child.get()); + }) + + "]"; } return result; } diff --git a/chromium/ui/views/controls/webview/unhandled_keyboard_event_handler_mac.mm b/chromium/ui/views/controls/webview/unhandled_keyboard_event_handler_mac.mm index 3aa97dc8fbd..e84db1650f0 100644 --- a/chromium/ui/views/controls/webview/unhandled_keyboard_event_handler_mac.mm +++ b/chromium/ui/views/controls/webview/unhandled_keyboard_event_handler_mac.mm @@ -4,7 +4,7 @@ #include "ui/views/controls/webview/unhandled_keyboard_event_handler.h" -#import "ui/views/cocoa/bridged_native_widget_host_impl.h" +#import "ui/views/cocoa/native_widget_mac_ns_window_host.h" namespace views { @@ -13,7 +13,7 @@ bool UnhandledKeyboardEventHandler::HandleNativeKeyboardEvent( gfx::NativeEvent event, FocusManager* focus_manager) { auto* host = - views::BridgedNativeWidgetHostImpl::GetFromNativeWindow([event window]); + views::NativeWidgetMacNSWindowHost::GetFromNativeWindow([event window]); if (host) return host->RedispatchKeyEvent(event); return false; diff --git a/chromium/ui/views/controls/webview/web_dialog_view.cc b/chromium/ui/views/controls/webview/web_dialog_view.cc index fc3d4875c89..dccc4e3d21f 100644 --- a/chromium/ui/views/controls/webview/web_dialog_view.cc +++ b/chromium/ui/views/controls/webview/web_dialog_view.cc @@ -124,6 +124,10 @@ bool WebDialogView::CanClose() { //////////////////////////////////////////////////////////////////////////////// // WebDialogView, views::WidgetDelegate implementation: +bool WebDialogView::OnCloseRequested(Widget::ClosedReason close_reason) { + return !delegate_ || delegate_->OnDialogCloseRequested(); +} + bool WebDialogView::CanResize() const { if (delegate_) return delegate_->CanResizeDialog(); diff --git a/chromium/ui/views/controls/webview/web_dialog_view.h b/chromium/ui/views/controls/webview/web_dialog_view.h index 3e397a508f3..f77676f6f69 100644 --- a/chromium/ui/views/controls/webview/web_dialog_view.h +++ b/chromium/ui/views/controls/webview/web_dialog_view.h @@ -64,6 +64,7 @@ class WEBVIEW_EXPORT WebDialogView : public views::ClientView, bool CanClose() override; // Overridden from views::WidgetDelegate: + bool OnCloseRequested(Widget::ClosedReason close_reason) override; bool CanResize() const override; ui::ModalType GetModalType() const override; base::string16 GetWindowTitle() const override; diff --git a/chromium/ui/views/controls/webview/webview.cc b/chromium/ui/views/controls/webview/webview.cc index 85a5587f4c5..805703d5f79 100644 --- a/chromium/ui/views/controls/webview/webview.cc +++ b/chromium/ui/views/controls/webview/webview.cc @@ -19,7 +19,6 @@ #include "ui/accessibility/ax_enums.mojom.h" #include "ui/accessibility/ax_node_data.h" #include "ui/events/event.h" -#include "ui/views/controls/native/native_view_host.h" #include "ui/views/focus/focus_manager.h" #include "ui/views/views_delegate.h" @@ -46,20 +45,11 @@ WebView::ScopedWebContentsCreatorForTesting:: *GetCreatorForTesting() = WebView::WebContentsCreator(); } -// static -const char WebView::kViewClassName[] = "WebView"; - //////////////////////////////////////////////////////////////////////////////// // WebView, public: WebView::WebView(content::BrowserContext* browser_context) - : holder_(new NativeViewHost()), - embed_fullscreen_widget_mode_enabled_(false), - is_embedding_fullscreen_widget_(false), - browser_context_(browser_context), - allow_accelerators_(false) { - AddChildView(holder_); // Takes ownership of |holder_|. -} + : browser_context_(browser_context) {} WebView::~WebView() { SetWebContents(nullptr); // Make sure all necessary tear-down takes place. @@ -79,7 +69,7 @@ void WebView::SetWebContents(content::WebContents* replacement) { if (replacement == web_contents()) return; SetCrashedOverlayView(nullptr); - DetachWebContents(); + DetachWebContentsNativeView(); WebContentsObserver::Observe(replacement); // web_contents() now returns |replacement| from here onwards. UpdateCrashedOverlayView(); @@ -87,11 +77,12 @@ void WebView::SetWebContents(content::WebContents* replacement) { wc_owner_.reset(); if (embed_fullscreen_widget_mode_enabled_) { is_embedding_fullscreen_widget_ = - web_contents() && web_contents()->GetFullscreenRenderWidgetHostView(); + fullscreen_native_view_for_testing_ || + (web_contents() && web_contents()->GetFullscreenRenderWidgetHostView()); } else { DCHECK(!is_embedding_fullscreen_widget_); } - AttachWebContents(); + AttachWebContentsNativeView(); NotifyAccessibilityWebContentsChanged(); MaybeEnableAutoResize(); @@ -148,10 +139,6 @@ void WebView::SetCrashedOverlayView(View* crashed_overlay_view) { //////////////////////////////////////////////////////////////////////////////// // WebView, View overrides: -const char* WebView::GetClassName() const { - return kViewClassName; -} - void WebView::OnBoundsChanged(const gfx::Rect& previous_bounds) { if (crashed_overlay_view_) crashed_overlay_view_->SetBoundsRect(gfx::Rect(size())); @@ -203,7 +190,7 @@ void WebView::OnBoundsChanged(const gfx::Rect& previous_bounds) { void WebView::ViewHierarchyChanged( const ViewHierarchyChangedDetails& details) { if (details.is_add) - AttachWebContents(); + AttachWebContentsNativeView(); } bool WebView::SkipDefaultKeyEventProcessing(const ui::KeyEvent& event) { @@ -268,7 +255,7 @@ gfx::NativeViewAccessible WebView::GetNativeViewAccessible() { //////////////////////////////////////////////////////////////////////////////// // WebView, content::WebContentsDelegate implementation: -bool WebView::EmbedsFullscreenWidget() const { +bool WebView::EmbedsFullscreenWidget() { DCHECK(wc_owner_.get()); return embed_fullscreen_widget_mode_enabled_; } @@ -348,16 +335,23 @@ void WebView::ResizeDueToAutoResize(content::WebContents* source, //////////////////////////////////////////////////////////////////////////////// // WebView, private: -void WebView::AttachWebContents() { - TRACE_EVENT0("views", "WebView::AttachWebContents"); +void WebView::AttachWebContentsNativeView() { + TRACE_EVENT0("views", "WebView::AttachWebContentsNativeView"); // Prevents attachment if the WebView isn't already in a Widget, or it's // already attached. if (!GetWidget() || !web_contents()) return; - const gfx::NativeView view_to_attach = is_embedding_fullscreen_widget_ ? - web_contents()->GetFullscreenRenderWidgetHostView()->GetNativeView() : - web_contents()->GetNativeView(); + gfx::NativeView view_to_attach; + if (is_embedding_fullscreen_widget_) { + view_to_attach = fullscreen_native_view_for_testing_ + ? fullscreen_native_view_for_testing_ + : web_contents() + ->GetFullscreenRenderWidgetHostView() + ->GetNativeView(); + } else { + view_to_attach = web_contents()->GetNativeView(); + } OnBoundsChanged(bounds()); if (holder_->native_view() == view_to_attach) return; @@ -381,24 +375,24 @@ void WebView::AttachWebContents() { OnWebContentsAttached(); } -void WebView::DetachWebContents() { - TRACE_EVENT0("views", "WebView::DetachWebContents"); - if (web_contents()) { +void WebView::DetachWebContentsNativeView() { + TRACE_EVENT0("views", "WebView::DetachWebContentsNativeView"); + if (web_contents()) holder_->Detach(); - } } void WebView::ReattachForFullscreenChange(bool enter_fullscreen) { DCHECK(embed_fullscreen_widget_mode_enabled_); const bool web_contents_has_separate_fs_widget = - web_contents() && web_contents()->GetFullscreenRenderWidgetHostView(); + fullscreen_native_view_for_testing_ || + (web_contents() && web_contents()->GetFullscreenRenderWidgetHostView()); if (is_embedding_fullscreen_widget_ || web_contents_has_separate_fs_widget) { // Shutting down or starting up the embedding of the separate fullscreen // widget. Need to detach and re-attach to a different native view. - DetachWebContents(); + DetachWebContentsNativeView(); is_embedding_fullscreen_widget_ = enter_fullscreen && web_contents_has_separate_fs_widget; - AttachWebContents(); + AttachWebContentsNativeView(); } else { // Entering or exiting "non-Flash" fullscreen mode, where the native view is // the same. So, do not change attachment. @@ -454,4 +448,8 @@ void WebView::MaybeEnableAutoResize() { render_widget_host_view->EnableAutoResize(min_size_, max_size_); } +BEGIN_METADATA(WebView) +METADATA_PARENT_CLASS(View) +END_METADATA() + } // namespace views diff --git a/chromium/ui/views/controls/webview/webview.h b/chromium/ui/views/controls/webview/webview.h index 2d358a9d028..9872ad3c2f4 100644 --- a/chromium/ui/views/controls/webview/webview.h +++ b/chromium/ui/views/controls/webview/webview.h @@ -14,13 +14,12 @@ #include "content/public/browser/web_contents_delegate.h" #include "content/public/browser/web_contents_observer.h" #include "ui/gfx/native_widget_types.h" +#include "ui/views/controls/native/native_view_host.h" #include "ui/views/controls/webview/webview_export.h" #include "ui/views/view.h" namespace views { -class NativeViewHost; - // Provides a view of a WebContents instance. WebView can be used standalone, // creating and displaying an internally-owned WebContents; or within a full // browser where the browser swaps its own WebContents instances in/out (e.g., @@ -39,7 +38,7 @@ class WEBVIEW_EXPORT WebView : public View, public content::WebContentsDelegate, public content::WebContentsObserver { public: - static const char kViewClassName[]; + METADATA_HEADER(WebView); explicit WebView(content::BrowserContext* browser_context); ~WebView() override; @@ -92,9 +91,6 @@ class WEBVIEW_EXPORT WebView : public View, allow_accelerators_ = allow_accelerators; } - // Overridden from View: - const char* GetClassName() const override; - // Overridden from content::WebContentsDelegate: void ResizeDueToAutoResize(content::WebContents* source, const gfx::Size& new_size) override; @@ -138,7 +134,7 @@ class WEBVIEW_EXPORT WebView : public View, gfx::NativeViewAccessible GetNativeViewAccessible() override; // Overridden from content::WebContentsDelegate: - bool EmbedsFullscreenWidget() const override; + bool EmbedsFullscreenWidget() override; // Overridden from content::WebContentsObserver: void RenderViewCreated(content::RenderViewHost* render_view_host) override; @@ -165,8 +161,8 @@ class WEBVIEW_EXPORT WebView : public View, private: friend class WebViewUnitTest; - void AttachWebContents(); - void DetachWebContents(); + void AttachWebContentsNativeView(); + void DetachWebContentsNativeView(); void ReattachForFullscreenChange(bool enter_fullscreen); void UpdateCrashedOverlayView(); void NotifyAccessibilityWebContentsChanged(); @@ -181,20 +177,21 @@ class WEBVIEW_EXPORT WebView : public View, std::unique_ptr<content::WebContents> CreateWebContents( content::BrowserContext* browser_context); - NativeViewHost* const holder_; + NativeViewHost* const holder_ = + AddChildView(std::make_unique<NativeViewHost>()); // Non-NULL if |web_contents()| was created and is owned by this WebView. std::unique_ptr<content::WebContents> wc_owner_; // When true, WebView auto-embeds fullscreen widgets as a child view. - bool embed_fullscreen_widget_mode_enabled_; + bool embed_fullscreen_widget_mode_enabled_ = false; // Set to true while WebView is embedding a fullscreen widget view as a child // view instead of the normal WebContentsView render view. Note: This will be // false in the case of non-Flash fullscreen. - bool is_embedding_fullscreen_widget_; + bool is_embedding_fullscreen_widget_ = false; // Set to true when |holder_| is letterboxed (scaled to be smaller than this // view, to preserve its aspect ratio). bool is_letterboxing_ = false; content::BrowserContext* browser_context_; - bool allow_accelerators_; + bool allow_accelerators_ = false; View* crashed_overlay_view_ = nullptr; // Minimum and maximum sizes to determine WebView bounds for auto-resizing. @@ -206,6 +203,13 @@ class WEBVIEW_EXPORT WebView : public View, // WebContents's main RenderFrameHost. ui::AXTreeID child_ax_tree_id_; + // Used as the fullscreen NativeView if + // |embed_fullscreen_widget_mode_enabled_| is enabled. This is only set in + // tests as injecting a different value for + // WebContents::GetFullscreenRenderWidgetHostView() is rather tricky in + // unit-tests. + gfx::NativeView fullscreen_native_view_for_testing_ = nullptr; + DISALLOW_COPY_AND_ASSIGN(WebView); }; diff --git a/chromium/ui/views/controls/webview/webview_unittest.cc b/chromium/ui/views/controls/webview/webview_unittest.cc index 8950e868889..e2fc8221a19 100644 --- a/chromium/ui/views/controls/webview/webview_unittest.cc +++ b/chromium/ui/views/controls/webview/webview_unittest.cc @@ -111,7 +111,7 @@ class WebViewTestWebContentsDelegate : public content::WebContentsDelegate { // content::WebContentsDelegate overrides. bool IsFullscreenForTabOrPending( - const content::WebContents* ignored) const override { + const content::WebContents* ignored) override { return is_fullscreened_; } @@ -190,6 +190,10 @@ class WebViewUnitTest : public views::test::WidgetTest { content::WebContents::CreateParams(browser_context_.get())); } + void SetFullscreenNativeView(WebView* web_view, gfx::NativeView native_view) { + web_view->fullscreen_native_view_for_testing_ = native_view; + } + private: std::unique_ptr<content::RenderViewHostTestEnabler> rvh_enabler_; std::unique_ptr<content::TestBrowserContext> browser_context_; @@ -561,4 +565,44 @@ TEST_F(WebViewUnitTest, CrashedOverlayViewOwnedbyClient) { delete crashed_overlay_view; } +#if defined(USE_AURA) +namespace { + +// TODO(sky): factor this for mac. +gfx::Rect GetNativeViewBounds(gfx::NativeView native_view) { + return native_view->bounds(); +} + +} // namespace + +TEST_F(WebViewUnitTest, LayoutFullscreenNativeView) { + web_view()->SetEmbedFullscreenWidgetMode(true); + // WebView lazily creates WebContents. Force creation. + web_view()->GetWebContents(); + // Layout is async, force a layout now to ensure bounds are set. + web_view()->Layout(); + const gfx::Rect initial_bounds = + GetNativeViewBounds(web_view()->GetWebContents()->GetNativeView()); + EXPECT_NE(gfx::Rect(), initial_bounds); + + // Create another WebContents for a separate gfx::NativeView. The WebContent's + // gfx::NativeView is used as the fullscreen widget for web_view(). + const std::unique_ptr<content::WebContents> fullscreen_web_contents( + CreateWebContents()); + EXPECT_NE(initial_bounds, + GetNativeViewBounds(fullscreen_web_contents->GetNativeView())); + SetFullscreenNativeView(web_view(), fullscreen_web_contents->GetNativeView()); + + // Trigger going fullscreen. Once fullscreen, the fullscreen gfx::NativeView + // should be immediately resized. + static_cast<content::WebContentsObserver*>(web_view()) + ->DidShowFullscreenWidget(); + EXPECT_EQ(initial_bounds, + GetNativeViewBounds(fullscreen_web_contents->GetNativeView())); + + static_cast<content::WebContentsObserver*>(web_view()) + ->DidDestroyFullscreenWidget(); +} +#endif + } // namespace views diff --git a/chromium/ui/views/corewm/tooltip_aura.cc b/chromium/ui/views/corewm/tooltip_aura.cc index 3bb4ca0c143..2279c53c3a3 100644 --- a/chromium/ui/views/corewm/tooltip_aura.cc +++ b/chromium/ui/views/corewm/tooltip_aura.cc @@ -13,6 +13,7 @@ #include "ui/display/display.h" #include "ui/display/screen.h" #include "ui/gfx/canvas.h" +#include "ui/gfx/color_utils.h" #include "ui/gfx/render_text.h" #include "ui/gfx/text_elider.h" #include "ui/gfx/text_utils.h" @@ -27,15 +28,20 @@ namespace { // Max visual tooltip width. If a tooltip is greater than this width, it will // be wrapped. -constexpr int kTooltipMaxWidthPixels = 400; +constexpr int kTooltipMaxWidthPixels = 800; // FIXME: get cursor offset from actual cursor size. constexpr int kCursorOffsetX = 10; constexpr int kCursorOffsetY = 15; +// Paddings +constexpr int kHorizontalPadding = 8; +constexpr int kVerticalPaddingTop = 4; +constexpr int kVerticalPaddingBottom = 5; + // TODO(varkha): Update if native widget can be transparent on Linux. bool CanUseTranslucentTooltipWidget() { -#if defined(OS_LINUX) && !defined(OS_CHROMEOS) +#if (defined(OS_LINUX) && !defined(OS_CHROMEOS)) || defined(OS_WIN) return false; #else return true; @@ -52,7 +58,7 @@ views::Widget* CreateTooltipWidget(aura::Window* tooltip_window, params.type = views::Widget::InitParams::TYPE_TOOLTIP; params.context = tooltip_window; DCHECK(params.context); - params.keep_on_top = true; + params.z_order = ui::ZOrderLevel::kFloatingUIElement; params.accept_events = false; params.bounds = bounds; if (CanUseTranslucentTooltipWidget()) @@ -71,9 +77,6 @@ namespace corewm { class TooltipAura::TooltipView : public views::View { public: TooltipView() : render_text_(gfx::RenderText::CreateHarfBuzzInstance()) { - constexpr int kHorizontalPadding = 8; - constexpr int kVerticalPaddingTop = 4; - constexpr int kVerticalPaddingBottom = 5; SetBorder(CreateEmptyBorder(kVerticalPaddingTop, kHorizontalPadding, kVerticalPaddingBottom, kHorizontalPadding)); @@ -122,13 +125,22 @@ class TooltipAura::TooltipView : public views::View { } void SetBackgroundColor(SkColor background_color) { - // Corner radius of tooltip background. - const float kTooltipCornerRadius = 2.f; - SetBackground(CanUseTranslucentTooltipWidget() - ? views::CreateBackgroundFromPainter( - views::Painter::CreateSolidRoundRectPainter( - background_color, kTooltipCornerRadius)) - : views::CreateSolidBackground(background_color)); + if (CanUseTranslucentTooltipWidget()) { + // Corner radius of tooltip background. + const float kTooltipCornerRadius = 2.f; + SetBackground(views::CreateBackgroundFromPainter( + views::Painter::CreateSolidRoundRectPainter(background_color, + kTooltipCornerRadius))); + } else { + SetBackground(views::CreateSolidBackground(background_color)); + + auto border_color = + color_utils::GetColorWithMaxContrast(background_color); + SetBorder(views::CreatePaddedBorder( + views::CreateSolidBorder(1, border_color), + gfx::Insets(kVerticalPaddingTop - 1, kHorizontalPadding - 1, + kVerticalPaddingBottom - 1, kHorizontalPadding - 1))); + } // Force the text color to be readable when |background_color| is not // opaque. @@ -220,10 +232,16 @@ void TooltipAura::SetText(aura::Window* window, } ui::NativeTheme* native_theme = widget_->GetNativeTheme(); - tooltip_view_->SetBackgroundColor(native_theme->GetSystemColor( - ui::NativeTheme::kColorId_TooltipBackground)); - tooltip_view_->SetForegroundColor(native_theme->GetSystemColor( - ui::NativeTheme::kColorId_TooltipText)); + auto background_color = + native_theme->GetSystemColor(ui::NativeTheme::kColorId_TooltipBackground); + if (!CanUseTranslucentTooltipWidget()) + background_color = SkColorSetA(background_color, 0xFF); + tooltip_view_->SetBackgroundColor(background_color); + auto foreground_color = + native_theme->GetSystemColor(ui::NativeTheme::kColorId_TooltipText); + if (!CanUseTranslucentTooltipWidget()) + foreground_color = SkColorSetA(foreground_color, 0xFF); + tooltip_view_->SetForegroundColor(foreground_color); } void TooltipAura::Show() { diff --git a/chromium/ui/views/corewm/tooltip_controller_unittest.cc b/chromium/ui/views/corewm/tooltip_controller_unittest.cc index 96851116416..600f6bde5d2 100644 --- a/chromium/ui/views/corewm/tooltip_controller_unittest.cc +++ b/chromium/ui/views/corewm/tooltip_controller_unittest.cc @@ -629,7 +629,7 @@ class TooltipControllerTest2 : public aura::test::AuraTestBase { std::unique_ptr<ui::test::EventGenerator> generator_; private: - // Needed to make sure the InputDeviceManager is cleaned up between test runs. + // Needed to make sure the DeviceDataManager is cleaned up between test runs. std::unique_ptr<base::ShadowingAtExitManager> at_exit_manager_; std::unique_ptr<TooltipController> controller_; diff --git a/chromium/ui/views/drag_utils.h b/chromium/ui/views/drag_utils.h index e6f778e4f31..1604d9ec0f5 100644 --- a/chromium/ui/views/drag_utils.h +++ b/chromium/ui/views/drag_utils.h @@ -5,7 +5,10 @@ #ifndef UI_VIEWS_DRAG_UTILS_H_ #define UI_VIEWS_DRAG_UTILS_H_ +#include <memory> + #include "ui/base/dragdrop/drag_drop_types.h" +#include "ui/base/dragdrop/os_exchange_data.h" #include "ui/gfx/native_widget_types.h" #include "ui/views/views_export.h" @@ -13,16 +16,12 @@ namespace gfx { class Point; } -namespace ui { -class OSExchangeData; -} - namespace views { class Widget; // Starts a drag operation. This blocks until the drag operation completes. VIEWS_EXPORT void RunShellDrag(gfx::NativeView view, - const ui::OSExchangeData& data, + std::unique_ptr<ui::OSExchangeData> data, const gfx::Point& location, int operation, ui::DragDropTypes::DragEventSource source); diff --git a/chromium/ui/views/drag_utils_aura.cc b/chromium/ui/views/drag_utils_aura.cc index 27577e0eb2c..3142d9c0f56 100644 --- a/chromium/ui/views/drag_utils_aura.cc +++ b/chromium/ui/views/drag_utils_aura.cc @@ -12,7 +12,7 @@ namespace views { void RunShellDrag(gfx::NativeView view, - const ui::OSExchangeData& data, + std::unique_ptr<ui::OSExchangeData> data, const gfx::Point& location, int operation, ui::DragDropTypes::DragEventSource source) { @@ -20,8 +20,9 @@ void RunShellDrag(gfx::NativeView view, wm::ConvertPointToScreen(view, &screen_location); aura::Window* root_window = view->GetRootWindow(); if (aura::client::GetDragDropClient(root_window)) { - aura::client::GetDragDropClient(root_window)->StartDragAndDrop( - data, root_window, view, screen_location, operation, source); + aura::client::GetDragDropClient(root_window) + ->StartDragAndDrop(std::move(data), root_window, view, screen_location, + operation, source); } } diff --git a/chromium/ui/views/drag_utils_mac.mm b/chromium/ui/views/drag_utils_mac.mm index 4d512b1f97c..59dc6eae3f3 100644 --- a/chromium/ui/views/drag_utils_mac.mm +++ b/chromium/ui/views/drag_utils_mac.mm @@ -7,7 +7,7 @@ namespace views { void RunShellDrag(gfx::NativeView view, - const ui::OSExchangeData& data, + std::unique_ptr<ui::OSExchangeData> data, const gfx::Point& location, int operation, ui::DragDropTypes::DragEventSource source) { diff --git a/chromium/ui/views/event_utils.cc b/chromium/ui/views/event_utils.cc deleted file mode 100644 index 83ca464f1b6..00000000000 --- a/chromium/ui/views/event_utils.cc +++ /dev/null @@ -1,21 +0,0 @@ -// Copyright (c) 2018 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "ui/views/event_utils.h" - -#include "base/time/time.h" -#include "ui/events/event.h" -#include "ui/views/metrics.h" - -namespace views { - -bool IsPossiblyUnintendedInteraction(const base::TimeTicks& initial_timestamp, - const ui::Event& event) { - return (event.IsMouseEvent() || event.IsTouchEvent()) && - event.time_stamp() < - initial_timestamp + - base::TimeDelta::FromMilliseconds(GetDoubleClickInterval()); -} - -} // namespace views diff --git a/chromium/ui/views/event_utils.h b/chromium/ui/views/event_utils.h deleted file mode 100644 index cfc553ff659..00000000000 --- a/chromium/ui/views/event_utils.h +++ /dev/null @@ -1,28 +0,0 @@ -// Copyright (c) 2018 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef UI_VIEWS_EVENT_UTILS_H_ -#define UI_VIEWS_EVENT_UTILS_H_ - -#include "ui/views/views_export.h" - -namespace base { -class TimeTicks; -} - -namespace ui { -class Event; -} - -namespace views { - -// Returns true if the event is a mouse, touch, or pointer event that took place -// within the double-click time interval after the |initial_timestamp|. -VIEWS_EXPORT bool IsPossiblyUnintendedInteraction( - const base::TimeTicks& initial_timestamp, - const ui::Event& event); - -} // namespace views - -#endif // UI_VIEWS_EVENT_UTILS_H_ diff --git a/chromium/ui/views/examples/BUILD.gn b/chromium/ui/views/examples/BUILD.gn index d184ac3f46a..90e1df27192 100644 --- a/chromium/ui/views/examples/BUILD.gn +++ b/chromium/ui/views/examples/BUILD.gn @@ -123,6 +123,7 @@ executable("views_examples_exe") { "//build/win:default_exe_manifest", "//components/viz/host", "//components/viz/service", + "//mojo/core/embedder", "//ui/base", "//ui/base/ime/init", "//ui/compositor", diff --git a/chromium/ui/views/examples/DEPS b/chromium/ui/views/examples/DEPS index 36a503acd46..7d637be55cc 100644 --- a/chromium/ui/views/examples/DEPS +++ b/chromium/ui/views/examples/DEPS @@ -3,6 +3,7 @@ include_rules = [ "+components/viz/service", # In-process viz service. "+content/public", "+content/shell", + "+mojo/core/embedder/embedder.h", # TestGpuServiceHolder needs Mojo. "+sandbox", "+ui/gl/gl_switches.h", # Disable Direct Composition Workaround. "+ui/gl/init/gl_factory.h", # To initialize GL bindings. diff --git a/chromium/ui/views/examples/animated_image_view_example.cc b/chromium/ui/views/examples/animated_image_view_example.cc index 98959097b70..dc26fdee841 100644 --- a/chromium/ui/views/examples/animated_image_view_example.cc +++ b/chromium/ui/views/examples/animated_image_view_example.cc @@ -47,8 +47,8 @@ class AnimationGallery : public View, CreateSolidSidedBorder(1, 1, 1, 1, SK_ColorBLACK)); image_view_container_ = AddChildView(std::move(image_view_container)); - BoxLayout* box = SetLayoutManager( - std::make_unique<BoxLayout>(BoxLayout::kVertical, gfx::Insets(10), 10)); + BoxLayout* box = SetLayoutManager(std::make_unique<BoxLayout>( + BoxLayout::Orientation::kVertical, gfx::Insets(10), 10)); box->SetFlexForView(image_view_container_, 1); auto file_chooser = std::make_unique<Textfield>(); @@ -57,7 +57,7 @@ class AnimationGallery : public View, auto file_container = std::make_unique<View>(); BoxLayout* file_box = file_container->SetLayoutManager(std::make_unique<BoxLayout>( - BoxLayout::kHorizontal, gfx::Insets(10), 10)); + 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"))); diff --git a/chromium/ui/views/examples/bubble_example.cc b/chromium/ui/views/examples/bubble_example.cc index a2d94d57a41..644d10592e7 100644 --- a/chromium/ui/views/examples/bubble_example.cc +++ b/chromium/ui/views/examples/bubble_example.cc @@ -59,8 +59,8 @@ class ExampleBubble : public BubbleDialogDelegateView { int GetDialogButtons() const override { return ui::DIALOG_BUTTON_NONE; } void Init() override { - SetLayoutManager( - std::make_unique<BoxLayout>(BoxLayout::kVertical, gfx::Insets(50))); + SetLayoutManager(std::make_unique<BoxLayout>( + BoxLayout::Orientation::kVertical, gfx::Insets(50))); AddChildView(new Label(GetArrowName(arrow()))); } @@ -75,10 +75,8 @@ BubbleExample::BubbleExample() : ExampleBase("Bubble") {} BubbleExample::~BubbleExample() = default; void BubbleExample::CreateExampleView(View* container) { - PrintStatus("Click with optional modifiers: [Ctrl] for set_arrow(NONE), " - "[Alt] for set_arrow(FLOAT), or [Shift] to reverse the arrow iteration."); - container->SetLayoutManager( - std::make_unique<BoxLayout>(BoxLayout::kHorizontal, gfx::Insets(), 10)); + 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")); @@ -123,6 +121,8 @@ void BubbleExample::ButtonPressed(Button* sender, const ui::Event& event) { BubbleDialogDelegateView::CreateBubble(bubble); bubble->GetWidget()->Show(); + PrintStatus("Click with optional modifiers: [Ctrl] for set_arrow(NONE), " + "[Alt] for set_arrow(FLOAT), or [Shift] to reverse the arrow iteration."); } } // namespace examples diff --git a/chromium/ui/views/examples/button_example.cc b/chromium/ui/views/examples/button_example.cc index 993e0efb2de..50197395469 100644 --- a/chromium/ui/views/examples/button_example.cc +++ b/chromium/ui/views/examples/button_example.cc @@ -37,8 +37,8 @@ ButtonExample::~ButtonExample() = default; void ButtonExample::CreateExampleView(View* container) { container->SetBackground(CreateSolidBackground(SK_ColorWHITE)); - auto layout = - std::make_unique<BoxLayout>(BoxLayout::kVertical, gfx::Insets(10), 10); + auto layout = std::make_unique<BoxLayout>(BoxLayout::Orientation::kVertical, + gfx::Insets(10), 10); layout->set_cross_axis_alignment(BoxLayout::CrossAxisAlignment::kCenter); container->SetLayoutManager(std::move(layout)); diff --git a/chromium/ui/views/examples/button_sticker_sheet.cc b/chromium/ui/views/examples/button_sticker_sheet.cc index 8e19bd6a74e..38681f7a529 100644 --- a/chromium/ui/views/examples/button_sticker_sheet.cc +++ b/chromium/ui/views/examples/button_sticker_sheet.cc @@ -4,6 +4,10 @@ #include "ui/views/examples/button_sticker_sheet.h" +#include <memory> +#include <utility> + +#include "base/memory/ptr_util.h" #include "base/strings/utf_string_conversions.h" #include "ui/views/controls/button/button.h" #include "ui/views/controls/button/md_text_button.h" @@ -31,7 +35,7 @@ GridLayout* MakeStretchyGridLayout(View* host, int ncols) { const int kColumnWidth = 96; GridLayout* layout = - host->SetLayoutManager(std::make_unique<views::GridLayout>(host)); + host->SetLayoutManager(std::make_unique<views::GridLayout>()); ColumnSet* columns = layout->AddColumnSet(kStretchyGridColumnSetId); for (int i = 0; i < ncols; ++i) { if (i != 0) @@ -50,24 +54,24 @@ std::unique_ptr<View> MakePlainLabel(const std::string& text) { // Add a row containing a label whose text is |label_text| and then all the // views in |views| to the supplied GridLayout, with padding between rows. template <typename T> -void AddLabelledRowToGridLayout(GridLayout* layout, - const std::string& label_text, - std::vector<std::unique_ptr<T>> views) { +void AddLabeledRowToGridLayout(GridLayout* layout, + const std::string& label_text, + std::vector<std::unique_ptr<T>> views) { const float kRowDoesNotResizeVertically = 0.0; const int kPaddingRowHeight = 8; layout->StartRow(kRowDoesNotResizeVertically, kStretchyGridColumnSetId); - layout->AddView(MakePlainLabel(label_text).get()); + layout->AddView(MakePlainLabel(label_text)); for (auto& view : views) - layout->AddView(view.release()); + layout->AddView(std::move(view)); // This gets added extraneously after the last row, but it doesn't hurt and // means there's no need to keep track of whether to add it or not. layout->AddPaddingRow(kRowDoesNotResizeVertically, kPaddingRowHeight); } // Constructs a pair of MdTextButtons in the specified |state| with the -// specified |listener|, and returns them in |primary| and |secondary|. The -// button in |primary| is a call-to-action button, and the button in -// |secondary| is a regular button. +// specified |listener|, and returns them in |*primary| and |*secondary|. The +// button in |*primary| is a call-to-action button, and the button in +// |*secondary| is a regular button. std::vector<std::unique_ptr<MdTextButton>> MakeButtonsInState( ButtonListener* listener, Button::ButtonState state) { @@ -98,18 +102,18 @@ void ButtonStickerSheet::CreateExampleView(View* container) { std::vector<std::unique_ptr<View>> plainLabel; plainLabel.push_back(MakePlainLabel("Primary")); plainLabel.push_back(MakePlainLabel("Secondary")); - AddLabelledRowToGridLayout(layout, std::string(), std::move(plainLabel)); - - AddLabelledRowToGridLayout(layout, "Default", - MakeButtonsInState(this, Button::STATE_NORMAL)); - AddLabelledRowToGridLayout(layout, "Normal", - MakeButtonsInState(this, Button::STATE_NORMAL)); - AddLabelledRowToGridLayout(layout, "Hovered", - MakeButtonsInState(this, Button::STATE_HOVERED)); - AddLabelledRowToGridLayout(layout, "Pressed", - MakeButtonsInState(this, Button::STATE_PRESSED)); - AddLabelledRowToGridLayout(layout, "Disabled", - MakeButtonsInState(this, Button::STATE_DISABLED)); + AddLabeledRowToGridLayout(layout, std::string(), std::move(plainLabel)); + + AddLabeledRowToGridLayout(layout, "Default", + MakeButtonsInState(this, Button::STATE_NORMAL)); + AddLabeledRowToGridLayout(layout, "Normal", + MakeButtonsInState(this, Button::STATE_NORMAL)); + AddLabeledRowToGridLayout(layout, "Hovered", + MakeButtonsInState(this, Button::STATE_HOVERED)); + AddLabeledRowToGridLayout(layout, "Pressed", + MakeButtonsInState(this, Button::STATE_PRESSED)); + AddLabeledRowToGridLayout(layout, "Disabled", + MakeButtonsInState(this, Button::STATE_DISABLED)); } void ButtonStickerSheet::ButtonPressed(Button* button, const ui::Event& event) { diff --git a/chromium/ui/views/examples/combobox_example.cc b/chromium/ui/views/examples/combobox_example.cc index d8acc7b71c4..32f086700eb 100644 --- a/chromium/ui/views/examples/combobox_example.cc +++ b/chromium/ui/views/examples/combobox_example.cc @@ -51,8 +51,8 @@ void ComboboxExample::CreateExampleView(View* container) { disabled_combobox->SetSelectedIndex(4); disabled_combobox->SetEnabled(false); - container->SetLayoutManager( - std::make_unique<BoxLayout>(BoxLayout::kVertical, gfx::Insets(10, 0), 5)); + container->SetLayoutManager(std::make_unique<BoxLayout>( + BoxLayout::Orientation::kVertical, gfx::Insets(10, 0), 5)); container->AddChildView(combobox_); container->AddChildView(disabled_combobox); } diff --git a/chromium/ui/views/examples/dialog_example.cc b/chromium/ui/views/examples/dialog_example.cc index 152a8a5e1a1..21e9ab75b0d 100644 --- a/chromium/ui/views/examples/dialog_example.cc +++ b/chromium/ui/views/examples/dialog_example.cc @@ -59,14 +59,12 @@ class DialogExample::Delegate : public virtual DialogType { return parent_->title_->text(); } - // TODO(crbug.com/961660): CreateExtraView should return std::unique_ptr<View> - // DialogDelegate: - View* CreateExtraView() override { + std::unique_ptr<View> CreateExtraView() override { if (!parent_->has_extra_button_->GetChecked()) return nullptr; auto view = MdTextButton::CreateSecondaryUiButton( nullptr, parent_->extra_button_label_->text()); - return view.release(); + return view; } bool Cancel() override { return parent_->AllowDialogClose(false); } @@ -135,8 +133,8 @@ void DialogExample::CreateExampleView(View* container) { views::LayoutProvider* provider = views::LayoutProvider::Get(); const int horizontal_spacing = provider->GetDistanceMetric(views::DISTANCE_RELATED_BUTTON_HORIZONTAL); - GridLayout* layout = container->SetLayoutManager( - std::make_unique<views::GridLayout>(container)); + GridLayout* layout = + 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); @@ -160,10 +158,9 @@ void DialogExample::CreateExampleView(View* container) { AddCheckbox(layout, &has_extra_button_); StartRowWithLabel(layout, "Modal Type"); - mode_ = new Combobox(&mode_model_); + mode_ = layout->AddView(std::make_unique<Combobox>(&mode_model_)); mode_->set_listener(this); mode_->SetSelectedIndex(ui::MODAL_TYPE_CHILD); - layout->AddView(mode_); StartRowWithLabel(layout, "Bubble"); AddCheckbox(layout, &bubble_); @@ -177,16 +174,8 @@ void DialogExample::CreateExampleView(View* container) { kFixed, kButtonsColumnId, kFixed, provider->GetDistanceMetric(views::DISTANCE_UNRELATED_CONTROL_VERTICAL)); - // TODO(crbug.com/943560): Avoid this release(). - show_ = - MdTextButton::CreateSecondaryUiButton(this, base::ASCIIToUTF16("Show")) - .release(); - layout->AddView(show_); - - // Grow the dialog a bit when this example is first selected, so it all fits. - gfx::Size dialog_size = container->GetWidget()->GetRestoredBounds().size(); - dialog_size.set_height(dialog_size.height() + 80); - container->GetWidget()->SetSize(dialog_size); + show_ = layout->AddView( + MdTextButton::CreateSecondaryUiButton(this, base::ASCIIToUTF16("Show"))); } void DialogExample::StartRowWithLabel(GridLayout* layout, const char* label) { @@ -195,7 +184,7 @@ void DialogExample::StartRowWithLabel(GridLayout* layout, const char* label) { kFixedVerticalResize, views::LayoutProvider::Get()->GetDistanceMetric( views::DISTANCE_RELATED_CONTROL_VERTICAL)); - layout->AddView(new Label(base::ASCIIToUTF16(label))); + layout->AddView(std::make_unique<Label>(base::ASCIIToUTF16(label))); } void DialogExample::StartTextfieldRow(GridLayout* layout, @@ -203,18 +192,16 @@ void DialogExample::StartTextfieldRow(GridLayout* layout, const char* label, const char* value) { StartRowWithLabel(layout, label); - Textfield* textfield = new Textfield(); - layout->AddView(textfield); + auto textfield = std::make_unique<Textfield>(); textfield->set_controller(this); textfield->SetText(base::ASCIIToUTF16(value)); - *member = textfield; + *member = layout->AddView(std::move(textfield)); } void DialogExample::AddCheckbox(GridLayout* layout, Checkbox** member) { - Checkbox* checkbox = new Checkbox(base::string16(), this); + auto checkbox = std::make_unique<Checkbox>(base::string16(), this); checkbox->SetChecked(true); - layout->AddView(checkbox); - *member = checkbox; + *member = layout->AddView(std::move(checkbox)); } ui::ModalType DialogExample::GetModalType() const { @@ -272,10 +259,10 @@ void DialogExample::ButtonPressed(Button* sender, const ui::Event& event) { // be created as MODAL_TYPE_WINDOW without specifying a parent. gfx::NativeView parent = nullptr; if (mode_->GetSelectedIndex() != kFakeModeless) - parent = container()->GetWidget()->GetNativeView(); + parent = example_view()->GetWidget()->GetNativeView(); DialogDelegate::CreateDialogWidget( - dialog, container()->GetWidget()->GetNativeWindow(), parent); + dialog, example_view()->GetWidget()->GetNativeWindow(), parent); } last_dialog_->GetWidget()->Show(); return; diff --git a/chromium/ui/views/examples/example_base.cc b/chromium/ui/views/examples/example_base.cc index bc296aee322..878ac70059a 100644 --- a/chromium/ui/views/examples/example_base.cc +++ b/chromium/ui/views/examples/example_base.cc @@ -17,43 +17,10 @@ namespace examples { // This function can only be called if there is a visible examples window. void LogStatus(const std::string& status); -namespace { - -// TODO(oshima): Check if this special container is still necessary. -class ContainerView : public View { - public: - explicit ContainerView(ExampleBase* base) - : example_view_created_(false), - example_base_(base) { - } - - private: - // View: - void ViewHierarchyChanged( - const ViewHierarchyChangedDetails& details) override { - View::ViewHierarchyChanged(details); - // We're not using child == this because a Widget may not be - // available when this is added to the hierarchy. - if (details.is_add && GetWidget() && !example_view_created_) { - example_view_created_ = true; - example_base_->CreateExampleView(this); - } - } - - // True if the example view has already been created, or false otherwise. - bool example_view_created_; - - ExampleBase* example_base_; - - DISALLOW_COPY_AND_ASSIGN(ContainerView); -}; - -} // namespace - ExampleBase::~ExampleBase() = default; ExampleBase::ExampleBase(const char* title) : example_title_(title) { - container_ = new ContainerView(this); + container_ = new View(); } // Prints a message in the status area, at the bottom of the window. diff --git a/chromium/ui/views/examples/example_base.h b/chromium/ui/views/examples/example_base.h index 7a6b33d5766..e582096a50c 100644 --- a/chromium/ui/views/examples/example_base.h +++ b/chromium/ui/views/examples/example_base.h @@ -28,8 +28,6 @@ class VIEWS_EXAMPLES_EXPORT ExampleBase { protected: explicit ExampleBase(const char* title); - View* container() { return container_; } - // Prints a message in the status area, at the bottom of the window. void PrintStatus(const char* format, ...); diff --git a/chromium/ui/views/examples/examples_main.cc b/chromium/ui/views/examples/examples_main.cc index 00a933899b9..65ba7c824e7 100644 --- a/chromium/ui/views/examples/examples_main.cc +++ b/chromium/ui/views/examples/examples_main.cc @@ -20,6 +20,7 @@ #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/material_design/material_design_controller.h" #include "ui/base/resource/resource_bundle.h" @@ -55,7 +56,7 @@ base::LazyInstance<base::TestDiscardableMemoryAllocator>::DestructorAtExit int main(int argc, char** argv) { #if defined(OS_WIN) - ui::ScopedOleInitializer ole_initializer_; + ui::ScopedOleInitializer ole_initializer; #endif base::CommandLine::Init(argc, argv); @@ -68,6 +69,8 @@ int main(int argc, char** argv) { base::AtExitManager at_exit; + mojo::core::Init(); + #if defined(USE_X11) // This demo uses InProcessContextFactory which uses X on a separate Gpu // thread. @@ -104,8 +107,8 @@ int main(int argc, char** argv) { base::DiscardableMemoryAllocator::SetInstance( g_discardable_memory_allocator.Pointer()); - base::PowerMonitor power_monitor( - base::WrapUnique(new base::PowerMonitorDeviceSource)); + base::PowerMonitor::Initialize( + std::make_unique<base::PowerMonitorDeviceSource>()); #if defined(OS_WIN) gfx::win::InitializeDirectWrite(); diff --git a/chromium/ui/views/examples/examples_window.cc b/chromium/ui/views/examples/examples_window.cc index d95d1431b13..2de2b7249ab 100644 --- a/chromium/ui/views/examples/examples_window.cc +++ b/chromium/ui/views/examples/examples_window.cc @@ -90,6 +90,9 @@ ExampleVector CreateExamples() { examples.push_back(std::make_unique<TreeViewExample>()); examples.push_back(std::make_unique<VectorExample>()); examples.push_back(std::make_unique<WidgetExample>()); + + for (auto& example : examples) + example->CreateExampleView(example->example_view()); return examples; } @@ -139,20 +142,19 @@ class ExamplesWindowContents : public WidgetDelegateView, public ComboboxListener { public: ExamplesWindowContents(base::OnceClosure on_close, ExampleVector examples) - : example_shown_(new View), - status_label_(new Label), - on_close_(std::move(on_close)) { + : on_close_(std::move(on_close)) { auto combobox_model = std::make_unique<ComboboxModelExampleList>(); combobox_model_ = combobox_model.get(); combobox_model_->SetExamples(std::move(examples)); - combobox_ = new Combobox(std::move(combobox_model)); + auto combobox = std::make_unique<Combobox>(std::move(combobox_model)); instance_ = this; - combobox_->set_listener(this); + combobox->set_listener(this); - SetBackground(CreateStandardPanelBackground()); + SetBackground(CreateThemedSolidBackground( + this, ui::NativeTheme::kColorId_DialogBackground)); GridLayout* layout = - SetLayoutManager(std::make_unique<views::GridLayout>(this)); + SetLayoutManager(std::make_unique<views::GridLayout>()); ColumnSet* column_set = layout->AddColumnSet(0); column_set->AddPaddingColumn(0, 5); column_set->AddColumn(GridLayout::FILL, GridLayout::FILL, 1, @@ -160,17 +162,18 @@ class ExamplesWindowContents : public WidgetDelegateView, column_set->AddPaddingColumn(0, 5); layout->AddPaddingRow(0, 5); layout->StartRow(0 /* no expand */, 0); - layout->AddView(combobox_); + combobox_ = layout->AddView(std::move(combobox)); if (combobox_model_->GetItemCount() > 0) { layout->StartRow(1, 0); - example_shown_->SetLayoutManager(std::make_unique<FillLayout>()); - example_shown_->AddChildView(combobox_model_->GetItemViewAt(0)); - layout->AddView(example_shown_); + auto example_shown = std::make_unique<View>(); + example_shown->SetLayoutManager(std::make_unique<FillLayout>()); + example_shown->AddChildView(combobox_model_->GetItemViewAt(0)); + example_shown_ = layout->AddView(std::move(example_shown)); } layout->StartRow(0 /* no expand */, 0); - layout->AddView(status_label_); + status_label_ = layout->AddView(std::make_unique<Label>()); layout->AddPaddingRow(0, 5); } @@ -197,24 +200,30 @@ class ExamplesWindowContents : public WidgetDelegateView, std::move(on_close_).Run(); } gfx::Size CalculatePreferredSize() const override { - return gfx::Size(800, 300); + gfx::Size size(800, 300); + for (int i = 0; i < combobox_model_->GetItemCount(); i++) { + size.set_height( + std::max(size.height(), + combobox_model_->GetItemViewAt(i)->GetHeightForWidth(800))); + } + return size; } // ComboboxListener: void OnPerformAction(Combobox* combobox) override { DCHECK_EQ(combobox, combobox_); - DCHECK(combobox->GetSelectedIndex()); + int index = combobox->GetSelectedIndex(); + DCHECK_LT(index, combobox_model_->GetItemCount()); example_shown_->RemoveAllChildViews(false); - example_shown_->AddChildView( - combobox_model_->GetItemViewAt(combobox->GetSelectedIndex())); + example_shown_->AddChildView(combobox_model_->GetItemViewAt(index)); example_shown_->RequestFocus(); SetStatus(std::string()); InvalidateLayout(); } static ExamplesWindowContents* instance_; - View* example_shown_; - Label* status_label_; + View* example_shown_ = nullptr; + Label* status_label_ = nullptr; base::OnceClosure on_close_; Combobox* combobox_ = nullptr; // Owned by |combobox_|. @@ -244,7 +253,8 @@ void ShowExamplesWindow(base::OnceClosure on_close, } void LogStatus(const std::string& string) { - ExamplesWindowContents::instance()->SetStatus(string); + if (ExamplesWindowContents::instance()) + ExamplesWindowContents::instance()->SetStatus(string); } } // namespace examples diff --git a/chromium/ui/views/examples/flex_layout_example.cc b/chromium/ui/views/examples/flex_layout_example.cc index 0983e30622e..7027dc172b5 100644 --- a/chromium/ui/views/examples/flex_layout_example.cc +++ b/chromium/ui/views/examples/flex_layout_example.cc @@ -23,6 +23,7 @@ #include "ui/views/examples/example_combobox_model.h" #include "ui/views/layout/fill_layout.h" #include "ui/views/view.h" +#include "ui/views/view_class_properties.h" namespace views { namespace examples { @@ -81,8 +82,8 @@ void FlexLayoutExample::ContentsChanged(Textfield* sender, const base::string16& new_contents) { layout_->SetInteriorMargin( LayoutExampleBase::TextfieldsToInsets(interior_margin_)); - layout_->SetDefaultChildMargins( - LayoutExampleBase::TextfieldsToInsets(default_child_margins_)); + layout_->SetDefault(views::kMarginsKey, LayoutExampleBase::TextfieldsToInsets( + default_child_margins_)); RefreshLayoutPanel(false); } @@ -97,9 +98,9 @@ void FlexLayoutExample::UpdateLayoutManager() { ChildPanel* panel = static_cast<ChildPanel*>(child); int flex = panel->GetFlex(); if (flex < 0) - layout_->ClearFlexForView(panel); + panel->ClearProperty(views::kFlexBehaviorKey); else - layout_->SetFlexForView(panel, GetFlexSpecification(flex)); + panel->SetProperty(views::kFlexBehaviorKey, GetFlexSpecification(flex)); } } diff --git a/chromium/ui/views/examples/label_example.cc b/chromium/ui/views/examples/label_example.cc index 82a9bd87301..c8d8ceb070a 100644 --- a/chromium/ui/views/examples/label_example.cc +++ b/chromium/ui/views/examples/label_example.cc @@ -62,60 +62,62 @@ LabelExample::~LabelExample() = default; void LabelExample::CreateExampleView(View* container) { // A very simple label example, followed by additional helpful examples. - container->SetLayoutManager( - std::make_unique<BoxLayout>(BoxLayout::kVertical, gfx::Insets(), 10)); - Label* label = new Label(ASCIIToUTF16("Hello world!")); - container->AddChildView(label); + container->SetLayoutManager(std::make_unique<BoxLayout>( + BoxLayout::Orientation::kVertical, gfx::Insets(), 10)); + container->AddChildView( + std::make_unique<Label>(ASCIIToUTF16("Hello world!"))); const wchar_t hello_world_hebrew[] = L"\x5e9\x5dc\x5d5\x5dd \x5d4\x5e2\x5d5\x5dc\x5dd!"; - label = new Label(WideToUTF16(hello_world_hebrew)); + auto label = std::make_unique<Label>(WideToUTF16(hello_world_hebrew)); label->SetHorizontalAlignment(gfx::ALIGN_RIGHT); - container->AddChildView(label); + container->AddChildView(std::move(label)); - label = new Label(WideToUTF16(L"A UTF16 surrogate pair: \x5d0\x5b0")); + label = std::make_unique<Label>( + WideToUTF16(L"A UTF16 surrogate pair: \x5d0\x5b0")); label->SetHorizontalAlignment(gfx::ALIGN_RIGHT); - container->AddChildView(label); + container->AddChildView(std::move(label)); - label = new Label(ASCIIToUTF16("A left-aligned blue label.")); + label = std::make_unique<Label>(ASCIIToUTF16("A left-aligned blue label.")); label->SetHorizontalAlignment(gfx::ALIGN_LEFT); label->SetEnabledColor(SK_ColorBLUE); - container->AddChildView(label); + container->AddChildView(std::move(label)); - label = new Label(WideToUTF16(L"Password!")); + label = std::make_unique<Label>(WideToUTF16(L"Password!")); label->SetObscured(true); - container->AddChildView(label); + container->AddChildView(std::move(label)); - label = new Label(ASCIIToUTF16("A Courier-18 label with shadows.")); + label = + std::make_unique<Label>(ASCIIToUTF16("A Courier-18 label with shadows.")); label->SetFontList(gfx::FontList("Courier, 18px")); gfx::ShadowValues shadows(1, gfx::ShadowValue(gfx::Vector2d(), 1, SK_ColorRED)); constexpr gfx::ShadowValue shadow(gfx::Vector2d(2, 2), 0, SK_ColorGRAY); shadows.push_back(shadow); label->SetShadows(shadows); - container->AddChildView(label); + container->AddChildView(std::move(label)); - label = new ExamplePreferredSizeLabel(); + label = std::make_unique<ExamplePreferredSizeLabel>(); label->SetText(ASCIIToUTF16("A long label will elide toward its logical end " "if the text's width exceeds the label's available width.")); - container->AddChildView(label); + container->AddChildView(std::move(label)); - label = new ExamplePreferredSizeLabel(); + label = std::make_unique<ExamplePreferredSizeLabel>(); label->SetText(ASCIIToUTF16("A multi-line label will wrap onto subsequent " "lines if the text's width exceeds the label's available width, which is " "helpful for extemely long text used to demonstrate line wrapping.")); label->SetMultiLine(true); - container->AddChildView(label); + container->AddChildView(std::move(label)); - label = new Label(ASCIIToUTF16("Label with thick border")); + label = std::make_unique<Label>(ASCIIToUTF16("Label with thick border")); label->SetBorder(CreateSolidBorder(20, SK_ColorRED)); - container->AddChildView(label); + container->AddChildView(std::move(label)); - label = new Label( + label = std::make_unique<Label>( ASCIIToUTF16("A multiline label...\n\n...which supports text selection")); label->SetSelectable(true); label->SetMultiLine(true); - container->AddChildView(label); + container->AddChildView(std::move(label)); AddCustomLabel(container); } @@ -154,11 +156,11 @@ void LabelExample::ContentsChanged(Textfield* sender, } void LabelExample::AddCustomLabel(View* container) { - View* control_container = new View(); + std::unique_ptr<View> control_container = std::make_unique<View>(); control_container->SetBorder(CreateSolidBorder(2, SK_ColorGRAY)); control_container->SetBackground(CreateSolidBackground(SK_ColorLTGRAY)); GridLayout* layout = control_container->SetLayoutManager( - std::make_unique<views::GridLayout>(control_container)); + std::make_unique<views::GridLayout>()); ColumnSet* column_set = layout->AddColumnSet(0); column_set->AddColumn(GridLayout::LEADING, GridLayout::FILL, @@ -167,13 +169,14 @@ void LabelExample::AddCustomLabel(View* container) { 1.0f, GridLayout::USE_PREF, 0, 0); layout->StartRow(0, 0); - layout->AddView(new Label(ASCIIToUTF16("Content: "))); - textfield_ = new Textfield(); - textfield_->SetText(ASCIIToUTF16("Use the provided controls to configure the " - "content and presentation of this custom label.")); - textfield_->SetEditableSelectionRange(gfx::Range()); - textfield_->set_controller(this); - layout->AddView(textfield_); + layout->AddView(std::make_unique<Label>(ASCIIToUTF16("Content: "))); + auto textfield = std::make_unique<Textfield>(); + textfield->SetText( + ASCIIToUTF16("Use the provided controls to configure the " + "content and presentation of this custom label.")); + textfield->SetEditableSelectionRange(gfx::Range()); + textfield->set_controller(this); + textfield_ = layout->AddView(std::move(textfield)); alignment_ = AddCombobox(layout, "Alignment: ", kAlignments, base::size(kAlignments)); @@ -189,29 +192,29 @@ void LabelExample::AddCustomLabel(View* container) { column_set->AddColumn(GridLayout::LEADING, GridLayout::LEADING, 0, GridLayout::USE_PREF, 0, 0); layout->StartRow(0, 1); - multiline_ = new Checkbox(base::ASCIIToUTF16("Multiline"), this); - layout->AddView(multiline_); - shadows_ = new Checkbox(base::ASCIIToUTF16("Shadows"), this); - layout->AddView(shadows_); - selectable_ = new Checkbox(base::ASCIIToUTF16("Selectable"), this); - layout->AddView(selectable_); + multiline_ = layout->AddView( + std::make_unique<Checkbox>(base::ASCIIToUTF16("Multiline"), this)); + shadows_ = layout->AddView( + std::make_unique<Checkbox>(base::ASCIIToUTF16("Shadows"), this)); + selectable_ = layout->AddView( + std::make_unique<Checkbox>(base::ASCIIToUTF16("Selectable"), this)); layout->AddPaddingRow(0, 8); column_set = layout->AddColumnSet(2); column_set->AddColumn(GridLayout::FILL, GridLayout::FILL, 1, GridLayout::USE_PREF, 0, 0); layout->StartRow(0, 2); - custom_label_ = new ExamplePreferredSizeLabel(); - custom_label_->SetHorizontalAlignment(gfx::ALIGN_LEFT); - custom_label_->SetElideBehavior(gfx::NO_ELIDE); - custom_label_->SetText(textfield_->text()); - layout->AddView(custom_label_); + auto custom_label = std::make_unique<ExamplePreferredSizeLabel>(); + custom_label->SetHorizontalAlignment(gfx::ALIGN_LEFT); + custom_label->SetElideBehavior(gfx::NO_ELIDE); + custom_label->SetText(textfield_->text()); + custom_label_ = layout->AddView(std::move(custom_label)); // Disable the text selection checkbox if |custom_label_| does not support // text selection. selectable_->SetEnabled(custom_label_->IsSelectionSupported()); - container->AddChildView(control_container); + container->AddChildView(std::move(control_container)); } Combobox* LabelExample::AddCombobox(GridLayout* layout, @@ -219,13 +222,12 @@ Combobox* LabelExample::AddCombobox(GridLayout* layout, const char** strings, int count) { layout->StartRow(0, 0); - layout->AddView(new Label(base::ASCIIToUTF16(name))); - Combobox* combobox = - new Combobox(std::make_unique<ExampleComboboxModel>(strings, count)); + layout->AddView(std::make_unique<Label>(base::ASCIIToUTF16(name))); + auto combobox = std::make_unique<Combobox>( + std::make_unique<ExampleComboboxModel>(strings, count)); combobox->SetSelectedIndex(0); combobox->set_listener(this); - layout->AddView(combobox); - return combobox; + return layout->AddView(std::move(combobox)); } } // namespace examples diff --git a/chromium/ui/views/examples/layout_example_base.cc b/chromium/ui/views/examples/layout_example_base.cc index c763feaa3c9..fe07c1ddd17 100644 --- a/chromium/ui/views/examples/layout_example_base.cc +++ b/chromium/ui/views/examples/layout_example_base.cc @@ -145,7 +145,7 @@ void LayoutExampleBase::ChildPanel::ContentsChanged( const base::string16& new_contents) { const gfx::Insets margins = LayoutExampleBase::TextfieldsToInsets(margin_); if (!margins.IsEmpty()) - this->SetProperty(kMarginsKey, new gfx::Insets(margins)); + this->SetProperty(kMarginsKey, margins); else this->ClearProperty(kMarginsKey); example_->RefreshLayoutPanel(sender == flex_); diff --git a/chromium/ui/views/examples/message_box_example.cc b/chromium/ui/views/examples/message_box_example.cc index d1837ff5d65..67ff6dc0c78 100644 --- a/chromium/ui/views/examples/message_box_example.cc +++ b/chromium/ui/views/examples/message_box_example.cc @@ -4,6 +4,9 @@ #include "ui/views/examples/message_box_example.h" +#include <memory> +#include <utility> + #include "base/strings/utf_string_conversions.h" #include "ui/views/controls/button/label_button.h" #include "ui/views/controls/message_box_view.h" @@ -21,22 +24,19 @@ MessageBoxExample::MessageBoxExample() : ExampleBase("Message Box View") { MessageBoxExample::~MessageBoxExample() = default; void MessageBoxExample::CreateExampleView(View* container) { - message_box_view_ = new MessageBoxView( - MessageBoxView::InitParams(ASCIIToUTF16("Hello, world!"))); - status_ = new LabelButton(this, ASCIIToUTF16("Show Status")); - toggle_ = new LabelButton(this, ASCIIToUTF16("Toggle Checkbox")); + GridLayout* layout = + container->SetLayoutManager(std::make_unique<views::GridLayout>()); - GridLayout* layout = container->SetLayoutManager( - std::make_unique<views::GridLayout>(container)); - - message_box_view_->SetCheckBoxLabel(ASCIIToUTF16("Check Box")); + auto message_box_view = std::make_unique<MessageBoxView>( + MessageBoxView::InitParams(ASCIIToUTF16("Hello, world!"))); + message_box_view->SetCheckBoxLabel(ASCIIToUTF16("Check Box")); 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); layout->StartRow(1 /* expand */, message_box_column); - layout->AddView(message_box_view_); + message_box_view_ = layout->AddView(std::move(message_box_view)); const int button_column = 1; column_set = layout->AddColumnSet(button_column); @@ -47,8 +47,10 @@ void MessageBoxExample::CreateExampleView(View* container) { layout->StartRow(0 /* no expand */, button_column); - layout->AddView(status_); - layout->AddView(toggle_); + status_ = layout->AddView( + std::make_unique<LabelButton>(this, ASCIIToUTF16("Show Status"))); + toggle_ = layout->AddView( + std::make_unique<LabelButton>(this, ASCIIToUTF16("Toggle Checkbox"))); } void MessageBoxExample::ButtonPressed(Button* sender, const ui::Event& event) { diff --git a/chromium/ui/views/examples/multiline_example.cc b/chromium/ui/views/examples/multiline_example.cc index 7533fb2e5f6..9a481821475 100644 --- a/chromium/ui/views/examples/multiline_example.cc +++ b/chromium/ui/views/examples/multiline_example.cc @@ -130,28 +130,30 @@ void MultilineExample::CreateExampleView(View* container) { L"\x627\x644\x631\x626\x64A\x633\x64A\x629" L"asdfgh"); - render_text_view_ = new RenderTextView(); - render_text_view_->SetText(kTestString); + auto render_text_view = std::make_unique<RenderTextView>(); + render_text_view->SetText(kTestString); - label_ = new PreferredSizeLabel(); - label_->SetText(kTestString); - label_->SetMultiLine(true); - label_->SetBorder(CreateSolidBorder(2, SK_ColorCYAN)); + auto label = std::make_unique<PreferredSizeLabel>(); + label->SetText(kTestString); + label->SetMultiLine(true); + label->SetBorder(CreateSolidBorder(2, SK_ColorCYAN)); - label_checkbox_ = new Checkbox(ASCIIToUTF16("views::Label:"), this); - label_checkbox_->SetChecked(true); - label_checkbox_->set_request_focus_on_press(false); + auto label_checkbox = + std::make_unique<Checkbox>(ASCIIToUTF16("views::Label:"), this); + label_checkbox->SetChecked(true); + label_checkbox->set_request_focus_on_press(false); - elision_checkbox_ = new Checkbox(ASCIIToUTF16("elide text?"), this); - elision_checkbox_->SetChecked(false); - elision_checkbox_->set_request_focus_on_press(false); + auto elision_checkbox = + std::make_unique<Checkbox>(ASCIIToUTF16("elide text?"), this); + elision_checkbox->SetChecked(false); + elision_checkbox->set_request_focus_on_press(false); - textfield_ = new Textfield(); - textfield_->set_controller(this); - textfield_->SetText(kTestString); + auto textfield = std::make_unique<Textfield>(); + textfield->set_controller(this); + textfield->SetText(kTestString); - GridLayout* layout = container->SetLayoutManager( - std::make_unique<views::GridLayout>(container)); + GridLayout* layout = + container->SetLayoutManager(std::make_unique<views::GridLayout>()); ColumnSet* column_set = layout->AddColumnSet(0); column_set->AddColumn(GridLayout::LEADING, GridLayout::CENTER, @@ -160,19 +162,19 @@ void MultilineExample::CreateExampleView(View* container) { 1.0f, GridLayout::FIXED, 0, 0); layout->StartRow(0, 0); - layout->AddView(new Label(ASCIIToUTF16("gfx::RenderText:"))); - layout->AddView(render_text_view_); + layout->AddView(std::make_unique<Label>(ASCIIToUTF16("gfx::RenderText:"))); + render_text_view_ = layout->AddView(std::move(render_text_view)); layout->StartRow(0, 0); - layout->AddView(label_checkbox_); - layout->AddView(label_); + label_checkbox_ = layout->AddView(std::move(label_checkbox)); + label_ = layout->AddView(std::move(label)); layout->StartRow(0, 0); - layout->AddView(elision_checkbox_); + elision_checkbox_ = layout->AddView(std::move(elision_checkbox)); layout->StartRow(0, 0); - layout->AddView(new Label(ASCIIToUTF16("Sample Text:"))); - layout->AddView(textfield_); + layout->AddView(std::make_unique<Label>(ASCIIToUTF16("Sample Text:"))); + textfield_ = layout->AddView(std::move(textfield)); } void MultilineExample::ContentsChanged(Textfield* sender, @@ -180,8 +182,8 @@ void MultilineExample::ContentsChanged(Textfield* sender, render_text_view_->SetText(new_contents); if (label_checkbox_->GetChecked()) label_->SetText(new_contents); - container()->InvalidateLayout(); - container()->SchedulePaint(); + example_view()->InvalidateLayout(); + example_view()->SchedulePaint(); } void MultilineExample::ButtonPressed(Button* sender, const ui::Event& event) { @@ -191,8 +193,8 @@ void MultilineExample::ButtonPressed(Button* sender, const ui::Event& event) { } else if (sender == elision_checkbox_) { render_text_view_->SetMaxLines(elision_checkbox_->GetChecked() ? 3 : 0); } - container()->InvalidateLayout(); - container()->SchedulePaint(); + example_view()->InvalidateLayout(); + example_view()->SchedulePaint(); } } // namespace examples diff --git a/chromium/ui/views/examples/native_theme_example.cc b/chromium/ui/views/examples/native_theme_example.cc index c5d1fbb191e..680988f6cc9 100644 --- a/chromium/ui/views/examples/native_theme_example.cc +++ b/chromium/ui/views/examples/native_theme_example.cc @@ -56,16 +56,15 @@ void InsertColorRow(GridLayout* layout, color_view->SetSelectable(true); layout->StartRow(GridLayout::kFixedSize, 0); - layout->AddView(label_view.release()); - layout->AddView(color_view.release()); + layout->AddView(std::move(label_view)); + layout->AddView(std::move(color_view)); } // Returns a view of two columns where the first contains the identifier names // of ui::NativeTheme::ColorId and the second contains the color. std::unique_ptr<View> CreateAllColorsView() { auto container = std::make_unique<View>(); - auto* layout = container->SetLayoutManager( - std::make_unique<GridLayout>(container.get())); + 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); @@ -157,12 +156,6 @@ std::unique_ptr<View> CreateAllColorsView() { InsertColorRow(layout, COLOR_LABEL_ARGS(kColorId_TableHeaderText)); InsertColorRow(layout, COLOR_LABEL_ARGS(kColorId_TableHeaderBackground)); InsertColorRow(layout, COLOR_LABEL_ARGS(kColorId_TableHeaderSeparator)); - InsertColorRow(layout, - COLOR_LABEL_ARGS(kColorId_ResultsTableNormalBackground)); - InsertColorRow(layout, - COLOR_LABEL_ARGS(kColorId_ResultsTableHoveredBackground)); - InsertColorRow(layout, COLOR_LABEL_ARGS(kColorId_ResultsTableNormalText)); - InsertColorRow(layout, COLOR_LABEL_ARGS(kColorId_ResultsTableDimmedText)); InsertColorRow(layout, COLOR_LABEL_ARGS(kColorId_ThrobberSpinningColor)); InsertColorRow(layout, COLOR_LABEL_ARGS(kColorId_ThrobberWaitingColor)); InsertColorRow(layout, COLOR_LABEL_ARGS(kColorId_ThrobberLightColor)); diff --git a/chromium/ui/views/examples/progress_bar_example.cc b/chromium/ui/views/examples/progress_bar_example.cc index 1f640e28c9a..4a1039335bb 100644 --- a/chromium/ui/views/examples/progress_bar_example.cc +++ b/chromium/ui/views/examples/progress_bar_example.cc @@ -30,8 +30,8 @@ ProgressBarExample::ProgressBarExample() : ExampleBase("Progress Bar") {} ProgressBarExample::~ProgressBarExample() = default; void ProgressBarExample::CreateExampleView(View* container) { - GridLayout* layout = container->SetLayoutManager( - std::make_unique<views::GridLayout>(container)); + GridLayout* layout = + container->SetLayoutManager(std::make_unique<views::GridLayout>()); ColumnSet* column_set = layout->AddColumnSet(0); column_set->AddColumn(GridLayout::TRAILING, GridLayout::CENTER, 0, @@ -44,24 +44,25 @@ void ProgressBarExample::CreateExampleView(View* container) { GridLayout::USE_PREF, 0, 0); layout->StartRow(0, 0); - minus_button_ = MdTextButton::Create(this, base::ASCIIToUTF16("-")).release(); - layout->AddView(minus_button_); - progress_bar_ = new ProgressBar(); - layout->AddView(progress_bar_); - plus_button_ = MdTextButton::Create(this, base::ASCIIToUTF16("+")).release(); - layout->AddView(plus_button_); + minus_button_ = + layout->AddView(MdTextButton::Create(this, base::ASCIIToUTF16("-"))); + progress_bar_ = layout->AddView(std::make_unique<ProgressBar>()); + plus_button_ = + layout->AddView(MdTextButton::Create(this, base::ASCIIToUTF16("+"))); + layout->StartRowWithPadding(0, 0, 0, 10); - layout->AddView(new Label(base::ASCIIToUTF16("Infinite loader:"))); - ProgressBar* infinite_bar = new ProgressBar(); + layout->AddView( + std::make_unique<Label>(base::ASCIIToUTF16("Infinite loader:"))); + auto infinite_bar = std::make_unique<ProgressBar>(); infinite_bar->SetValue(-1); - layout->AddView(infinite_bar); + layout->AddView(std::move(infinite_bar)); layout->StartRowWithPadding(0, 0, 0, 10); - layout->AddView( - new Label(base::ASCIIToUTF16("Infinite loader (very short):"))); - ProgressBar* shorter_bar = new ProgressBar(2); + layout->AddView(std::make_unique<Label>( + base::ASCIIToUTF16("Infinite loader (very short):"))); + auto shorter_bar = std::make_unique<ProgressBar>(2); shorter_bar->SetValue(-1); - layout->AddView(shorter_bar); + layout->AddView(std::move(shorter_bar)); } void ProgressBarExample::ButtonPressed(Button* sender, const ui::Event& event) { diff --git a/chromium/ui/views/examples/radio_button_example.cc b/chromium/ui/views/examples/radio_button_example.cc index d1e25bebf30..0bbcc618efa 100644 --- a/chromium/ui/views/examples/radio_button_example.cc +++ b/chromium/ui/views/examples/radio_button_example.cc @@ -30,26 +30,26 @@ RadioButtonExample::RadioButtonExample() : ExampleBase("Radio Button") {} RadioButtonExample::~RadioButtonExample() = default; void RadioButtonExample::CreateExampleView(View* container) { - GridLayout* layout = container->SetLayoutManager( - std::make_unique<views::GridLayout>(container)); + GridLayout* layout = + 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); - + const int group = 1; for (size_t i = 0; i < 3; ++i) { layout->StartRow(0, 0); - radio_buttons_.push_back(new RadioButton( - base::ASCIIToUTF16("Radio ") + base::NumberToString16(i), 1)); - layout->AddView(radio_buttons_.back()); + radio_buttons_.push_back(layout->AddView(std::make_unique<RadioButton>( + base::UTF8ToUTF16(base::StringPrintf("Radio %d in group %d", + static_cast<int>(i) + 1, group)), + group))); } layout->StartRow(0, 0); - select_ = new LabelButton(this, base::ASCIIToUTF16("Select")); - layout->AddView(select_); - + select_ = layout->AddView( + std::make_unique<LabelButton>(this, base::ASCIIToUTF16("Select"))); layout->StartRow(0, 0); - status_ = new LabelButton(this, base::ASCIIToUTF16("Show Status")); - layout->AddView(status_); + status_ = layout->AddView( + std::make_unique<LabelButton>(this, base::ASCIIToUTF16("Show Status"))); } 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 a5d6c18a680..543f34d7550 100644 --- a/chromium/ui/views/examples/scroll_view_example.cc +++ b/chromium/ui/views/examples/scroll_view_example.cc @@ -75,25 +75,20 @@ ScrollViewExample::ScrollViewExample() : ExampleBase("Scroll View") { ScrollViewExample::~ScrollViewExample() = default; void ScrollViewExample::CreateExampleView(View* container) { - wide_ = new LabelButton(this, ASCIIToUTF16("Wide")); - tall_ = new LabelButton(this, ASCIIToUTF16("Tall")); - big_square_ = new LabelButton(this, ASCIIToUTF16("Big Square")); - small_square_ = new LabelButton(this, ASCIIToUTF16("Small Square")); - scroll_to_ = new LabelButton(this, ASCIIToUTF16("Scroll to")); - scroll_view_ = new ScrollView(); - scrollable_ = scroll_view_->SetContents(std::make_unique<ScrollableView>()); + auto scroll_view = std::make_unique<ScrollView>(); + scrollable_ = scroll_view->SetContents(std::make_unique<ScrollableView>()); scrollable_->SetBounds(0, 0, 1000, 100); scrollable_->SetColor(SK_ColorYELLOW, SK_ColorCYAN); - GridLayout* layout = container->SetLayoutManager( - std::make_unique<views::GridLayout>(container)); + GridLayout* layout = + container->SetLayoutManager(std::make_unique<views::GridLayout>()); // Add scroll view. ColumnSet* column_set = layout->AddColumnSet(0); column_set->AddColumn(GridLayout::FILL, GridLayout::FILL, 1, GridLayout::USE_PREF, 0, 0); layout->StartRow(1, 0); - layout->AddView(scroll_view_); + scroll_view_ = layout->AddView(std::move(scroll_view)); // Add control buttons. column_set = layout->AddColumnSet(1); @@ -102,11 +97,16 @@ void ScrollViewExample::CreateExampleView(View* container) { GridLayout::USE_PREF, 0, 0); } layout->StartRow(0, 1); - layout->AddView(wide_); - layout->AddView(tall_); - layout->AddView(big_square_); - layout->AddView(small_square_); - layout->AddView(scroll_to_); + 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"))); } 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 edc6f843a01..a3e1ce7ec5c 100644 --- a/chromium/ui/views/examples/slider_example.cc +++ b/chromium/ui/views/examples/slider_example.cc @@ -25,8 +25,8 @@ void SliderExample::CreateExampleView(View* container) { slider_->SetValue(0.5); - container->SetLayoutManager( - std::make_unique<BoxLayout>(BoxLayout::kHorizontal, gfx::Insets(3), 3)); + container->SetLayoutManager(std::make_unique<BoxLayout>( + BoxLayout::Orientation::kHorizontal, gfx::Insets(3), 3)); container->AddChildView(slider_); container->AddChildView(label_); } diff --git a/chromium/ui/views/examples/tabbed_pane_example.cc b/chromium/ui/views/examples/tabbed_pane_example.cc index c4b0f6cc6f3..b534d663862 100644 --- a/chromium/ui/views/examples/tabbed_pane_example.cc +++ b/chromium/ui/views/examples/tabbed_pane_example.cc @@ -20,21 +20,22 @@ TabbedPaneExample::TabbedPaneExample() : ExampleBase("Tabbed Pane") { TabbedPaneExample::~TabbedPaneExample() = default; void TabbedPaneExample::CreateExampleView(View* container) { - tabbed_pane_ = new TabbedPane(); - tabbed_pane_->set_listener(this); - add_ = new LabelButton(this, ASCIIToUTF16("Add")); - add_at_ = new LabelButton(this, ASCIIToUTF16("Add At 1")); - select_at_ = new LabelButton(this, ASCIIToUTF16("Select At 1")); + 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")); - GridLayout* layout = container->SetLayoutManager( - std::make_unique<views::GridLayout>(container)); + GridLayout* layout = + container->SetLayoutManager(std::make_unique<views::GridLayout>()); 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); layout->StartRow(1 /* expand */, tabbed_pane_column); - layout->AddView(tabbed_pane_); + tabbed_pane_ = layout->AddView(std::move(tabbed_pane)); // Create a few tabs with a button first. AddButton("Tab 1"); @@ -50,9 +51,9 @@ void TabbedPaneExample::CreateExampleView(View* container) { } layout->StartRow(0 /* no expand */, button_column); - layout->AddView(add_); - layout->AddView(add_at_); - layout->AddView(select_at_); + add_ = layout->AddView(std::move(add)); + add_at_ = layout->AddView(std::move(add_at)); + select_at_ = layout->AddView(std::move(select_at)); } void TabbedPaneExample::ButtonPressed(Button* sender, const ui::Event& event) { @@ -60,7 +61,8 @@ void TabbedPaneExample::ButtonPressed(Button* sender, const ui::Event& event) { AddButton("Added"); } else if (sender == add_at_) { const base::string16 label = ASCIIToUTF16("Added at 1"); - tabbed_pane_->AddTabAtIndex(1, label, new LabelButton(nullptr, label)); + tabbed_pane_->AddTabAtIndex(1, label, + std::make_unique<LabelButton>(nullptr, label)); } else if (sender == select_at_) { if (tabbed_pane_->GetTabCount() > 1) tabbed_pane_->SelectTabAt(1); @@ -80,8 +82,8 @@ void TabbedPaneExample::PrintStatus() { } void TabbedPaneExample::AddButton(const std::string& label) { - LabelButton* button = new LabelButton(nullptr, ASCIIToUTF16(label)); - tabbed_pane_->AddTab(ASCIIToUTF16(label), button); + tabbed_pane_->AddTab(ASCIIToUTF16(label), std::make_unique<LabelButton>( + nullptr, ASCIIToUTF16(label))); } } // namespace examples diff --git a/chromium/ui/views/examples/table_example.cc b/chromium/ui/views/examples/table_example.cc index fc264a8c2ac..8b60316c723 100644 --- a/chromium/ui/views/examples/table_example.cc +++ b/chromium/ui/views/examples/table_example.cc @@ -41,21 +41,8 @@ TableExample::~TableExample() { } void TableExample::CreateExampleView(View* container) { - column1_visible_checkbox_ = - new Checkbox(ASCIIToUTF16("Fruit column visible"), this); - column1_visible_checkbox_->SetChecked(true); - column2_visible_checkbox_ = - new Checkbox(ASCIIToUTF16("Color column visible"), this); - column2_visible_checkbox_->SetChecked(true); - column3_visible_checkbox_ = - new Checkbox(ASCIIToUTF16("Origin column visible"), this); - column3_visible_checkbox_->SetChecked(true); - column4_visible_checkbox_ = - new Checkbox(ASCIIToUTF16("Price column visible"), this); - column4_visible_checkbox_->SetChecked(true); - - GridLayout* layout = container->SetLayoutManager( - std::make_unique<views::GridLayout>(container)); + GridLayout* layout = + container->SetLayoutManager(std::make_unique<views::GridLayout>()); std::vector<ui::TableColumn> columns; columns.push_back(TestTableColumn(0, "Fruit")); @@ -80,8 +67,7 @@ void TableExample::CreateExampleView(View* container) { GridLayout::USE_PREF, 0, 0); layout->StartRow(1 /* expand */, 0); table_ = table.get(); - layout->AddView( - TableView::CreateScrollViewWithTable(std::move(table)).release()); + layout->AddView(TableView::CreateScrollViewWithTable(std::move(table))); column_set = layout->AddColumnSet(1); column_set->AddColumn(GridLayout::FILL, GridLayout::FILL, @@ -95,10 +81,21 @@ void TableExample::CreateExampleView(View* container) { layout->StartRow(0 /* no expand */, 1); - layout->AddView(column1_visible_checkbox_); - layout->AddView(column2_visible_checkbox_); - layout->AddView(column3_visible_checkbox_); - layout->AddView(column4_visible_checkbox_); + auto make_checkbox = + [this](base::string16 text) -> std::unique_ptr<Checkbox> { + auto result = std::make_unique<Checkbox>(text, this); + result->SetChecked(true); + return result; + }; + + column1_visible_checkbox_ = + layout->AddView(make_checkbox(ASCIIToUTF16("Fruit column visible"))); + column2_visible_checkbox_ = + layout->AddView(make_checkbox(ASCIIToUTF16("Color column visible"))); + column3_visible_checkbox_ = + layout->AddView(make_checkbox(ASCIIToUTF16("Origin column visible"))); + column4_visible_checkbox_ = + layout->AddView(make_checkbox(ASCIIToUTF16("Price column visible"))); } int TableExample::RowCount() { diff --git a/chromium/ui/views/examples/text_example.cc b/chromium/ui/views/examples/text_example.cc index 48f71158b30..4b3ed1255e3 100644 --- a/chromium/ui/views/examples/text_example.cc +++ b/chromium/ui/views/examples/text_example.cc @@ -130,9 +130,8 @@ TextExample::TextExample() : ExampleBase("Text Styles") {} TextExample::~TextExample() = default; Checkbox* TextExample::AddCheckbox(GridLayout* layout, const char* name) { - Checkbox* checkbox = new Checkbox(base::ASCIIToUTF16(name), this); - layout->AddView(checkbox); - return checkbox; + return layout->AddView( + std::make_unique<Checkbox>(base::ASCIIToUTF16(name), this)); } Combobox* TextExample::AddCombobox(GridLayout* layout, @@ -140,20 +139,19 @@ Combobox* TextExample::AddCombobox(GridLayout* layout, const char* const* strings, int count) { layout->StartRow(0, 0); - layout->AddView(new Label(base::ASCIIToUTF16(name))); - Combobox* combobox = - new Combobox(std::make_unique<ExampleComboboxModel>(strings, count)); + layout->AddView(std::make_unique<Label>(base::ASCIIToUTF16(name))); + auto combobox = std::make_unique<Combobox>( + std::make_unique<ExampleComboboxModel>(strings, count)); combobox->SetSelectedIndex(0); combobox->set_listener(this); - layout->AddView(combobox, kNumColumns - 1, 1); - return combobox; + return layout->AddView(std::move(combobox), kNumColumns - 1, 1); } void TextExample::CreateExampleView(View* container) { - text_view_ = new TextExampleView; - text_view_->SetBorder(CreateSolidBorder(1, SK_ColorGRAY)); - GridLayout* layout = container->SetLayoutManager( - std::make_unique<views::GridLayout>(container)); + auto text_view = std::make_unique<TextExampleView>(); + text_view->SetBorder(CreateSolidBorder(1, SK_ColorGRAY)); + GridLayout* layout = + container->SetLayoutManager(std::make_unique<views::GridLayout>()); layout->AddPaddingRow(0, 8); ColumnSet* column_set = layout->AddColumnSet(0); @@ -190,7 +188,7 @@ void TextExample::CreateExampleView(View* container) { 1, GridLayout::USE_PREF, 0, 0); column_set->AddPaddingColumn(0, 16); layout->StartRow(1, 1); - layout->AddView(text_view_); + text_view_ = layout->AddView(std::move(text_view)); layout->AddPaddingRow(0, 8); } diff --git a/chromium/ui/views/examples/textfield_example.cc b/chromium/ui/views/examples/textfield_example.cc index b7a0290ecb5..6c217df53a1 100644 --- a/chromium/ui/views/examples/textfield_example.cc +++ b/chromium/ui/views/examples/textfield_example.cc @@ -23,37 +23,44 @@ using base::UTF16ToUTF8; namespace views { namespace examples { +namespace { + +template <class K, class T> +T* MakeRow(GridLayout* layout, + std::unique_ptr<K> view1, + std::unique_ptr<T> view2) { + layout->StartRowWithPadding(0, 0, 0, 5); + if (view1) + layout->AddView(std::move(view1)); + return layout->AddView(std::move(view2)); +} + +} // namespace + TextfieldExample::TextfieldExample() : ExampleBase("Textfield") {} TextfieldExample::~TextfieldExample() = default; void TextfieldExample::CreateExampleView(View* container) { - name_ = new Textfield(); - password_ = new Textfield(); - password_->SetTextInputType(ui::TEXT_INPUT_TYPE_PASSWORD); - password_->set_placeholder_text(ASCIIToUTF16("password")); - disabled_ = new Textfield(); - disabled_->SetEnabled(false); - disabled_->SetText(ASCIIToUTF16("disabled")); - read_only_ = new Textfield(); - read_only_->SetReadOnly(true); - read_only_->SetText(ASCIIToUTF16("read only")); - invalid_ = new Textfield(); - invalid_->SetInvalid(true); - rtl_ = new Textfield(); - rtl_->ChangeTextDirectionAndLayoutAlignment(base::i18n::RIGHT_TO_LEFT); - show_password_ = new LabelButton(this, ASCIIToUTF16("Show password")); - set_background_ = - new LabelButton(this, ASCIIToUTF16("Set non-default background")); - clear_all_ = new LabelButton(this, ASCIIToUTF16("Clear All")); - append_ = new LabelButton(this, ASCIIToUTF16("Append")); - set_ = new LabelButton(this, ASCIIToUTF16("Set")); - set_style_ = new LabelButton(this, ASCIIToUTF16("Set Styles")); - name_->set_controller(this); - password_->set_controller(this); - - GridLayout* layout = container->SetLayoutManager( - std::make_unique<views::GridLayout>(container)); + auto name = std::make_unique<Textfield>(); + name->set_controller(this); + auto password = std::make_unique<Textfield>(); + password->SetTextInputType(ui::TEXT_INPUT_TYPE_PASSWORD); + password->set_placeholder_text(ASCIIToUTF16("password")); + password->set_controller(this); + auto disabled = std::make_unique<Textfield>(); + disabled->SetEnabled(false); + disabled->SetText(ASCIIToUTF16("disabled")); + auto read_only = std::make_unique<Textfield>(); + read_only->SetReadOnly(true); + read_only->SetText(ASCIIToUTF16("read only")); + auto invalid = std::make_unique<Textfield>(); + invalid->SetInvalid(true); + auto rtl = std::make_unique<Textfield>(); + rtl->ChangeTextDirectionAndLayoutAlignment(base::i18n::RIGHT_TO_LEFT); + + GridLayout* layout = + container->SetLayoutManager(std::make_unique<views::GridLayout>()); ColumnSet* column_set = layout->AddColumnSet(0); column_set->AddColumn(GridLayout::LEADING, GridLayout::FILL, @@ -61,25 +68,42 @@ void TextfieldExample::CreateExampleView(View* container) { column_set->AddColumn(GridLayout::FILL, GridLayout::FILL, 0.8f, GridLayout::USE_PREF, 0, 0); - auto MakeRow = [layout](View* view1, View* view2) { - layout->StartRowWithPadding(0, 0, 0, 5); - layout->AddView(view1); - if (view2) - layout->AddView(view2); - }; - MakeRow(new Label(ASCIIToUTF16("Name:")), name_); - MakeRow(new Label(ASCIIToUTF16("Password:")), password_); - MakeRow(new Label(ASCIIToUTF16("Disabled:")), disabled_); - MakeRow(new Label(ASCIIToUTF16("Read Only:")), read_only_); - MakeRow(new Label(ASCIIToUTF16("Invalid:")), invalid_); - MakeRow(new Label(ASCIIToUTF16("RTL:")), rtl_); - MakeRow(new Label(ASCIIToUTF16("Name:")), nullptr); - MakeRow(show_password_, nullptr); - MakeRow(set_background_, nullptr); - MakeRow(clear_all_, nullptr); - MakeRow(append_, nullptr); - MakeRow(set_, nullptr); - MakeRow(set_style_, nullptr); + 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:"))); + show_password_ = MakeRow<View, LabelButton>( + layout, nullptr, + std::make_unique<LabelButton>(this, ASCIIToUTF16("Show password"))); + set_background_ = MakeRow<View, LabelButton>( + layout, nullptr, + std::make_unique<LabelButton>( + this, ASCIIToUTF16("Set non-default background"))); + clear_all_ = MakeRow<View, LabelButton>( + layout, nullptr, + std::make_unique<LabelButton>(this, ASCIIToUTF16("Clear All"))); + append_ = MakeRow<View, LabelButton>( + layout, nullptr, + std::make_unique<LabelButton>(this, ASCIIToUTF16("Append"))); + set_ = MakeRow<View, LabelButton>( + layout, nullptr, + std::make_unique<LabelButton>(this, ASCIIToUTF16("Set"))); + set_style_ = MakeRow<View, LabelButton>( + layout, nullptr, + std::make_unique<LabelButton>(this, ASCIIToUTF16("Set Styles"))); } void TextfieldExample::ContentsChanged(Textfield* sender, diff --git a/chromium/ui/views/examples/toggle_button_example.cc b/chromium/ui/views/examples/toggle_button_example.cc index 700edfdc3fc..fd476535188 100644 --- a/chromium/ui/views/examples/toggle_button_example.cc +++ b/chromium/ui/views/examples/toggle_button_example.cc @@ -19,7 +19,7 @@ ToggleButtonExample::~ToggleButtonExample() = default; void ToggleButtonExample::CreateExampleView(View* container) { button_ = new ToggleButton(this); - auto layout = std::make_unique<BoxLayout>(BoxLayout::kVertical); + 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_); diff --git a/chromium/ui/views/examples/tree_view_example.cc b/chromium/ui/views/examples/tree_view_example.cc index 38cce7cb589..7496472d9f3 100644 --- a/chromium/ui/views/examples/tree_view_example.cc +++ b/chromium/ui/views/examples/tree_view_example.cc @@ -73,18 +73,19 @@ void TreeViewExample::CreateExampleView(View* container) { tree_view->SetController(this); tree_view->SetDrawingProvider( std::make_unique<ExampleTreeViewDrawingProvider>()); - add_ = new LabelButton(this, ASCIIToUTF16("Add")); - add_->SetFocusForPlatform(); - add_->set_request_focus_on_press(true); - remove_ = new LabelButton(this, ASCIIToUTF16("Remove")); - remove_->SetFocusForPlatform(); - remove_->set_request_focus_on_press(true); - change_title_ = new LabelButton(this, ASCIIToUTF16("Change Title")); - change_title_->SetFocusForPlatform(); - change_title_->set_request_focus_on_press(true); - - GridLayout* layout = container->SetLayoutManager( - std::make_unique<views::GridLayout>(container)); + auto add = std::make_unique<LabelButton>(this, ASCIIToUTF16("Add")); + add->SetFocusForPlatform(); + add->set_request_focus_on_press(true); + auto remove = std::make_unique<LabelButton>(this, ASCIIToUTF16("Remove")); + remove->SetFocusForPlatform(); + remove->set_request_focus_on_press(true); + auto change_title = + std::make_unique<LabelButton>(this, ASCIIToUTF16("Change Title")); + change_title->SetFocusForPlatform(); + change_title->set_request_focus_on_press(true); + + GridLayout* layout = + container->SetLayoutManager(std::make_unique<views::GridLayout>()); const int tree_view_column = 0; ColumnSet* column_set = layout->AddColumnSet(tree_view_column); @@ -92,8 +93,7 @@ void TreeViewExample::CreateExampleView(View* container) { 1.0f, GridLayout::USE_PREF, 0, 0); layout->StartRow(1 /* expand */, tree_view_column); tree_view_ = tree_view.get(); - layout->AddView( - TreeView::CreateScrollViewWithTree(std::move(tree_view)).release()); + layout->AddView(TreeView::CreateScrollViewWithTree(std::move(tree_view))); // Add control buttons horizontally. const int button_column = 1; @@ -104,9 +104,9 @@ void TreeViewExample::CreateExampleView(View* container) { } layout->StartRow(0 /* no expand */, button_column); - layout->AddView(add_); - layout->AddView(remove_); - layout->AddView(change_title_); + add_ = layout->AddView(std::move(add)); + remove_ = layout->AddView(std::move(remove)); + change_title_ = layout->AddView(std::move(change_title)); } void TreeViewExample::AddNewNode() { @@ -115,8 +115,7 @@ void TreeViewExample::AddNewNode() { if (!selected_node) selected_node = model_.GetRoot(); NodeType* new_node = model_.Add( - selected_node, std::make_unique<NodeType>(selected_node->GetTitle(), 1), - selected_node->child_count()); + selected_node, std::make_unique<NodeType>(selected_node->GetTitle(), 1)); tree_view_->SetSelectedNode(new_node); } diff --git a/chromium/ui/views/examples/vector_example.cc b/chromium/ui/views/examples/vector_example.cc index bae6772d285..f631b382b1e 100644 --- a/chromium/ui/views/examples/vector_example.cc +++ b/chromium/ui/views/examples/vector_example.cc @@ -39,7 +39,8 @@ class VectorIconGallery : public View, auto image_view_container = std::make_unique<views::View>(); image_view_ = image_view_container->AddChildView(std::make_unique<ImageView>()); - auto image_layout = std::make_unique<BoxLayout>(BoxLayout::kHorizontal); + auto image_layout = + std::make_unique<BoxLayout>(BoxLayout::Orientation::kHorizontal); image_layout->set_cross_axis_alignment( BoxLayout::CrossAxisAlignment::kCenter); image_layout->set_main_axis_alignment( @@ -48,8 +49,8 @@ class VectorIconGallery : public View, image_view_->SetBorder(CreateSolidSidedBorder(1, 1, 1, 1, SK_ColorBLACK)); image_view_container_ = AddChildView(std::move(image_view_container)); - BoxLayout* box = SetLayoutManager( - std::make_unique<BoxLayout>(BoxLayout::kVertical, gfx::Insets(10), 10)); + BoxLayout* box = SetLayoutManager(std::make_unique<BoxLayout>( + BoxLayout::Orientation::kVertical, gfx::Insets(10), 10)); box->SetFlexForView(image_view_container_, 1); auto file_chooser = std::make_unique<Textfield>(); @@ -58,7 +59,7 @@ class VectorIconGallery : public View, auto file_container = std::make_unique<View>(); BoxLayout* file_box = file_container->SetLayoutManager(std::make_unique<BoxLayout>( - BoxLayout::kHorizontal, gfx::Insets(10), 10)); + 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"))); diff --git a/chromium/ui/views/examples/widget_example.cc b/chromium/ui/views/examples/widget_example.cc index 891c4e5e64d..0a36f58420d 100644 --- a/chromium/ui/views/examples/widget_example.cc +++ b/chromium/ui/views/examples/widget_example.cc @@ -28,8 +28,8 @@ class WidgetDialogExample : public DialogDelegateView { WidgetDialogExample(); ~WidgetDialogExample() override; base::string16 GetWindowTitle() const override; - View* CreateExtraView() override; - View* CreateFootnoteView() override; + std::unique_ptr<View> CreateExtraView() override; + std::unique_ptr<View> CreateFootnoteView() override; }; class ModalDialogExample : public WidgetDialogExample { @@ -45,8 +45,8 @@ class ModalDialogExample : public WidgetDialogExample { WidgetDialogExample::WidgetDialogExample() { SetBackground(CreateSolidBackground(SK_ColorGRAY)); - SetLayoutManager( - std::make_unique<BoxLayout>(BoxLayout::kVertical, gfx::Insets(10), 10)); + SetLayoutManager(std::make_unique<BoxLayout>( + BoxLayout::Orientation::kVertical, gfx::Insets(10), 10)); AddChildView(new Label(ASCIIToUTF16("Dialog contents label!"))); } @@ -56,15 +56,14 @@ base::string16 WidgetDialogExample::GetWindowTitle() const { return ASCIIToUTF16("Dialog Widget Example"); } -// TODO(crbug.com/961660): CreateExtraView should return std::unique_ptr<View> -View* WidgetDialogExample::CreateExtraView() { +std::unique_ptr<View> WidgetDialogExample::CreateExtraView() { auto view = MdTextButton::CreateSecondaryUiButton( nullptr, ASCIIToUTF16("Extra button!")); - return view.release(); + return view; } -View* WidgetDialogExample::CreateFootnoteView() { - return new Label(ASCIIToUTF16("Footnote label!")); +std::unique_ptr<View> WidgetDialogExample::CreateFootnoteView() { + return std::make_unique<Label>(ASCIIToUTF16("Footnote label!")); } } // namespace @@ -75,8 +74,8 @@ WidgetExample::WidgetExample() : ExampleBase("Widget") { WidgetExample::~WidgetExample() = default; void WidgetExample::CreateExampleView(View* container) { - container->SetLayoutManager( - std::make_unique<BoxLayout>(BoxLayout::kHorizontal, gfx::Insets(), 10)); + 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); @@ -109,7 +108,7 @@ void WidgetExample::ShowWidget(View* sender, Widget::InitParams params) { if (!widget->GetContentsView()) { View* contents = new View(); contents->SetLayoutManager( - std::make_unique<BoxLayout>(BoxLayout::kHorizontal)); + std::make_unique<BoxLayout>(BoxLayout::Orientation::kHorizontal)); contents->SetBackground(CreateSolidBackground(SK_ColorGRAY)); BuildButton(contents, "Close", CLOSE_WIDGET); widget->SetContentsView(contents); diff --git a/chromium/ui/views/focus/focus_traversal_unittest.cc b/chromium/ui/views/focus/focus_traversal_unittest.cc index 2b8285974af..3d2ef8e76ce 100644 --- a/chromium/ui/views/focus/focus_traversal_unittest.cc +++ b/chromium/ui/views/focus/focus_traversal_unittest.cc @@ -299,18 +299,17 @@ void FocusTraversalTest::InitContentView() { GetContentsView()->SetBackground(CreateSolidBackground(SK_ColorWHITE)); - Checkbox* cb = new Checkbox(ASCIIToUTF16("This is a checkbox")); - GetContentsView()->AddChildView(cb); + auto cb = std::make_unique<Checkbox>(ASCIIToUTF16("This is a checkbox")); + auto* cb_ptr = GetContentsView()->AddChildView(std::move(cb)); // In this fast paced world, who really has time for non hard-coded layout? - cb->SetBounds(10, 10, 200, 20); - cb->SetID(TOP_CHECKBOX_ID); - - left_container_ = new PaneView(); - left_container_->SetBorder(CreateSolidBorder(1, SK_ColorBLACK)); - left_container_->SetBackground( - CreateSolidBackground(SkColorSetRGB(240, 240, 240))); - left_container_->SetID(LEFT_CONTAINER_ID); - GetContentsView()->AddChildView(left_container_); + cb_ptr->SetBounds(10, 10, 200, 20); + cb_ptr->SetID(TOP_CHECKBOX_ID); + + auto container = std::make_unique<PaneView>(); + container->SetBorder(CreateSolidBorder(1, SK_ColorBLACK)); + container->SetBackground(CreateSolidBackground(SkColorSetRGB(240, 240, 240))); + container->SetID(LEFT_CONTAINER_ID); + left_container_ = GetContentsView()->AddChildView(std::move(container)); left_container_->SetBounds(10, 35, 250, 200); int label_x = 5; @@ -320,55 +319,55 @@ void FocusTraversalTest::InitContentView() { int y = 10; int gap_between_labels = 10; - Label* label = new Label(ASCIIToUTF16("Apple:")); + auto label = std::make_unique<Label>(ASCIIToUTF16("Apple:")); label->SetID(APPLE_LABEL_ID); - left_container_->AddChildView(label); - label->SetBounds(label_x, y, label_width, label_height); + auto* label_ptr = left_container_->AddChildView(std::move(label)); + label_ptr->SetBounds(label_x, y, label_width, label_height); - Textfield* text_field = new Textfield(); + auto text_field = std::make_unique<Textfield>(); text_field->SetID(APPLE_TEXTFIELD_ID); - left_container_->AddChildView(text_field); - text_field->SetBounds(label_x + label_width + 5, y, - text_field_width, label_height); + auto* text_field_ptr = left_container_->AddChildView(std::move(text_field)); + text_field_ptr->SetBounds(label_x + label_width + 5, y, text_field_width, + label_height); y += label_height + gap_between_labels; - label = new Label(ASCIIToUTF16("Orange:")); + label = std::make_unique<Label>(ASCIIToUTF16("Orange:")); label->SetID(ORANGE_LABEL_ID); - left_container_->AddChildView(label); - label->SetBounds(label_x, y, label_width, label_height); + label_ptr = left_container_->AddChildView(std::move(label)); + label_ptr->SetBounds(label_x, y, label_width, label_height); - text_field = new Textfield(); + text_field = std::make_unique<Textfield>(); text_field->SetID(ORANGE_TEXTFIELD_ID); - left_container_->AddChildView(text_field); - text_field->SetBounds(label_x + label_width + 5, y, - text_field_width, label_height); + text_field_ptr = left_container_->AddChildView(std::move(text_field)); + text_field_ptr->SetBounds(label_x + label_width + 5, y, text_field_width, + label_height); y += label_height + gap_between_labels; - label = new Label(ASCIIToUTF16("Banana:")); + label = std::make_unique<Label>(ASCIIToUTF16("Banana:")); label->SetID(BANANA_LABEL_ID); - left_container_->AddChildView(label); - label->SetBounds(label_x, y, label_width, label_height); + label_ptr = left_container_->AddChildView(std::move(label)); + label_ptr->SetBounds(label_x, y, label_width, label_height); - text_field = new Textfield(); + text_field = std::make_unique<Textfield>(); text_field->SetID(BANANA_TEXTFIELD_ID); - left_container_->AddChildView(text_field); - text_field->SetBounds(label_x + label_width + 5, y, - text_field_width, label_height); + text_field_ptr = left_container_->AddChildView(std::move(text_field)); + text_field_ptr->SetBounds(label_x + label_width + 5, y, text_field_width, + label_height); y += label_height + gap_between_labels; - label = new Label(ASCIIToUTF16("Kiwi:")); + label = std::make_unique<Label>(ASCIIToUTF16("Kiwi:")); label->SetID(KIWI_LABEL_ID); - left_container_->AddChildView(label); - label->SetBounds(label_x, y, label_width, label_height); + label_ptr = left_container_->AddChildView(std::move(label)); + label_ptr->SetBounds(label_x, y, label_width, label_height); - text_field = new Textfield(); + text_field = std::make_unique<Textfield>(); text_field->SetID(KIWI_TEXTFIELD_ID); - left_container_->AddChildView(text_field); - text_field->SetBounds(label_x + label_width + 5, y, - text_field_width, label_height); + text_field_ptr = left_container_->AddChildView(std::move(text_field)); + text_field_ptr->SetBounds(label_x + label_width + 5, y, text_field_width, + label_height); y += label_height + gap_between_labels; @@ -378,67 +377,70 @@ void FocusTraversalTest::InitContentView() { left_container_->AddChildView(std::move(button)); y += 40; - cb = new Checkbox(ASCIIToUTF16("This is another check box")); + cb = std::make_unique<Checkbox>(ASCIIToUTF16("This is another check box")); cb->SetBounds(label_x + label_width + 5, y, 180, 20); cb->SetID(FRUIT_CHECKBOX_ID); - left_container_->AddChildView(cb); + left_container_->AddChildView(std::move(cb)); y += 20; - Combobox* combobox = new Combobox(&combobox_model_); + auto combobox = std::make_unique<Combobox>(&combobox_model_); combobox->SetBounds(label_x + label_width + 5, y, 150, 30); combobox->SetID(COMBOBOX_ID); - left_container_->AddChildView(combobox); - - right_container_ = new PaneView(); - right_container_->SetBorder(CreateSolidBorder(1, SK_ColorBLACK)); - right_container_->SetBackground( - CreateSolidBackground(SkColorSetRGB(240, 240, 240))); - right_container_->SetID(RIGHT_CONTAINER_ID); - GetContentsView()->AddChildView(right_container_); + left_container_->AddChildView(std::move(combobox)); + + container = std::make_unique<PaneView>(); + container->SetBorder(CreateSolidBorder(1, SK_ColorBLACK)); + container->SetBackground(CreateSolidBackground(SkColorSetRGB(240, 240, 240))); + container->SetID(RIGHT_CONTAINER_ID); + right_container_ = GetContentsView()->AddChildView(std::move(container)); right_container_->SetBounds(270, 35, 300, 200); y = 10; int radio_button_height = 18; int gap_between_radio_buttons = 10; - RadioButton* radio_button = new RadioButton(ASCIIToUTF16("Asparagus"), 1); + auto radio_button = + std::make_unique<RadioButton>(ASCIIToUTF16("Asparagus"), 1); radio_button->SetID(ASPARAGUS_BUTTON_ID); - right_container_->AddChildView(radio_button); - radio_button->SetBounds(5, y, 70, radio_button_height); - radio_button->SetGroup(1); + auto* radio_button_ptr = + right_container_->AddChildView(std::move(radio_button)); + radio_button_ptr->SetBounds(5, y, 70, radio_button_height); + radio_button_ptr->SetGroup(1); y += radio_button_height + gap_between_radio_buttons; - radio_button = new RadioButton(ASCIIToUTF16("Broccoli"), 1); + radio_button = std::make_unique<RadioButton>(ASCIIToUTF16("Broccoli"), 1); radio_button->SetID(BROCCOLI_BUTTON_ID); - right_container_->AddChildView(radio_button); - radio_button->SetBounds(5, y, 70, radio_button_height); - radio_button->SetGroup(1); - RadioButton* radio_button_to_check = radio_button; + radio_button_ptr = right_container_->AddChildView(std::move(radio_button)); + radio_button_ptr->SetBounds(5, y, 70, radio_button_height); + radio_button_ptr->SetGroup(1); + RadioButton* radio_button_to_check = radio_button_ptr; y += radio_button_height + gap_between_radio_buttons; - radio_button = new RadioButton(ASCIIToUTF16("Cauliflower"), 1); + radio_button = std::make_unique<RadioButton>(ASCIIToUTF16("Cauliflower"), 1); radio_button->SetID(CAULIFLOWER_BUTTON_ID); - right_container_->AddChildView(radio_button); - radio_button->SetBounds(5, y, 70, radio_button_height); - radio_button->SetGroup(1); + radio_button_ptr = right_container_->AddChildView(std::move(radio_button)); + radio_button_ptr->SetBounds(5, y, 70, radio_button_height); + radio_button_ptr->SetGroup(1); y += radio_button_height + gap_between_radio_buttons; - View* inner_container = new View(); + auto inner_container = std::make_unique<View>(); inner_container->SetBorder(CreateSolidBorder(1, SK_ColorBLACK)); inner_container->SetBackground( CreateSolidBackground(SkColorSetRGB(230, 230, 230))); inner_container->SetID(INNER_CONTAINER_ID); - right_container_->AddChildView(inner_container); - inner_container->SetBounds(100, 10, 150, 180); + auto* inner_container_ptr = + right_container_->AddChildView(std::move(inner_container)); + inner_container_ptr->SetBounds(100, 10, 150, 180); - ScrollView* scroll_view = new ScrollView(); + auto scroll_view = std::make_unique<ScrollView>(); scroll_view->SetID(SCROLL_VIEW_ID); - inner_container->AddChildView(scroll_view); - scroll_view->SetBounds(1, 1, 148, 178); + auto* scroll_view_ptr = + inner_container_ptr->AddChildView(std::move(scroll_view)); + scroll_view_ptr->SetBounds(1, 1, 148, 178); auto scroll_content = std::make_unique<View>(); scroll_content->SetBounds(0, 0, 200, 200); scroll_content->SetBackground( CreateSolidBackground(SkColorSetRGB(200, 200, 200))); auto* scroll_content_ptr = - scroll_view->SetContents(std::move(scroll_content)); + scroll_view_ptr->SetContents(std::move(scroll_content)); static const char* const kTitles[] = { "Rosetta", "Stupeur et tremblement", "The diner game", @@ -458,11 +460,11 @@ void FocusTraversalTest::InitContentView() { y = 5; for (size_t i = 0; i < base::size(kTitles); ++i) { - Link* link = new Link(ASCIIToUTF16(kTitles[i])); + auto link = std::make_unique<Link>(ASCIIToUTF16(kTitles[i])); link->SetHorizontalAlignment(gfx::ALIGN_LEFT); link->SetID(kIDs[i]); - scroll_content_ptr->AddChildView(link); - link->SetBounds(5, y, 300, 15); + auto* link_ptr = scroll_content_ptr->AddChildView(std::move(link)); + link_ptr->SetBounds(5, y, 300, 15); y += 15; } @@ -486,72 +488,70 @@ void FocusTraversalTest::InitContentView() { y += 40; - View* contents = nullptr; - Link* link = nullptr; - // Left bottom box with style checkboxes. - contents = new View(); + auto contents = std::make_unique<View>(); contents->SetBackground(CreateSolidBackground(SK_ColorWHITE)); - cb = new Checkbox(ASCIIToUTF16("Bold")); - contents->AddChildView(cb); - cb->SetBounds(10, 10, 50, 20); - cb->SetID(BOLD_CHECKBOX_ID); - - cb = new Checkbox(ASCIIToUTF16("Italic")); - contents->AddChildView(cb); - cb->SetBounds(70, 10, 50, 20); - cb->SetID(ITALIC_CHECKBOX_ID); - - cb = new Checkbox(ASCIIToUTF16("Underlined")); - contents->AddChildView(cb); - cb->SetBounds(130, 10, 70, 20); - cb->SetID(UNDERLINED_CHECKBOX_ID); - - link = new Link(ASCIIToUTF16("Help")); - contents->AddChildView(link); - link->SetBounds(10, 35, 70, 10); - link->SetID(STYLE_HELP_LINK_ID); - - text_field = new Textfield(); - contents->AddChildView(text_field); - text_field->SetBounds(10, 50, 100, 20); - text_field->SetID(STYLE_TEXT_EDIT_ID); - - style_tab_ = new TabbedPane(); - GetContentsView()->AddChildView(style_tab_); + cb = std::make_unique<Checkbox>(ASCIIToUTF16("Bold")); + cb_ptr = contents->AddChildView(std::move(cb)); + cb_ptr->SetBounds(10, 10, 50, 20); + cb_ptr->SetID(BOLD_CHECKBOX_ID); + + cb = std::make_unique<Checkbox>(ASCIIToUTF16("Italic")); + cb_ptr = contents->AddChildView(std::move(cb)); + cb_ptr->SetBounds(70, 10, 50, 20); + cb_ptr->SetID(ITALIC_CHECKBOX_ID); + + cb = std::make_unique<Checkbox>(ASCIIToUTF16("Underlined")); + cb_ptr = contents->AddChildView(std::move(cb)); + cb_ptr->SetBounds(130, 10, 70, 20); + cb_ptr->SetID(UNDERLINED_CHECKBOX_ID); + + auto link = std::make_unique<Link>(ASCIIToUTF16("Help")); + auto* link_ptr = contents->AddChildView(std::move(link)); + link_ptr->SetBounds(10, 35, 70, 10); + link_ptr->SetID(STYLE_HELP_LINK_ID); + + text_field = std::make_unique<Textfield>(); + text_field_ptr = contents->AddChildView(std::move(text_field)); + text_field_ptr->SetBounds(10, 50, 100, 20); + text_field_ptr->SetID(STYLE_TEXT_EDIT_ID); + + auto style_tab = std::make_unique<TabbedPane>(); + style_tab_ = GetContentsView()->AddChildView(std::move(style_tab)); style_tab_->SetBounds(10, y, 210, 100); - style_tab_->AddTab(ASCIIToUTF16("Style"), contents); + style_tab_->AddTab(ASCIIToUTF16("Style"), std::move(contents)); style_tab_->GetSelectedTab()->SetID(STYLE_CONTAINER_ID); - style_tab_->AddTab(ASCIIToUTF16("Other"), new View()); + style_tab_->AddTab(ASCIIToUTF16("Other"), std::make_unique<View>()); // Right bottom box with search. - contents = new View(); + contents = std::make_unique<View>(); contents->SetBackground(CreateSolidBackground(SK_ColorWHITE)); - text_field = new Textfield(); - contents->AddChildView(text_field); - text_field->SetBounds(10, 10, 100, 20); - text_field->SetID(SEARCH_TEXTFIELD_ID); + text_field = std::make_unique<Textfield>(); + text_field_ptr = contents->AddChildView(std::move(text_field)); + text_field_ptr->SetBounds(10, 10, 100, 20); + text_field_ptr->SetID(SEARCH_TEXTFIELD_ID); button = MdTextButton::Create(nullptr, ASCIIToUTF16("Search")); button->SetBounds(112, 5, 60, 30); button->SetID(SEARCH_BUTTON_ID); contents->AddChildView(std::move(button)); - link = new Link(ASCIIToUTF16("Help")); + link = std::make_unique<Link>(ASCIIToUTF16("Help")); link->SetHorizontalAlignment(gfx::ALIGN_LEFT); link->SetID(HELP_LINK_ID); - contents->AddChildView(link); - link->SetBounds(175, 10, 30, 20); + link_ptr = contents->AddChildView(std::move(link)); + link_ptr->SetBounds(175, 10, 30, 20); - search_border_view_ = new BorderView(contents); - search_border_view_->SetID(SEARCH_CONTAINER_ID); + auto search_border_view = std::make_unique<BorderView>(contents.release()); + search_border_view->SetID(SEARCH_CONTAINER_ID); - GetContentsView()->AddChildView(search_border_view_); + search_border_view_ = + GetContentsView()->AddChildView(std::move(search_border_view)); search_border_view_->SetBounds(300, y, 240, 50); y += 60; - contents = new View(); + contents = std::make_unique<View>(); contents->SetFocusBehavior(View::FocusBehavior::ALWAYS); contents->SetBackground(CreateSolidBackground(SK_ColorBLUE)); contents->SetID(THUMBNAIL_CONTAINER_ID); @@ -564,8 +564,8 @@ void FocusTraversalTest::InitContentView() { button->SetID(THUMBNAIL_SUPER_STAR_ID); contents->AddChildView(std::move(button)); - GetContentsView()->AddChildView(contents); - contents->SetBounds(250, y, 200, 50); + auto* contents_ptr = GetContentsView()->AddChildView(std::move(contents)); + contents_ptr->SetBounds(250, y, 200, 50); // We can only call RadioButton::SetChecked() on the radio-button is part of // the view hierarchy. radio_button_to_check->SetChecked(true); diff --git a/chromium/ui/views/input_event_activation_protector.cc b/chromium/ui/views/input_event_activation_protector.cc new file mode 100644 index 00000000000..887b5e6d962 --- /dev/null +++ b/chromium/ui/views/input_event_activation_protector.cc @@ -0,0 +1,50 @@ +// Copyright (c) 2018 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "ui/views/input_event_activation_protector.h" + +#include "ui/events/event.h" +#include "ui/views/metrics.h" + +namespace views { + +void InputEventActivationProtector::VisibilityChanged(bool is_visible) { + if (is_visible) + view_shown_time_stamp_ = base::TimeTicks::Now(); +} + +bool InputEventActivationProtector::IsPossiblyUnintendedInteraction( + const ui::Event& event) { + if (view_shown_time_stamp_ == base::TimeTicks()) { + // The UI was never shown, ignore. This can happen in tests. + return false; + } + + if (!event.IsMouseEvent() && !event.IsTouchEvent()) + return false; + + const base::TimeDelta kShortInterval = + base::TimeDelta::FromMilliseconds(GetDoubleClickInterval()); + const bool short_event_after_last_event = + event.time_stamp() < last_event_timestamp_ + kShortInterval; + last_event_timestamp_ = event.time_stamp(); + + // Unintended if the user has been clicking with short intervals. + if (short_event_after_last_event) { + repeated_event_count_++; + return true; + } + repeated_event_count_ = 0; + + // Unintended if the user clicked right after the UI showed. + return event.time_stamp() < view_shown_time_stamp_ + kShortInterval; +} + +void InputEventActivationProtector::ResetForTesting() { + view_shown_time_stamp_ = base::TimeTicks(); + last_event_timestamp_ = base::TimeTicks(); + repeated_event_count_ = 0; +} + +} // namespace views diff --git a/chromium/ui/views/input_event_activation_protector.h b/chromium/ui/views/input_event_activation_protector.h new file mode 100644 index 00000000000..24a0d5b9bd2 --- /dev/null +++ b/chromium/ui/views/input_event_activation_protector.h @@ -0,0 +1,49 @@ +// Copyright (c) 2018 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef UI_VIEWS_INPUT_EVENT_ACTIVATION_PROTECTOR_H_ +#define UI_VIEWS_INPUT_EVENT_ACTIVATION_PROTECTOR_H_ + +#include "base/macros.h" +#include "base/time/time.h" +#include "ui/views/views_export.h" + +namespace ui { +class Event; +} + +namespace views { + +// The goal of this class is to prevent potentially unintentional user +// interaction with a UI element. +class VIEWS_EXPORT InputEventActivationProtector { + public: + InputEventActivationProtector() = default; + ~InputEventActivationProtector() = default; + + // Updates the state of the protector based off of visibility changes. This + // method must be called when the visibility of the view is changed. + void VisibilityChanged(bool is_visible); + + // Returns true if the event is a mouse, touch, or pointer event that took + // place within the double-click time interval after |view_shown_time_stamp_|. + bool IsPossiblyUnintendedInteraction(const ui::Event& event); + + // Resets the state for click tracking. + void ResetForTesting(); + + private: + // Timestamp of when the view being tracked is first shown. + base::TimeTicks view_shown_time_stamp_; + // Timestamp of the last event. + base::TimeTicks last_event_timestamp_; + // Number of repeated UI events with short intervals. + size_t repeated_event_count_ = 0; + + DISALLOW_COPY_AND_ASSIGN(InputEventActivationProtector); +}; + +} // namespace views + +#endif // UI_VIEWS_INPUT_EVENT_ACTIVATION_PROTECTOR_H_ diff --git a/chromium/ui/views/layout/box_layout.cc b/chromium/ui/views/layout/box_layout.cc index 94322109cf6..b8466099f9c 100644 --- a/chromium/ui/views/layout/box_layout.cc +++ b/chromium/ui/views/layout/box_layout.cc @@ -201,10 +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_ == kVertical ? HORIZONTAL_AXIS : VERTICAL_AXIS, - child.margins(), inside_border_insets_, child.margins(), - inside_border_insets_); + child_margins = + MaxAxisInsets(orientation_ == Orientation::kVertical ? HORIZONTAL_AXIS + : VERTICAL_AXIS, + child.margins(), inside_border_insets_, child.margins(), + inside_border_insets_); } else { child_margins = child.margins(); } @@ -289,7 +290,7 @@ gfx::Size BoxLayout::GetPreferredSize(const View* host) const { DCHECK_EQ(host_, host); // Calculate the child views' preferred width. int width = 0; - if (orientation_ == kVertical) { + if (orientation_ == Orientation::kVertical) { // Calculating the child views' overall preferred width is a little involved // because of the way the margins interact with |cross_axis_alignment_|. int leading = 0; @@ -371,49 +372,51 @@ int BoxLayout::GetMinimumSizeForView(const View* view) const { if (it == flex_map_.end() || !it->second.use_min_size) return 0; - return (orientation_ == kHorizontal) ? view->GetMinimumSize().width() - : view->GetMinimumSize().height(); + return (orientation_ == Orientation::kHorizontal) + ? view->GetMinimumSize().width() + : view->GetMinimumSize().height(); } int BoxLayout::MainAxisSize(const gfx::Rect& rect) const { - return orientation_ == kHorizontal ? rect.width() : rect.height(); + return orientation_ == Orientation::kHorizontal ? rect.width() + : rect.height(); } int BoxLayout::MainAxisPosition(const gfx::Rect& rect) const { - return orientation_ == kHorizontal ? rect.x() : rect.y(); + return orientation_ == Orientation::kHorizontal ? rect.x() : rect.y(); } void BoxLayout::SetMainAxisSize(int size, gfx::Rect* rect) const { - if (orientation_ == kHorizontal) + if (orientation_ == Orientation::kHorizontal) rect->set_width(size); else rect->set_height(size); } void BoxLayout::SetMainAxisPosition(int position, gfx::Rect* rect) const { - if (orientation_ == kHorizontal) + if (orientation_ == Orientation::kHorizontal) rect->set_x(position); else rect->set_y(position); } int BoxLayout::CrossAxisSize(const gfx::Rect& rect) const { - return orientation_ == kVertical ? rect.width() : rect.height(); + return orientation_ == Orientation::kVertical ? rect.width() : rect.height(); } int BoxLayout::CrossAxisPosition(const gfx::Rect& rect) const { - return orientation_ == kVertical ? rect.x() : rect.y(); + return orientation_ == Orientation::kVertical ? rect.x() : rect.y(); } void BoxLayout::SetCrossAxisSize(int size, gfx::Rect* rect) const { - if (orientation_ == kVertical) + if (orientation_ == Orientation::kVertical) rect->set_width(size); else rect->set_height(size); } void BoxLayout::SetCrossAxisPosition(int position, gfx::Rect* rect) const { - if (orientation_ == kVertical) + if (orientation_ == Orientation::kVertical) rect->set_x(position); else rect->set_y(position); @@ -421,7 +424,7 @@ void BoxLayout::SetCrossAxisPosition(int position, gfx::Rect* rect) const { int BoxLayout::MainAxisSizeForView(const ViewWrapper& view, int child_area_width) const { - return orientation_ == kHorizontal + return orientation_ == Orientation::kHorizontal ? view.GetPreferredSize().width() : view.GetHeightForWidth(cross_axis_alignment_ == CrossAxisAlignment::kStretch @@ -430,23 +433,26 @@ int BoxLayout::MainAxisSizeForView(const ViewWrapper& view, } int BoxLayout::MainAxisLeadingInset(const gfx::Insets& insets) const { - return orientation_ == kHorizontal ? insets.left() : insets.top(); + return orientation_ == Orientation::kHorizontal ? insets.left() + : insets.top(); } int BoxLayout::MainAxisTrailingInset(const gfx::Insets& insets) const { - return orientation_ == kHorizontal ? insets.right() : insets.bottom(); + return orientation_ == Orientation::kHorizontal ? insets.right() + : insets.bottom(); } int BoxLayout::CrossAxisLeadingEdge(const gfx::Rect& rect) const { - return orientation_ == kVertical ? rect.x() : rect.y(); + return orientation_ == Orientation::kVertical ? rect.x() : rect.y(); } int BoxLayout::CrossAxisLeadingInset(const gfx::Insets& insets) const { - return orientation_ == kVertical ? insets.left() : insets.top(); + return orientation_ == Orientation::kVertical ? insets.left() : insets.top(); } int BoxLayout::CrossAxisTrailingInset(const gfx::Insets& insets) const { - return orientation_ == kVertical ? insets.right() : insets.bottom(); + return orientation_ == Orientation::kVertical ? insets.right() + : insets.bottom(); } int BoxLayout::MainAxisMarginBetweenViews(const ViewWrapper& leading, @@ -462,15 +468,17 @@ gfx::Insets BoxLayout::MainAxisOuterMargin() const { if (collapse_margins_spacing_) { const ViewWrapper first(this, FirstVisibleView()); const ViewWrapper last(this, LastVisibleView()); - return MaxAxisInsets( - orientation_ == kHorizontal ? HORIZONTAL_AXIS : VERTICAL_AXIS, - inside_border_insets_, first.margins(), inside_border_insets_, - last.margins()); + return MaxAxisInsets(orientation_ == Orientation::kHorizontal + ? HORIZONTAL_AXIS + : VERTICAL_AXIS, + inside_border_insets_, first.margins(), + inside_border_insets_, last.margins()); } - return MaxAxisInsets( - orientation_ == kHorizontal ? HORIZONTAL_AXIS : VERTICAL_AXIS, - inside_border_insets_, gfx::Insets(), inside_border_insets_, - gfx::Insets()); + return MaxAxisInsets(orientation_ == Orientation::kHorizontal + ? HORIZONTAL_AXIS + : VERTICAL_AXIS, + inside_border_insets_, gfx::Insets(), + inside_border_insets_, gfx::Insets()); } gfx::Insets BoxLayout::CrossAxisMaxViewMargin() const { @@ -503,15 +511,15 @@ void BoxLayout::AdjustCrossAxisForInsets(gfx::Rect* rect) const { int BoxLayout::CrossAxisSizeForView(const ViewWrapper& view) const { // TODO(bruthig): For horizontal case use the available width and not the // preferred width. See https://crbug.com/682266. - return orientation_ == kVertical + return orientation_ == Orientation::kVertical ? view.GetPreferredSize().width() : view.GetHeightForWidth(view.GetPreferredSize().width()); } int BoxLayout::CrossAxisMarginSizeForView(const ViewWrapper& view) const { - return collapse_margins_spacing_ - ? 0 - : (orientation_ == kVertical ? view.margins().width() + return collapse_margins_spacing_ ? 0 + : (orientation_ == Orientation::kVertical + ? view.margins().width() : view.margins().height()); } @@ -522,7 +530,7 @@ int BoxLayout::CrossAxisLeadingMarginForView(const ViewWrapper& view) const { void BoxLayout::InsetCrossAxis(gfx::Rect* rect, int leading, int trailing) const { - if (orientation_ == kVertical) + if (orientation_ == Orientation::kVertical) rect->Inset(leading, 0, trailing, 0); else rect->Inset(0, leading, 0, trailing); @@ -533,7 +541,7 @@ gfx::Size BoxLayout::GetPreferredSizeForChildWidth(const View* host, DCHECK_EQ(host, host_); gfx::Rect child_area_bounds; - if (orientation_ == kHorizontal) { + if (orientation_ == Orientation::kHorizontal) { // Horizontal layouts ignore |child_area_width|, meaning they mimic the // default behavior of GridLayout::GetPreferredHeightForWidth(). // TODO(estade|bruthig): Fix this See // https://crbug.com/682266. diff --git a/chromium/ui/views/layout/box_layout.h b/chromium/ui/views/layout/box_layout.h index f91486cf76f..63567e762af 100644 --- a/chromium/ui/views/layout/box_layout.h +++ b/chromium/ui/views/layout/box_layout.h @@ -27,7 +27,7 @@ namespace views { // Excess space will not be distributed. class VIEWS_EXPORT BoxLayout : public LayoutManager { public: - enum Orientation { + enum class Orientation { kHorizontal, kVertical, }; @@ -58,7 +58,7 @@ class VIEWS_EXPORT BoxLayout : public LayoutManager { // Use |inside_border_insets| to add additional space between the child // view area and the host view border. |between_child_spacing| controls the // space in between child views. Use view->SetProperty(kMarginsKey, - // new gfx::Insets(xxx)) to add additional margins on a per-view basis. The + // gfx::Insets(xxx)) to add additional margins on a per-view basis. The // |collapse_margins_spacing| parameter controls whether or not adjacent // spacing/margins are collapsed based on the max of the two values. For the // cross axis, |collapse_margins_spacing| will collapse to the max of diff --git a/chromium/ui/views/layout/box_layout_unittest.cc b/chromium/ui/views/layout/box_layout_unittest.cc index 21e806582ec..9e7702f73aa 100644 --- a/chromium/ui/views/layout/box_layout_unittest.cc +++ b/chromium/ui/views/layout/box_layout_unittest.cc @@ -34,14 +34,14 @@ class BoxLayoutTest : public testing::Test { } // namespace TEST_F(BoxLayoutTest, Empty) { - BoxLayout* layout = host_->SetLayoutManager( - std::make_unique<BoxLayout>(BoxLayout::kHorizontal, gfx::Insets(10), 20)); + BoxLayout* layout = host_->SetLayoutManager(std::make_unique<BoxLayout>( + BoxLayout::Orientation::kHorizontal, gfx::Insets(10), 20)); EXPECT_EQ(gfx::Size(20, 20), layout->GetPreferredSize(host_.get())); } TEST_F(BoxLayoutTest, AlignmentHorizontal) { BoxLayout* layout = host_->SetLayoutManager( - std::make_unique<BoxLayout>(BoxLayout::kHorizontal)); + std::make_unique<BoxLayout>(BoxLayout::Orientation::kHorizontal)); View* v1 = new StaticSizedView(gfx::Size(10, 20)); host_->AddChildView(v1); View* v2 = new StaticSizedView(gfx::Size(10, 10)); @@ -55,7 +55,7 @@ TEST_F(BoxLayoutTest, AlignmentHorizontal) { TEST_F(BoxLayoutTest, AlignmentVertical) { BoxLayout* layout = host_->SetLayoutManager( - std::make_unique<BoxLayout>(BoxLayout::kVertical)); + std::make_unique<BoxLayout>(BoxLayout::Orientation::kVertical)); View* v1 = new StaticSizedView(gfx::Size(20, 10)); host_->AddChildView(v1); View* v2 = new StaticSizedView(gfx::Size(10, 10)); @@ -68,8 +68,8 @@ TEST_F(BoxLayoutTest, AlignmentVertical) { } TEST_F(BoxLayoutTest, SetInsideBorderInsets) { - BoxLayout* layout = host_->SetLayoutManager( - std::make_unique<BoxLayout>(BoxLayout::kHorizontal, gfx::Insets(20, 10))); + BoxLayout* layout = host_->SetLayoutManager(std::make_unique<BoxLayout>( + BoxLayout::Orientation::kHorizontal, gfx::Insets(20, 10))); View* v1 = new StaticSizedView(gfx::Size(10, 20)); host_->AddChildView(v1); View* v2 = new StaticSizedView(gfx::Size(10, 10)); @@ -90,8 +90,8 @@ TEST_F(BoxLayoutTest, SetInsideBorderInsets) { } TEST_F(BoxLayoutTest, Spacing) { - BoxLayout* layout = host_->SetLayoutManager( - std::make_unique<BoxLayout>(BoxLayout::kHorizontal, gfx::Insets(7), 8)); + BoxLayout* layout = host_->SetLayoutManager(std::make_unique<BoxLayout>( + BoxLayout::Orientation::kHorizontal, gfx::Insets(7), 8)); View* v1 = new StaticSizedView(gfx::Size(10, 20)); host_->AddChildView(v1); View* v2 = new StaticSizedView(gfx::Size(10, 20)); @@ -105,7 +105,7 @@ TEST_F(BoxLayoutTest, Spacing) { TEST_F(BoxLayoutTest, Overflow) { BoxLayout* layout = host_->SetLayoutManager( - std::make_unique<BoxLayout>(BoxLayout::kHorizontal)); + std::make_unique<BoxLayout>(BoxLayout::Orientation::kHorizontal)); View* v1 = new StaticSizedView(gfx::Size(20, 20)); host_->AddChildView(v1); View* v2 = new StaticSizedView(gfx::Size(10, 20)); @@ -137,8 +137,8 @@ TEST_F(BoxLayoutTest, Overflow) { } TEST_F(BoxLayoutTest, NoSpace) { - host_->SetLayoutManager( - std::make_unique<BoxLayout>(BoxLayout::kHorizontal, gfx::Insets(10), 10)); + host_->SetLayoutManager(std::make_unique<BoxLayout>( + BoxLayout::Orientation::kHorizontal, gfx::Insets(10), 10)); View* childView = new StaticSizedView(gfx::Size(20, 20)); host_->AddChildView(childView); host_->SetBounds(0, 0, 10, 10); @@ -147,8 +147,8 @@ TEST_F(BoxLayoutTest, NoSpace) { } TEST_F(BoxLayoutTest, InvisibleChild) { - BoxLayout* layout = host_->SetLayoutManager( - std::make_unique<BoxLayout>(BoxLayout::kHorizontal, gfx::Insets(10), 10)); + BoxLayout* layout = host_->SetLayoutManager(std::make_unique<BoxLayout>( + BoxLayout::Orientation::kHorizontal, gfx::Insets(10), 10)); View* v1 = new StaticSizedView(gfx::Size(20, 20)); v1->SetVisible(false); host_->AddChildView(v1); @@ -162,7 +162,7 @@ TEST_F(BoxLayoutTest, InvisibleChild) { TEST_F(BoxLayoutTest, UseHeightForWidth) { BoxLayout* layout = host_->SetLayoutManager( - std::make_unique<BoxLayout>(BoxLayout::kVertical)); + std::make_unique<BoxLayout>(BoxLayout::Orientation::kVertical)); View* v1 = new StaticSizedView(gfx::Size(20, 10)); host_->AddChildView(v1); ProportionallySizedView* v2 = new ProportionallySizedView(2); @@ -192,8 +192,9 @@ TEST_F(BoxLayoutTest, UseHeightForWidth) { TEST_F(BoxLayoutTest, EmptyPreferredSize) { for (size_t i = 0; i < 2; i++) { - BoxLayout::Orientation orientation = i == 0 ? BoxLayout::kHorizontal : - BoxLayout::kVertical; + BoxLayout::Orientation orientation = + i == 0 ? BoxLayout::Orientation::kHorizontal + : BoxLayout::Orientation::kVertical; host_->RemoveAllChildViews(true); host_->SetLayoutManager( std::make_unique<BoxLayout>(orientation, gfx::Insets(), 5)); @@ -217,8 +218,8 @@ TEST_F(BoxLayoutTest, EmptyPreferredSize) { // empty preferred size, simultaneously. TEST_F(BoxLayoutTest, EmptyPreferredSizeWithFlexLayoutAndChildSpacing) { host_->RemoveAllChildViews(true); - BoxLayout* layout = host_->SetLayoutManager( - std::make_unique<BoxLayout>(BoxLayout::kHorizontal, gfx::Insets(), 5)); + BoxLayout* layout = host_->SetLayoutManager(std::make_unique<BoxLayout>( + BoxLayout::Orientation::kHorizontal, gfx::Insets(), 5)); View* v1 = new StaticSizedView(gfx::Size()); host_->AddChildView(v1); View* v2 = new StaticSizedView(gfx::Size(10, 10)); @@ -233,8 +234,8 @@ TEST_F(BoxLayoutTest, EmptyPreferredSizeWithFlexLayoutAndChildSpacing) { } TEST_F(BoxLayoutTest, MainAxisAlignmentHorizontal) { - BoxLayout* layout = host_->SetLayoutManager( - std::make_unique<BoxLayout>(BoxLayout::kHorizontal, gfx::Insets(10), 10)); + BoxLayout* layout = host_->SetLayoutManager(std::make_unique<BoxLayout>( + BoxLayout::Orientation::kHorizontal, gfx::Insets(10), 10)); View* v1 = new StaticSizedView(gfx::Size(20, 20)); host_->AddChildView(v1); @@ -269,8 +270,8 @@ TEST_F(BoxLayoutTest, MainAxisAlignmentHorizontal) { } TEST_F(BoxLayoutTest, MainAxisAlignmentVertical) { - BoxLayout* layout = host_->SetLayoutManager( - std::make_unique<BoxLayout>(BoxLayout::kVertical, gfx::Insets(10), 10)); + BoxLayout* layout = host_->SetLayoutManager(std::make_unique<BoxLayout>( + BoxLayout::Orientation::kVertical, gfx::Insets(10), 10)); View* v1 = new StaticSizedView(gfx::Size(20, 20)); host_->AddChildView(v1); @@ -305,8 +306,8 @@ TEST_F(BoxLayoutTest, MainAxisAlignmentVertical) { } TEST_F(BoxLayoutTest, CrossAxisAlignmentHorizontal) { - BoxLayout* layout = host_->SetLayoutManager( - std::make_unique<BoxLayout>(BoxLayout::kHorizontal, gfx::Insets(10), 10)); + BoxLayout* layout = host_->SetLayoutManager(std::make_unique<BoxLayout>( + BoxLayout::Orientation::kHorizontal, gfx::Insets(10), 10)); View* v1 = new StaticSizedView(gfx::Size(20, 20)); host_->AddChildView(v1); @@ -347,8 +348,8 @@ TEST_F(BoxLayoutTest, CrossAxisAlignmentHorizontal) { } TEST_F(BoxLayoutTest, CrossAxisAlignmentVertical) { - BoxLayout* layout = host_->SetLayoutManager( - std::make_unique<BoxLayout>(BoxLayout::kVertical, gfx::Insets(10), 10)); + BoxLayout* layout = host_->SetLayoutManager(std::make_unique<BoxLayout>( + BoxLayout::Orientation::kVertical, gfx::Insets(10), 10)); View* v1 = new StaticSizedView(gfx::Size(20, 20)); host_->AddChildView(v1); @@ -389,8 +390,8 @@ TEST_F(BoxLayoutTest, CrossAxisAlignmentVertical) { } TEST_F(BoxLayoutTest, FlexAll) { - BoxLayout* layout = host_->SetLayoutManager( - std::make_unique<BoxLayout>(BoxLayout::kHorizontal, gfx::Insets(10), 10)); + BoxLayout* layout = host_->SetLayoutManager(std::make_unique<BoxLayout>( + BoxLayout::Orientation::kHorizontal, gfx::Insets(10), 10)); layout->SetDefaultFlex(1); View* v1 = new StaticSizedView(gfx::Size(20, 20)); @@ -409,8 +410,8 @@ TEST_F(BoxLayoutTest, FlexAll) { } TEST_F(BoxLayoutTest, FlexGrowVertical) { - BoxLayout* layout = host_->SetLayoutManager( - std::make_unique<BoxLayout>(BoxLayout::kVertical, gfx::Insets(10), 10)); + BoxLayout* layout = host_->SetLayoutManager(std::make_unique<BoxLayout>( + BoxLayout::Orientation::kVertical, gfx::Insets(10), 10)); View* v1 = new StaticSizedView(gfx::Size(20, 20)); host_->AddChildView(v1); @@ -462,7 +463,7 @@ TEST_F(BoxLayoutTest, FlexGrowVertical) { TEST_F(BoxLayoutTest, FlexGrowHorizontalWithRemainder) { BoxLayout* layout = host_->SetLayoutManager( - std::make_unique<BoxLayout>(BoxLayout::kHorizontal)); + std::make_unique<BoxLayout>(BoxLayout::Orientation::kHorizontal)); layout->SetDefaultFlex(1); std::vector<View*> views; for (int i = 0; i < 5; ++i) { @@ -486,7 +487,7 @@ TEST_F(BoxLayoutTest, FlexGrowHorizontalWithRemainder) { TEST_F(BoxLayoutTest, FlexGrowHorizontalWithRemainder2) { BoxLayout* layout = host_->SetLayoutManager( - std::make_unique<BoxLayout>(BoxLayout::kHorizontal)); + std::make_unique<BoxLayout>(BoxLayout::Orientation::kHorizontal)); layout->SetDefaultFlex(1); std::vector<View*> views; for (int i = 0; i < 4; ++i) { @@ -508,8 +509,8 @@ TEST_F(BoxLayoutTest, FlexGrowHorizontalWithRemainder2) { } TEST_F(BoxLayoutTest, FlexShrinkHorizontal) { - BoxLayout* layout = host_->SetLayoutManager( - std::make_unique<BoxLayout>(BoxLayout::kHorizontal, gfx::Insets(10), 10)); + BoxLayout* layout = host_->SetLayoutManager(std::make_unique<BoxLayout>( + BoxLayout::Orientation::kHorizontal, gfx::Insets(10), 10)); View* v1 = new StaticSizedView(gfx::Size(20, 20)); host_->AddChildView(v1); @@ -563,7 +564,7 @@ TEST_F(BoxLayoutTest, FlexShrinkHorizontal) { TEST_F(BoxLayoutTest, FlexShrinkVerticalWithRemainder) { BoxLayout* layout = host_->SetLayoutManager( - std::make_unique<BoxLayout>(BoxLayout::kVertical)); + std::make_unique<BoxLayout>(BoxLayout::Orientation::kVertical)); View* v1 = new StaticSizedView(gfx::Size(20, 10)); host_->AddChildView(v1); View* v2 = new StaticSizedView(gfx::Size(20, 20)); @@ -608,7 +609,7 @@ TEST_F(BoxLayoutTest, FlexShrinkVerticalWithRemainder) { TEST_F(BoxLayoutTest, MinimumCrossAxisVertical) { BoxLayout* layout = host_->SetLayoutManager( - std::make_unique<BoxLayout>(BoxLayout::kVertical)); + std::make_unique<BoxLayout>(BoxLayout::Orientation::kVertical)); View* v1 = new StaticSizedView(gfx::Size(20, 10)); host_->AddChildView(v1); layout->set_minimum_cross_axis_size(30); @@ -618,7 +619,7 @@ TEST_F(BoxLayoutTest, MinimumCrossAxisVertical) { TEST_F(BoxLayoutTest, MinimumCrossAxisHorizontal) { BoxLayout* layout = host_->SetLayoutManager( - std::make_unique<BoxLayout>(BoxLayout::kHorizontal)); + std::make_unique<BoxLayout>(BoxLayout::Orientation::kHorizontal)); View* v1 = new StaticSizedView(gfx::Size(20, 10)); host_->AddChildView(v1); layout->set_minimum_cross_axis_size(30); @@ -628,12 +629,12 @@ TEST_F(BoxLayoutTest, MinimumCrossAxisHorizontal) { TEST_F(BoxLayoutTest, MarginsUncollapsedHorizontal) { BoxLayout* layout = host_->SetLayoutManager( - std::make_unique<BoxLayout>(BoxLayout::kHorizontal)); + std::make_unique<BoxLayout>(BoxLayout::Orientation::kHorizontal)); View* v1 = new StaticSizedView(gfx::Size(20, 10)); - v1->SetProperty(kMarginsKey, new gfx::Insets(5, 5, 5, 5)); + v1->SetProperty(kMarginsKey, gfx::Insets(5, 5, 5, 5)); host_->AddChildView(v1); View* v2 = new StaticSizedView(gfx::Size(20, 10)); - v2->SetProperty(kMarginsKey, new gfx::Insets(6, 4, 6, 4)); + v2->SetProperty(kMarginsKey, gfx::Insets(6, 4, 6, 4)); host_->AddChildView(v2); EXPECT_EQ(gfx::Size(58, 22), layout->GetPreferredSize(host_.get())); @@ -645,12 +646,12 @@ TEST_F(BoxLayoutTest, MarginsUncollapsedHorizontal) { TEST_F(BoxLayoutTest, MarginsCollapsedHorizontal) { BoxLayout* layout = host_->SetLayoutManager(std::make_unique<BoxLayout>( - BoxLayout::kHorizontal, gfx::Insets(0, 0), 0, true)); + BoxLayout::Orientation::kHorizontal, gfx::Insets(0, 0), 0, true)); View* v1 = new StaticSizedView(gfx::Size(20, 10)); - v1->SetProperty(kMarginsKey, new gfx::Insets(5, 5, 5, 5)); + v1->SetProperty(kMarginsKey, gfx::Insets(5, 5, 5, 5)); host_->AddChildView(v1); View* v2 = new StaticSizedView(gfx::Size(20, 10)); - v2->SetProperty(kMarginsKey, new gfx::Insets(6, 4, 6, 4)); + v2->SetProperty(kMarginsKey, gfx::Insets(6, 4, 6, 4)); host_->AddChildView(v2); EXPECT_EQ(gfx::Size(54, 22), layout->GetPreferredSize(host_.get())); @@ -662,12 +663,12 @@ TEST_F(BoxLayoutTest, MarginsCollapsedHorizontal) { TEST_F(BoxLayoutTest, MarginsUncollapsedVertical) { BoxLayout* layout = host_->SetLayoutManager( - std::make_unique<BoxLayout>(BoxLayout::kVertical)); + std::make_unique<BoxLayout>(BoxLayout::Orientation::kVertical)); View* v1 = new StaticSizedView(gfx::Size(20, 10)); - v1->SetProperty(kMarginsKey, new gfx::Insets(5, 5, 5, 5)); + v1->SetProperty(kMarginsKey, gfx::Insets(5, 5, 5, 5)); host_->AddChildView(v1); View* v2 = new StaticSizedView(gfx::Size(20, 10)); - v2->SetProperty(kMarginsKey, new gfx::Insets(6, 4, 6, 4)); + v2->SetProperty(kMarginsKey, gfx::Insets(6, 4, 6, 4)); host_->AddChildView(v2); EXPECT_EQ(gfx::Size(30, 42), layout->GetPreferredSize(host_.get())); @@ -679,12 +680,12 @@ TEST_F(BoxLayoutTest, MarginsUncollapsedVertical) { TEST_F(BoxLayoutTest, MarginsCollapsedVertical) { BoxLayout* layout = host_->SetLayoutManager(std::make_unique<BoxLayout>( - BoxLayout::kVertical, gfx::Insets(0, 0), 0, true)); + BoxLayout::Orientation::kVertical, gfx::Insets(0, 0), 0, true)); View* v1 = new StaticSizedView(gfx::Size(20, 10)); - v1->SetProperty(kMarginsKey, new gfx::Insets(5, 5, 5, 5)); + v1->SetProperty(kMarginsKey, gfx::Insets(5, 5, 5, 5)); host_->AddChildView(v1); View* v2 = new StaticSizedView(gfx::Size(20, 10)); - v2->SetProperty(kMarginsKey, new gfx::Insets(6, 4, 6, 4)); + v2->SetProperty(kMarginsKey, gfx::Insets(6, 4, 6, 4)); host_->AddChildView(v2); EXPECT_EQ(gfx::Size(30, 37), layout->GetPreferredSize(host_.get())); @@ -695,15 +696,16 @@ TEST_F(BoxLayoutTest, MarginsCollapsedVertical) { } TEST_F(BoxLayoutTest, UnbalancedMarginsUncollapsedHorizontal) { - auto layout_owner = std::make_unique<BoxLayout>(BoxLayout::kHorizontal); + auto layout_owner = + std::make_unique<BoxLayout>(BoxLayout::Orientation::kHorizontal); layout_owner->set_cross_axis_alignment( BoxLayout::CrossAxisAlignment::kCenter); BoxLayout* layout = host_->SetLayoutManager(std::move(layout_owner)); View* v1 = new StaticSizedView(gfx::Size(20, 10)); - v1->SetProperty(kMarginsKey, new gfx::Insets(5, 5, 4, 4)); + v1->SetProperty(kMarginsKey, gfx::Insets(5, 5, 4, 4)); host_->AddChildView(v1); View* v2 = new StaticSizedView(gfx::Size(20, 10)); - v2->SetProperty(kMarginsKey, new gfx::Insets(6, 4, 3, 6)); + v2->SetProperty(kMarginsKey, gfx::Insets(6, 4, 3, 6)); host_->AddChildView(v2); EXPECT_EQ(gfx::Size(59, 20), layout->GetPreferredSize(host_.get())); @@ -714,16 +716,16 @@ TEST_F(BoxLayoutTest, UnbalancedMarginsUncollapsedHorizontal) { } TEST_F(BoxLayoutTest, UnbalancedMarginsCollapsedHorizontal) { - auto layout_owner = std::make_unique<BoxLayout>(BoxLayout::kHorizontal, - gfx::Insets(0, 0), 0, true); + auto layout_owner = std::make_unique<BoxLayout>( + BoxLayout::Orientation::kHorizontal, gfx::Insets(0, 0), 0, true); layout_owner->set_cross_axis_alignment( BoxLayout::CrossAxisAlignment::kCenter); BoxLayout* layout = host_->SetLayoutManager(std::move(layout_owner)); View* v1 = new StaticSizedView(gfx::Size(20, 10)); - v1->SetProperty(kMarginsKey, new gfx::Insets(5, 5, 4, 4)); + v1->SetProperty(kMarginsKey, gfx::Insets(5, 5, 4, 4)); host_->AddChildView(v1); View* v2 = new StaticSizedView(gfx::Size(20, 10)); - v2->SetProperty(kMarginsKey, new gfx::Insets(6, 4, 3, 6)); + v2->SetProperty(kMarginsKey, gfx::Insets(6, 4, 3, 6)); host_->AddChildView(v2); EXPECT_EQ(gfx::Size(55, 20), layout->GetPreferredSize(host_.get())); @@ -734,15 +736,16 @@ TEST_F(BoxLayoutTest, UnbalancedMarginsCollapsedHorizontal) { } TEST_F(BoxLayoutTest, UnbalancedMarginsUncollapsedVertical) { - auto layout_owner = std::make_unique<BoxLayout>(BoxLayout::kVertical); + auto layout_owner = + std::make_unique<BoxLayout>(BoxLayout::Orientation::kVertical); layout_owner->set_cross_axis_alignment( BoxLayout::CrossAxisAlignment::kCenter); BoxLayout* layout = host_->SetLayoutManager(std::move(layout_owner)); View* v1 = new StaticSizedView(gfx::Size(20, 10)); - v1->SetProperty(kMarginsKey, new gfx::Insets(4, 5, 5, 3)); + v1->SetProperty(kMarginsKey, gfx::Insets(4, 5, 5, 3)); host_->AddChildView(v1); View* v2 = new StaticSizedView(gfx::Size(20, 10)); - v2->SetProperty(kMarginsKey, new gfx::Insets(6, 4, 3, 5)); + v2->SetProperty(kMarginsKey, gfx::Insets(6, 4, 3, 5)); host_->AddChildView(v2); EXPECT_EQ(gfx::Size(30, 38), layout->GetPreferredSize(host_.get())); @@ -753,16 +756,16 @@ TEST_F(BoxLayoutTest, UnbalancedMarginsUncollapsedVertical) { } TEST_F(BoxLayoutTest, UnbalancedMarginsCollapsedVertical) { - auto layout_owner = std::make_unique<BoxLayout>(BoxLayout::kVertical, - gfx::Insets(0, 0), 0, true); + auto layout_owner = std::make_unique<BoxLayout>( + BoxLayout::Orientation::kVertical, gfx::Insets(0, 0), 0, true); layout_owner->set_cross_axis_alignment( BoxLayout::CrossAxisAlignment::kCenter); BoxLayout* layout = host_->SetLayoutManager(std::move(layout_owner)); View* v1 = new StaticSizedView(gfx::Size(20, 10)); - v1->SetProperty(kMarginsKey, new gfx::Insets(4, 5, 5, 3)); + v1->SetProperty(kMarginsKey, gfx::Insets(4, 5, 5, 3)); host_->AddChildView(v1); View* v2 = new StaticSizedView(gfx::Size(20, 10)); - v2->SetProperty(kMarginsKey, new gfx::Insets(6, 4, 3, 5)); + v2->SetProperty(kMarginsKey, gfx::Insets(6, 4, 3, 5)); host_->AddChildView(v2); EXPECT_EQ(gfx::Size(30, 33), layout->GetPreferredSize(host_.get())); @@ -775,13 +778,13 @@ TEST_F(BoxLayoutTest, UnbalancedMarginsCollapsedVertical) { TEST_F(BoxLayoutTest, OverlappingCrossMarginsAlignEnd) { { BoxLayout* layout = host_->SetLayoutManager( - std::make_unique<BoxLayout>(BoxLayout::kHorizontal)); + std::make_unique<BoxLayout>(BoxLayout::Orientation::kHorizontal)); layout->set_cross_axis_alignment(BoxLayout::CrossAxisAlignment::kEnd); View* v1 = new StaticSizedView(gfx::Size(20, 4)); - v1->SetProperty(kMarginsKey, new gfx::Insets(3, 0, 0, 0)); + v1->SetProperty(kMarginsKey, gfx::Insets(3, 0, 0, 0)); host_->AddChildView(v1); View* v2 = new StaticSizedView(gfx::Size(20, 5)); - v2->SetProperty(kMarginsKey, new gfx::Insets(0, 0, 2, 0)); + v2->SetProperty(kMarginsKey, gfx::Insets(0, 0, 2, 0)); host_->AddChildView(v2); EXPECT_EQ(9, layout->GetPreferredSize(host_.get()).height()); @@ -789,13 +792,13 @@ TEST_F(BoxLayoutTest, OverlappingCrossMarginsAlignEnd) { host_->RemoveAllChildViews(true); { BoxLayout* layout = host_->SetLayoutManager(std::make_unique<BoxLayout>( - BoxLayout::kHorizontal, gfx::Insets(0, 0), 0, true)); + BoxLayout::Orientation::kHorizontal, gfx::Insets(0, 0), 0, true)); layout->set_cross_axis_alignment(BoxLayout::CrossAxisAlignment::kEnd); View* v1 = new StaticSizedView(gfx::Size(20, 4)); - v1->SetProperty(kMarginsKey, new gfx::Insets(3, 0, 0, 0)); + v1->SetProperty(kMarginsKey, gfx::Insets(3, 0, 0, 0)); host_->AddChildView(v1); View* v2 = new StaticSizedView(gfx::Size(20, 5)); - v2->SetProperty(kMarginsKey, new gfx::Insets(0, 0, 2, 0)); + v2->SetProperty(kMarginsKey, gfx::Insets(0, 0, 2, 0)); host_->AddChildView(v2); EXPECT_EQ(9, layout->GetPreferredSize(host_.get()).height()); @@ -805,13 +808,13 @@ TEST_F(BoxLayoutTest, OverlappingCrossMarginsAlignEnd) { TEST_F(BoxLayoutTest, OverlappingCrossMarginsAlignStretch) { { BoxLayout* layout = host_->SetLayoutManager( - std::make_unique<BoxLayout>(BoxLayout::kHorizontal)); + std::make_unique<BoxLayout>(BoxLayout::Orientation::kHorizontal)); layout->set_cross_axis_alignment(BoxLayout::CrossAxisAlignment::kStretch); View* v1 = new StaticSizedView(gfx::Size(20, 4)); - v1->SetProperty(kMarginsKey, new gfx::Insets(3, 0, 0, 0)); + v1->SetProperty(kMarginsKey, gfx::Insets(3, 0, 0, 0)); host_->AddChildView(v1); View* v2 = new StaticSizedView(gfx::Size(20, 5)); - v2->SetProperty(kMarginsKey, new gfx::Insets(0, 0, 2, 0)); + v2->SetProperty(kMarginsKey, gfx::Insets(0, 0, 2, 0)); host_->AddChildView(v2); EXPECT_EQ(10, layout->GetPreferredSize(host_.get()).height()); @@ -819,13 +822,13 @@ TEST_F(BoxLayoutTest, OverlappingCrossMarginsAlignStretch) { host_->RemoveAllChildViews(true); { BoxLayout* layout = host_->SetLayoutManager(std::make_unique<BoxLayout>( - BoxLayout::kHorizontal, gfx::Insets(0, 0), 0, true)); + BoxLayout::Orientation::kHorizontal, gfx::Insets(0, 0), 0, true)); layout->set_cross_axis_alignment(BoxLayout::CrossAxisAlignment::kStretch); View* v1 = new StaticSizedView(gfx::Size(20, 4)); - v1->SetProperty(kMarginsKey, new gfx::Insets(3, 0, 0, 0)); + v1->SetProperty(kMarginsKey, gfx::Insets(3, 0, 0, 0)); host_->AddChildView(v1); View* v2 = new StaticSizedView(gfx::Size(20, 5)); - v2->SetProperty(kMarginsKey, new gfx::Insets(0, 0, 2, 0)); + v2->SetProperty(kMarginsKey, gfx::Insets(0, 0, 2, 0)); host_->AddChildView(v2); EXPECT_EQ(10, layout->GetPreferredSize(host_.get()).height()); @@ -835,13 +838,13 @@ TEST_F(BoxLayoutTest, OverlappingCrossMarginsAlignStretch) { TEST_F(BoxLayoutTest, OverlappingCrossMarginsAlignStart) { { BoxLayout* layout = host_->SetLayoutManager( - std::make_unique<BoxLayout>(BoxLayout::kHorizontal)); + std::make_unique<BoxLayout>(BoxLayout::Orientation::kHorizontal)); layout->set_cross_axis_alignment(BoxLayout::CrossAxisAlignment::kStart); View* v1 = new StaticSizedView(gfx::Size(20, 4)); - v1->SetProperty(kMarginsKey, new gfx::Insets(0, 0, 3, 0)); + v1->SetProperty(kMarginsKey, gfx::Insets(0, 0, 3, 0)); host_->AddChildView(v1); View* v2 = new StaticSizedView(gfx::Size(20, 5)); - v2->SetProperty(kMarginsKey, new gfx::Insets(2, 0, 0, 0)); + v2->SetProperty(kMarginsKey, gfx::Insets(2, 0, 0, 0)); host_->AddChildView(v2); EXPECT_EQ(9, layout->GetPreferredSize(host_.get()).height()); @@ -849,13 +852,13 @@ TEST_F(BoxLayoutTest, OverlappingCrossMarginsAlignStart) { host_->RemoveAllChildViews(true); { BoxLayout* layout = host_->SetLayoutManager(std::make_unique<BoxLayout>( - BoxLayout::kHorizontal, gfx::Insets(0, 0), 0, true)); + BoxLayout::Orientation::kHorizontal, gfx::Insets(0, 0), 0, true)); layout->set_cross_axis_alignment(BoxLayout::CrossAxisAlignment::kStart); View* v1 = new StaticSizedView(gfx::Size(20, 4)); - v1->SetProperty(kMarginsKey, new gfx::Insets(0, 0, 3, 0)); + v1->SetProperty(kMarginsKey, gfx::Insets(0, 0, 3, 0)); host_->AddChildView(v1); View* v2 = new StaticSizedView(gfx::Size(20, 5)); - v2->SetProperty(kMarginsKey, new gfx::Insets(2, 0, 0, 0)); + v2->SetProperty(kMarginsKey, gfx::Insets(2, 0, 0, 0)); host_->AddChildView(v2); EXPECT_EQ(9, layout->GetPreferredSize(host_.get()).height()); @@ -863,8 +866,8 @@ TEST_F(BoxLayoutTest, OverlappingCrossMarginsAlignStart) { } TEST_F(BoxLayoutTest, NegativeBetweenChildSpacing) { - BoxLayout* layout = host_->SetLayoutManager( - std::make_unique<BoxLayout>(BoxLayout::kVertical, gfx::Insets(), -10)); + BoxLayout* layout = host_->SetLayoutManager(std::make_unique<BoxLayout>( + BoxLayout::Orientation::kVertical, gfx::Insets(), -10)); View* v1 = new StaticSizedView(gfx::Size(20, 20)); host_->AddChildView(v1); View* v2 = new StaticSizedView(gfx::Size(20, 15)); @@ -879,8 +882,8 @@ TEST_F(BoxLayoutTest, NegativeBetweenChildSpacing) { } TEST_F(BoxLayoutTest, MinimumChildSize) { - BoxLayout* layout = host_->SetLayoutManager( - std::make_unique<BoxLayout>(BoxLayout::kHorizontal, gfx::Insets())); + BoxLayout* layout = host_->SetLayoutManager(std::make_unique<BoxLayout>( + BoxLayout::Orientation::kHorizontal, gfx::Insets())); StaticSizedView* v1 = new StaticSizedView(gfx::Size(20, 20)); host_->AddChildView(v1); StaticSizedView* v2 = new StaticSizedView(gfx::Size(20, 20)); diff --git a/chromium/ui/views/layout/flex_layout.cc b/chromium/ui/views/layout/flex_layout.cc index b8e2d10255e..91ad5ce2669 100644 --- a/chromium/ui/views/layout/flex_layout.cc +++ b/chromium/ui/views/layout/flex_layout.cc @@ -28,131 +28,57 @@ namespace views { -namespace internal { +using internal::NormalizedInsets; +using internal::NormalizedPoint; +using internal::NormalizedRect; +using internal::NormalizedSize; +using internal::NormalizedSizeBounds; + +using internal::Denormalize; +using internal::Normalize; namespace { // Layout information for a specific child view in a proposed layout. -struct ChildLayout { - ChildLayout(View* view, const FlexSpecification& flex) - : view(view), flex(flex) {} - ChildLayout(ChildLayout&& other) = default; - - View* view = nullptr; - bool excluded = false; - // Indicates whether external code has called SetVisible(false) on the view. - bool hidden_by_owner = false; - // Indicates whether the layout has chosen to display this child view. - bool visible = false; +struct FlexChildData { + explicit FlexChildData(const FlexSpecification& flex) : flex(flex) {} + FlexChildData(FlexChildData&& other) = default; + // Start with zero size rather than unspecified size bounds because we start // all layouts with controls at their minimum allowed sizes. NormalizedSizeBounds available_size{0, 0}; NormalizedSize preferred_size; NormalizedSize current_size; - NormalizedRect actual_bounds; NormalizedInsets margins; NormalizedInsets internal_padding; + NormalizedRect actual_bounds; FlexSpecification flex; private: // Copying this struct would be expensive and they only ever live in a vector // in Layout (see below) so we'll only allow move semantics. - DISALLOW_COPY_AND_ASSIGN(ChildLayout); + DISALLOW_COPY_AND_ASSIGN(FlexChildData); }; -// Represents a specific stored layout given a set of size bounds. -struct Layout { - explicit Layout(int64_t layout_counter) : layout_id(layout_counter) {} - - // Indicates which layout pass this layout was created/last validated on. If - // this number disagrees with FlexLayoutInternal::layout_counter_, we need to - // re-validate the layout before using it. - mutable uint32_t layout_id; - std::vector<ChildLayout> child_layouts; - NormalizedInsets interior_margin; - NormalizedSizeBounds available_size; - NormalizedSize total_size; - - private: - DISALLOW_COPY_AND_ASSIGN(Layout); -}; - -// Preserves layouts for common layout requests, because we may be asked to -// recompute them many times but don't want to repeat the calculation. -// -// Specifically, we will be asked for: -// - The layout's minimum size (bounds = {0, 0}) -// - The layout's preferred size (neither bound specified) -// - The final layout at the dimensions of the host view (both bounds nonzero) -// -// There is also the possibility of a view with a flex layout being embedded in -// a view with a flex layout, where the inner view has a nontrivial flex -// specification. In that case, in order to handle e.g. labels in the inner -// view, the outer layout manager will ask for the inner view's preferred height -// for its width, which potentially involves another layout calculation. -// However, these calculations are transient - they only happen when the layout -// is recalculated or validated, and in the latter case only one set of bounds -// is evaluated. So we will keep exactly one of these layouts around. -// -// As a first pass, we'll store all of these specifically. In the future we can -// switch to using something more sophisticated, like an MRU cache, if we find -// we're thrashing for some other reason. -class LayoutCache { - public: - // Removes all saved layouts. - void Clear() { - minimum_.reset(); - preferred_.reset(); - intermediate_.layout.reset(); - current_.layout.reset(); - } - - // Gets a cached layout, or nullptr if no layout is cached for the specified - // bounds. - Layout* Get(const NormalizedSizeBounds& bounds) const { - if (bounds == NormalizedSizeBounds(0, 0)) - return minimum_.get(); - if (bounds == NormalizedSizeBounds()) - return preferred_.get(); - if (bounds == intermediate_.bounds) - return intermediate_.layout.get(); - if (bounds == current_.bounds) - return current_.layout.get(); - return nullptr; - } +template <typename T> +T GetViewProperty(const View* view, + const ui::PropertyHandler& defaults, + const ui::ClassProperty<T*>* property) { + T* found_value = view->GetProperty(property); + if (found_value) + return *found_value; + found_value = defaults.GetProperty(property); + if (found_value) + return *found_value; + return T(); +} - // Caches a layout associated with the specified bounds. - void Put(const NormalizedSizeBounds& bounds, std::unique_ptr<Layout> layout) { - if (bounds == NormalizedSizeBounds(0, 0)) { - minimum_ = std::move(layout); - } else if (bounds == NormalizedSizeBounds()) { - preferred_ = std::move(layout); - } else if (!bounds.cross() || !bounds.main()) { - intermediate_.bounds = bounds; - intermediate_.layout = std::move(layout); - } else { - current_.bounds = bounds; - current_.layout = std::move(layout); - } - } +} // anonymous namespace - private: - struct CacheEntry { - NormalizedSizeBounds bounds; - std::unique_ptr<Layout> layout; - }; - - std::unique_ptr<Layout> minimum_; - std::unique_ptr<Layout> preferred_; - - // TODO(dfried): consider replacing these with an MRUCache if this ends up - // thrashing a lot (see note above). - CacheEntry intermediate_; - CacheEntry current_; -}; +// Private implementation ------------------------------------------------------ // Calculates and maintains 1D spacing between a sequence of child views. -class ChildViewSpacing { +class FlexLayout::ChildViewSpacing { public: // Given the indices of two child views, returns the amount of space that // should be placed between them if they were adjacent. If the first index is @@ -195,16 +121,18 @@ class ChildViewSpacing { int trailing_space_; }; -ChildViewSpacing::ChildViewSpacing(GetViewSpacingCallback get_view_spacing) +FlexLayout::ChildViewSpacing::ChildViewSpacing( + GetViewSpacingCallback get_view_spacing) : get_view_spacing_(std::move(get_view_spacing)), trailing_space_(get_view_spacing_.Run(base::nullopt, base::nullopt)) {} -ChildViewSpacing::ChildViewSpacing(ChildViewSpacing&& other) +FlexLayout::ChildViewSpacing::ChildViewSpacing(ChildViewSpacing&& other) : get_view_spacing_(std::move(other.get_view_spacing_)), leading_spacings_(std::move(other.leading_spacings_)), trailing_space_(other.trailing_space_) {} -ChildViewSpacing& ChildViewSpacing::operator=(ChildViewSpacing&& other) { +FlexLayout::ChildViewSpacing& FlexLayout::ChildViewSpacing::operator=( + ChildViewSpacing&& other) { if (this != &other) { get_view_spacing_ = std::move(other.get_view_spacing_); leading_spacings_ = std::move(other.leading_spacings_); @@ -213,33 +141,33 @@ ChildViewSpacing& ChildViewSpacing::operator=(ChildViewSpacing&& other) { return *this; } -bool ChildViewSpacing::HasViewIndex(size_t view_index) const { +bool FlexLayout::ChildViewSpacing::HasViewIndex(size_t view_index) const { return leading_spacings_.find(view_index) != leading_spacings_.end(); } -int ChildViewSpacing::GetLeadingInset() const { +int FlexLayout::ChildViewSpacing::GetLeadingInset() const { if (leading_spacings_.empty()) return 0; return leading_spacings_.begin()->second; } -int ChildViewSpacing::GetTrailingInset() const { +int FlexLayout::ChildViewSpacing::GetTrailingInset() const { return trailing_space_; } -int ChildViewSpacing::GetLeadingSpace(size_t view_index) const { +int FlexLayout::ChildViewSpacing::GetLeadingSpace(size_t view_index) const { auto it = leading_spacings_.find(view_index); DCHECK(it != leading_spacings_.end()); return it->second; } -int ChildViewSpacing::GetTotalSpace() const { +int FlexLayout::ChildViewSpacing::GetTotalSpace() const { return std::accumulate( leading_spacings_.cbegin(), leading_spacings_.cend(), trailing_space_, [](int total, const auto& value) { return total + value.second; }); } -int ChildViewSpacing::GetAddDelta(size_t view_index) const { +int FlexLayout::ChildViewSpacing::GetAddDelta(size_t view_index) const { DCHECK(!HasViewIndex(view_index)); base::Optional<size_t> prev = GetPreviousViewIndex(view_index); base::Optional<size_t> next = GetNextViewIndex(view_index); @@ -249,9 +177,9 @@ int ChildViewSpacing::GetAddDelta(size_t view_index) const { return new_spacing - old_spacing; } -void ChildViewSpacing::AddViewIndex(size_t view_index, - int* new_leading, - int* new_trailing) { +void FlexLayout::ChildViewSpacing::AddViewIndex(size_t view_index, + int* new_leading, + int* new_trailing) { DCHECK(!HasViewIndex(view_index)); base::Optional<size_t> prev = GetPreviousViewIndex(view_index); base::Optional<size_t> next = GetNextViewIndex(view_index); @@ -270,7 +198,7 @@ void ChildViewSpacing::AddViewIndex(size_t view_index, *new_trailing = trailing_space; } -base::Optional<size_t> ChildViewSpacing::GetPreviousViewIndex( +base::Optional<size_t> FlexLayout::ChildViewSpacing::GetPreviousViewIndex( size_t view_index) const { const auto it = leading_spacings_.lower_bound(view_index); if (it == leading_spacings_.begin()) @@ -278,7 +206,7 @@ base::Optional<size_t> ChildViewSpacing::GetPreviousViewIndex( return std::prev(it)->first; } -base::Optional<size_t> ChildViewSpacing::GetNextViewIndex( +base::Optional<size_t> FlexLayout::ChildViewSpacing::GetNextViewIndex( size_t view_index) const { const auto it = leading_spacings_.upper_bound(view_index); if (it == leading_spacings_.end()) @@ -286,153 +214,210 @@ base::Optional<size_t> ChildViewSpacing::GetNextViewIndex( return it->first; } -// Utility functions ----------------------------------------------------------- +// Represents a specific stored layout given a set of size bounds. +struct FlexLayout::FlexLayoutData { + FlexLayoutData() {} + ~FlexLayoutData() = default; -gfx::Insets GetInternalPadding(const View* view) { - const gfx::Insets* const margins = - view->GetProperty(views::kInternalPaddingKey); - return margins ? *margins : gfx::Insets(); -} + size_t num_children() const { return child_data.size(); } -} // anonymous namespace + ProposedLayout layout; -// Private implementation ------------------------------------------------------ + // Holds additional information about the child views of this layout. + std::vector<FlexChildData> child_data; + + // The total size of the layout (minus parent insets). + NormalizedSize total_size; + NormalizedInsets interior_margin; + NormalizedInsets host_insets; -// Holds child-view-specific layout parameters that are not stored in the -// properties system. -// -// We should consider storing some or all of these in the properites system. -struct ChildLayoutParams { - bool excluded = false; - bool hidden_by_owner = false; - base::Optional<FlexSpecification> flex_specification; + private: + DISALLOW_COPY_AND_ASSIGN(FlexLayoutData); }; -// Internal data structure and functionality for FlexLayout so we don't have to -// declare a bunch of classes and data in the .h file. -class FlexLayoutInternal { - public: - explicit FlexLayoutInternal(FlexLayout* layout) - : layout_(*layout) {} //, cached_layouts_(kMaxCachedLayouts) {} +FlexLayout::PropertyHandler::PropertyHandler(FlexLayout* layout) + : layout_(layout) {} - // Suggests that the current layout needs to be recalculated. Setting |force| - // to true indicates that we know all of the cached layouts are invalid and we - // should discard them; otherwise we will keep them and re-validate on the - // next layout pass. - void InvalidateLayout(bool force); +void FlexLayout::PropertyHandler::AfterPropertyChange(const void* key, + int64_t old_value) { + layout_->InvalidateLayout(); +} - // Gets the proposed layout for a set of size bounds. Returns a cached layout - // if one is present and valid. - const Layout& CalculateLayout(const SizeBounds& bounds); +// FlexLayout +// ------------------------------------------------------------------- - // Applies an existing layout to all child views, with the appropriate - // current alignment. - void DoLayout(const Layout& layout, const gfx::Rect& bounds); +FlexLayout::FlexLayout() {} +FlexLayout::~FlexLayout() = default; - private: - // Maps a flex order (lower = allocated first, and therefore higher priority) - // to the indices of child views within that order that can flex. - // See FlexSpecification::order(). - using FlexOrderToViewIndexMap = std::map<int, std::vector<size_t>>; +FlexLayout& FlexLayout::SetOrientation(LayoutOrientation orientation) { + if (orientation != orientation_) { + orientation_ = orientation; + InvalidateLayout(); + } + return *this; +} - LayoutOrientation orientation() const { return layout_.orientation(); } +FlexLayout& FlexLayout::SetCollapseMargins(bool collapse_margins) { + if (collapse_margins != collapse_margins_) { + collapse_margins_ = collapse_margins; + InvalidateLayout(); + } + return *this; +} - // Determines whether a layout is still valid. - bool IsLayoutValid(const Layout& cached_layout) const; +FlexLayout& FlexLayout::SetMainAxisAlignment( + LayoutAlignment main_axis_alignment) { + DCHECK_NE(main_axis_alignment, LayoutAlignment::kStretch) + << "Main axis stretch/justify is not yet supported."; + if (main_axis_alignment_ != main_axis_alignment) { + main_axis_alignment_ = main_axis_alignment; + InvalidateLayout(); + } + return *this; +} - // Creates a brand new layout from the available |bounds|. - // Call DoLayout() to actually apply the layout. - const Layout& CalculateNewLayout(const NormalizedSizeBounds& bounds); +FlexLayout& FlexLayout::SetCrossAxisAlignment( + LayoutAlignment cross_axis_alignment) { + if (cross_axis_alignment_ != cross_axis_alignment) { + cross_axis_alignment_ = cross_axis_alignment; + InvalidateLayout(); + } + return *this; +} - // Applies flex rules to each view in a layout, updating |layout| and - // |child_spacing|. - // - // If |expandable_views| is specified, any view requesting more than its - // preferred size will be clamped to its preferred size and be added to - // |expandable_views| for later processing after all other flex space has been - // allocated. - // - // Typically, this method will be called once with |expandable_views| set and - // then again with it null to allocate the remaining space. - void AllocateFlexSpace( - Layout* layout, - ChildViewSpacing* child_spacing, - const NormalizedSizeBounds& bounds, - const FlexOrderToViewIndexMap& order_to_index, - FlexOrderToViewIndexMap* expandable_views = nullptr) const; - - // Calculates the position of each child view and the size of the overall - // layout based on tentative visibilities and sizes for each child. - void UpdateLayoutFromChildren(Layout* layout, - ChildViewSpacing* child_spacing, - const NormalizedSizeBounds& bounds) const; - - // Calculates the cross-layout space available to a view based on the - // available space and margins. - base::Optional<int> GetAvailableCrossAxisSize( - const Layout& layout, - size_t child_index, - const NormalizedSizeBounds& bounds) const; - - // Calculates a margin between two child views based on each's margin and any - // internal padding present in one or both elements. Uses properties of the - // layout, like whether adjacent margins should be collapsed. - int CalculateMargin(int margin1, int margin2, int internal_padding) const; - - // Calculates the preferred spacing between two child views, or between a - // view edge and the first or last visible child views. - int CalculateChildSpacing(const Layout& layout, - base::Optional<size_t> child1_index, - base::Optional<size_t> child2_index) const; - - const gfx::Insets& GetMargins(const View* view) const; - - FlexLayout& layout_; - - // Instead of marking each layout as dirty/needing validation, we instead keep - // a value that changes every time InvalidateLayout() is called. If the value - // stored in a cached layout doesn't match this value, we validate it and - // update the value. We use an int64_t because the odds of spurious collision - // (i.e. the counter wrapping back around to the *exact* same value before - // validating) are effectively zero. - uint32_t layout_counter_ = 0; - LayoutCache layout_cache_; - - DISALLOW_COPY_AND_ASSIGN(FlexLayoutInternal); -}; +FlexLayout& FlexLayout::SetInteriorMargin(const gfx::Insets& interior_margin) { + if (interior_margin_ != interior_margin) { + interior_margin_ = interior_margin; + InvalidateLayout(); + } + return *this; +} -void FlexLayoutInternal::InvalidateLayout(bool force) { - ++layout_counter_; - if (force) - layout_cache_.Clear(); +FlexLayout& FlexLayout::SetMinimumCrossAxisSize(int size) { + if (minimum_cross_axis_size_ != size) { + minimum_cross_axis_size_ = size; + InvalidateLayout(); + } + return *this; } -const Layout& FlexLayoutInternal::CalculateLayout(const SizeBounds& bounds) { - // If bounds are smaller than the minimum cross axis size, expand them. - NormalizedSizeBounds normalized_bounds = Normalize(orientation(), bounds); - if (normalized_bounds.cross() && - *normalized_bounds.cross() < layout_.minimum_cross_axis_size()) { - normalized_bounds = NormalizedSizeBounds{normalized_bounds.main(), - layout_.minimum_cross_axis_size()}; +LayoutManagerBase::ProposedLayout FlexLayout::CalculateProposedLayout( + const SizeBounds& size_bounds) const { + FlexLayoutData data; + + data.host_insets = Normalize(orientation(), host_view()->GetInsets()); + data.interior_margin = Normalize(orientation(), interior_margin()); + NormalizedSizeBounds bounds = Normalize(orientation(), size_bounds); + bounds.Inset(data.host_insets); + if (bounds.cross() && *bounds.cross() < minimum_cross_axis_size()) + bounds.set_cross(minimum_cross_axis_size()); + + // Populate the child layout data vectors and the order-to-index map. + FlexOrderToViewIndexMap order_to_view_index; + InitializeChildData(bounds, &data, &order_to_view_index); + + // Do the initial layout update, calculating spacing between children. + ChildViewSpacing child_spacing( + base::BindRepeating(&FlexLayout::CalculateChildSpacing, + base::Unretained(this), std::cref(data))); + UpdateLayoutFromChildren(bounds, &data, &child_spacing); + + if (bounds.main().has_value() && !order_to_view_index.empty()) { + // Flex up to preferred size. + FlexOrderToViewIndexMap expandable_views; + AllocateFlexSpace(bounds, order_to_view_index, &data, &child_spacing, + &expandable_views); + + // Flex views that can exceed their preferred size. + if (!expandable_views.empty()) + AllocateFlexSpace(bounds, expandable_views, &data, &child_spacing); } - // See if we have a cached layout already that is still valid. - Layout* const cached_layout = layout_cache_.Get(normalized_bounds); - if (cached_layout && IsLayoutValid(*cached_layout)) - return *cached_layout; + // Calculate the size of the host view. + NormalizedSize host_size = data.total_size; + host_size.Enlarge(data.host_insets.main_size(), + data.host_insets.cross_size()); + data.layout.host_size = Denormalize(orientation(), host_size); + + // Size and position the children in screen space. + CalculateChildBounds(size_bounds, &data); - // Calculate a new layout from scratch. - return CalculateNewLayout(normalized_bounds); + return data.layout; } -void FlexLayoutInternal::DoLayout(const Layout& layout, - const gfx::Rect& bounds) { - NormalizedPoint start = Normalize(orientation(), bounds.origin()); +void FlexLayout::InitializeChildData( + const internal::NormalizedSizeBounds& bounds, + FlexLayoutData* data, + FlexOrderToViewIndexMap* flex_order_to_index) const { + // Step through the children, creating placeholder layout view elements + // and setting up initial minimal visibility. + const bool main_axis_bounded = bounds.main().has_value(); + for (auto it = host_view()->children().begin(); + it != host_view()->children().end(); ++it) { + View* const child = *it; + if (!IsChildIncludedInLayout(child)) + continue; + const size_t view_index = data->num_children(); + data->layout.child_layouts.emplace_back(ChildLayout{child}); + ChildLayout& child_layout = data->layout.child_layouts.back(); + data->child_data.emplace_back( + GetViewProperty(child, layout_defaults_, views::kFlexBehaviorKey)); + FlexChildData& flex_child = data->child_data.back(); + + flex_child.margins = + Normalize(orientation(), + GetViewProperty(child, layout_defaults_, views::kMarginsKey)); + flex_child.internal_padding = Normalize( + orientation(), + GetViewProperty(child, layout_defaults_, views::kInternalPaddingKey)); + flex_child.preferred_size = + Normalize(orientation(), child->GetPreferredSize()); + + // gfx::Size calculation depends on whether flex is allowed. + if (main_axis_bounded) { + flex_child.available_size = { + 0, GetAvailableCrossAxisSize(*data, view_index, bounds)}; + flex_child.current_size = Normalize( + orientation(), + flex_child.flex.rule().Run( + child, Denormalize(orientation(), flex_child.available_size))); - // Apply main axis alignment. - const int excess_main = - Normalize(orientation(), bounds.size()).main() - layout.total_size.main(); - switch (layout_.main_axis_alignment()) { + // We should revisit whether this is a valid assumption for text views + // in vertical layouts. + DCHECK_GE(flex_child.preferred_size.main(), + flex_child.current_size.main()); + + // Keep track of non-hidden flex controls. + const bool can_flex = + (flex_child.flex.weight() > 0 && + flex_child.preferred_size.main() > 0) || + flex_child.current_size.main() < flex_child.preferred_size.main(); + if (can_flex) + (*flex_order_to_index)[flex_child.flex.order()].push_back(view_index); + + } else { + // All non-flex or unbounded controls get preferred size. + flex_child.current_size = flex_child.preferred_size; + } + + child_layout.visible = flex_child.current_size.main() > 0; + } +} + +void FlexLayout::CalculateChildBounds(const SizeBounds& size_bounds, + FlexLayoutData* data) const { + // Apply main axis alignment (we've already done cross-axis alignment above). + const NormalizedSizeBounds normalized_bounds = + Normalize(orientation(), size_bounds); + const NormalizedSize normalized_host_size = + Normalize(orientation(), data->layout.host_size); + int available_main = (normalized_bounds.main() ? *normalized_bounds.main() + : normalized_host_size.main()); + available_main = std::max(0, available_main - data->host_insets.main_size()); + const int excess_main = available_main - data->total_size.main(); + NormalizedPoint start(data->host_insets.main_leading(), + data->host_insets.cross_leading()); + switch (main_axis_alignment()) { case LayoutAlignment::kStart: break; case LayoutAlignment::kCenter: @@ -446,29 +431,34 @@ void FlexLayoutInternal::DoLayout(const Layout& layout, break; } - // Position controls within the client area. - for (const ChildLayout& child_layout : layout.child_layouts) { - if (child_layout.excluded) - continue; - if (child_layout.visible != child_layout.view->GetVisible()) - layout_.SetViewVisibility(child_layout.view, child_layout.visible); + // Calculate the actual child bounds. + for (size_t i = 0; i < data->num_children(); ++i) { + ChildLayout& child_layout = data->layout.child_layouts[i]; if (child_layout.visible) { - NormalizedRect actual = child_layout.actual_bounds; + FlexChildData& flex_child = data->child_data[i]; + NormalizedRect actual = flex_child.actual_bounds; actual.Offset(start.main(), start.cross()); - gfx::Rect denormalized = Denormalize(orientation(), actual); - child_layout.view->SetBoundsRect(denormalized); + child_layout.bounds = Denormalize(orientation(), actual); } } } -base::Optional<int> FlexLayoutInternal::GetAvailableCrossAxisSize( - const Layout& layout, +int FlexLayout::CalculateMargin(int margin1, + int margin2, + int internal_padding) const { + const int result = + collapse_margins() ? std::max(margin1, margin2) : margin1 + margin2; + return std::max(0, result - internal_padding); +} + +base::Optional<int> FlexLayout::GetAvailableCrossAxisSize( + const FlexLayoutData& layout, size_t child_index, const NormalizedSizeBounds& bounds) const { if (!bounds.cross()) return base::nullopt; - const ChildLayout& child_layout = layout.child_layouts[child_index]; + const FlexChildData& child_layout = layout.child_data[child_index]; const int leading_margin = CalculateMargin(layout.interior_margin.cross_leading(), child_layout.margins.cross_leading(), @@ -480,84 +470,71 @@ base::Optional<int> FlexLayoutInternal::GetAvailableCrossAxisSize( return std::max(0, *bounds.cross() - (leading_margin + trailing_margin)); } -int FlexLayoutInternal::CalculateMargin(int margin1, - int margin2, - int internal_padding) const { - const int result = layout_.collapse_margins() ? std::max(margin1, margin2) - : margin1 + margin2; - return std::max(0, result - internal_padding); -} - -int FlexLayoutInternal::CalculateChildSpacing( - const Layout& layout, +int FlexLayout::CalculateChildSpacing( + const FlexLayoutData& layout, base::Optional<size_t> child1_index, base::Optional<size_t> child2_index) const { const int left_margin = - child1_index ? layout.child_layouts[*child1_index].margins.main_trailing() + child1_index ? layout.child_data[*child1_index].margins.main_trailing() : layout.interior_margin.main_leading(); const int right_margin = - child2_index ? layout.child_layouts[*child2_index].margins.main_leading() + child2_index ? layout.child_data[*child2_index].margins.main_leading() : layout.interior_margin.main_trailing(); const int left_padding = child1_index - ? layout.child_layouts[*child1_index].internal_padding.main_trailing() + ? layout.child_data[*child1_index].internal_padding.main_trailing() : 0; const int right_padding = child2_index - ? layout.child_layouts[*child2_index].internal_padding.main_leading() + ? layout.child_data[*child2_index].internal_padding.main_leading() : 0; return CalculateMargin(left_margin, right_margin, left_padding + right_padding); } -const gfx::Insets& FlexLayoutInternal::GetMargins(const View* view) const { - const gfx::Insets* const margins = view->GetProperty(views::kMarginsKey); - return margins ? *margins : layout_.default_child_margins(); -} - -void FlexLayoutInternal::UpdateLayoutFromChildren( - Layout* layout, - ChildViewSpacing* child_spacing, - const NormalizedSizeBounds& bounds) const { +void FlexLayout::UpdateLayoutFromChildren( + const NormalizedSizeBounds& bounds, + FlexLayoutData* data, + ChildViewSpacing* child_spacing) const { // Calculate starting minimum for cross-axis size. int min_cross_size = - std::max(layout_.minimum_cross_axis_size(), - CalculateMargin(layout->interior_margin.cross_leading(), - layout->interior_margin.cross_trailing(), 0)); - layout->total_size = NormalizedSize(0, min_cross_size); + std::max(minimum_cross_axis_size(), + CalculateMargin(data->interior_margin.cross_leading(), + data->interior_margin.cross_trailing(), 0)); + data->total_size = NormalizedSize(0, min_cross_size); // For cases with a non-zero cross-axis bound, the objective is to fit the // layout into that precise size, not to determine what size we need. bool force_cross_size = false; if (bounds.cross() && *bounds.cross() > 0) { - layout->total_size.SetToMax(0, *bounds.cross()); + data->total_size.SetToMax(0, *bounds.cross()); force_cross_size = true; } - std::vector<Inset1D> cross_spacings(layout->child_layouts.size()); - for (size_t i = 0; i < layout->child_layouts.size(); ++i) { - ChildLayout& child_layout = layout->child_layouts[i]; + std::vector<Inset1D> cross_spacings(data->num_children()); + for (size_t i = 0; i < data->num_children(); ++i) { + FlexChildData& flex_child = data->child_data[i]; - // We don't have to deal with excluded or invisible children. - if (child_layout.excluded || !child_layout.visible) + // We don't have to deal with invisible children. + if (!data->layout.child_layouts[i].visible) continue; // Update the cross-axis margins and if necessary, the size. Inset1D& cross_spacing = cross_spacings[i]; cross_spacing.set_leading( - CalculateMargin(layout->interior_margin.cross_leading(), - child_layout.margins.cross_leading(), - child_layout.internal_padding.cross_leading())); + CalculateMargin(data->interior_margin.cross_leading(), + flex_child.margins.cross_leading(), + flex_child.internal_padding.cross_leading())); cross_spacing.set_trailing( - CalculateMargin(layout->interior_margin.cross_trailing(), - child_layout.margins.cross_trailing(), - child_layout.internal_padding.cross_trailing())); + CalculateMargin(data->interior_margin.cross_trailing(), + flex_child.margins.cross_trailing(), + flex_child.internal_padding.cross_trailing())); if (!force_cross_size) { - const int cross_size = std::min(child_layout.current_size.cross(), - child_layout.preferred_size.cross()); - layout->total_size.SetToMax(0, cross_spacing.size() + cross_size); + const int cross_size = std::min(flex_child.current_size.cross(), + flex_child.preferred_size.cross()); + data->total_size.SetToMax(0, cross_spacing.size() + cross_size); } // Calculate main-axis size and upper-left main axis coordinate. @@ -566,50 +543,46 @@ void FlexLayoutInternal::UpdateLayoutFromChildren( leading_space = child_spacing->GetLeadingSpace(i); else child_spacing->AddViewIndex(i, &leading_space); - layout->total_size.Enlarge(leading_space, 0); + data->total_size.Enlarge(leading_space, 0); - const int size_main = child_layout.current_size.main(); - child_layout.actual_bounds.set_origin_main(layout->total_size.main()); - child_layout.actual_bounds.set_size_main(size_main); - layout->total_size.Enlarge(size_main, 0); + const int size_main = flex_child.current_size.main(); + flex_child.actual_bounds.set_origin_main(data->total_size.main()); + flex_child.actual_bounds.set_size_main(size_main); + data->total_size.Enlarge(size_main, 0); } // Add the end margin. - layout->total_size.Enlarge(child_spacing->GetTrailingInset(), 0); + data->total_size.Enlarge(child_spacing->GetTrailingInset(), 0); // Calculate cross-axis positioning based on the cross margins and size that // were calculated above. - const Span cross_span(0, layout->total_size.cross()); - for (size_t i = 0; i < layout->child_layouts.size(); ++i) { - ChildLayout& child_layout = layout->child_layouts[i]; - - // We don't have to deal with excluded or invisible children. - if (child_layout.excluded || !child_layout.visible) - continue; + const Span cross_span(0, data->total_size.cross()); + for (size_t i = 0; i < data->num_children(); ++i) { + FlexChildData& flex_child = data->child_data[i]; // Start with a size appropriate for the child view. For child views which // can become larger than the preferred size, start with the preferred size // and let the alignment operation (specifically, if the alignment is set to // kStretch) grow the child view. - const int starting_cross_size = std::min( - child_layout.current_size.cross(), child_layout.preferred_size.cross()); - child_layout.actual_bounds.set_size_cross(starting_cross_size); - child_layout.actual_bounds.AlignCross( - cross_span, layout_.cross_axis_alignment(), cross_spacings[i]); + const int starting_cross_size = std::min(flex_child.current_size.cross(), + flex_child.preferred_size.cross()); + flex_child.actual_bounds.set_size_cross(starting_cross_size); + flex_child.actual_bounds.AlignCross(cross_span, cross_axis_alignment(), + cross_spacings[i]); } } -void FlexLayoutInternal::AllocateFlexSpace( - Layout* layout, - ChildViewSpacing* child_spacing, +void FlexLayout::AllocateFlexSpace( const NormalizedSizeBounds& bounds, const FlexOrderToViewIndexMap& order_to_index, + FlexLayoutData* data, + ChildViewSpacing* child_spacing, FlexOrderToViewIndexMap* expandable_views) const { // Step through each flex priority allocating as much remaining space as // possible to each flex view. for (const auto& flex_elem : order_to_index) { // Check to see we haven't filled available space. - int remaining = *bounds.main() - layout->total_size.main(); + int remaining = *bounds.main() - data->total_size.main(); if (remaining <= 0) break; const int flex_order = flex_elem.first; @@ -639,11 +612,11 @@ void FlexLayoutInternal::AllocateFlexSpace( // reasonable margins and by using flex order). // Flex children at this priority order. - int flex_total = std::accumulate( - flex_elem.second.begin(), flex_elem.second.end(), 0, - [layout](int total, size_t index) { - return total + layout->child_layouts[index].flex.weight(); - }); + int flex_total = + std::accumulate(flex_elem.second.begin(), flex_elem.second.end(), 0, + [data](int total, size_t index) { + return total + data->child_data[index].flex.weight(); + }); // Note: because the child views are evaluated in order, if preferred // minimum sizes are not consistent across a single priority expanding @@ -655,12 +628,13 @@ void FlexLayoutInternal::AllocateFlexSpace( remaining >= 0 && index_it != flex_elem.second.end(); ++index_it) { const size_t view_index = *index_it; - ChildLayout& child_layout = layout->child_layouts[view_index]; + ChildLayout& child_layout = data->layout.child_layouts[view_index]; + FlexChildData& flex_child = data->child_data[view_index]; // Offer a share of the remaining space to the view. int flex_amount; - if (child_layout.flex.weight() > 0) { - const int flex_weight = child_layout.flex.weight(); + if (flex_child.flex.weight() > 0) { + const int flex_weight = flex_child.flex.weight(); // Round up so we give slightly greater weight to earlier views. flex_amount = int{std::ceil((float{remaining} * flex_weight) / flex_total)}; @@ -690,17 +664,17 @@ void FlexLayoutInternal::AllocateFlexSpace( // view since it is considered a fixed overhead of the layout if it is // nonzero. const int old_size = - child_layout.visible ? child_layout.current_size.main() : 0; + child_layout.visible ? flex_child.current_size.main() : 0; // Offer the modified flex space to the child view and see how large it // wants to be (or if it wants to be visible at that size at all). const NormalizedSizeBounds available( flex_amount + old_size - margin_delta, - child_layout.available_size.cross()); + flex_child.available_size.cross()); NormalizedSize new_size = Normalize( orientation(), - child_layout.flex.rule().Run(child_layout.view, - Denormalize(orientation(), available))); + flex_child.flex.rule().Run(child_layout.child_view, + Denormalize(orientation(), available))); if (new_size.main() <= 0) continue; @@ -709,9 +683,9 @@ void FlexLayoutInternal::AllocateFlexSpace( // them to |expandable_views| so that the remaining space can be allocated // later. if (expandable_views && - new_size.main() >= child_layout.preferred_size.main()) { + new_size.main() >= flex_child.preferred_size.main()) { (*expandable_views)[flex_order].push_back(view_index); - new_size.set_main(child_layout.preferred_size.main()); + new_size.set_main(flex_child.preferred_size.main()); } // If the amount of space claimed increases (but is still within @@ -720,8 +694,8 @@ void FlexLayoutInternal::AllocateFlexSpace( const int to_deduct = (new_size.main() - old_size) + margin_delta; DCHECK_GE(to_deduct, 0); if (to_deduct > 0 && to_deduct <= remaining) { - child_layout.available_size = available; - child_layout.current_size = new_size; + flex_child.available_size = available; + flex_child.current_size = new_size; child_layout.visible = true; remaining -= to_deduct; if (!child_spacing->HasViewIndex(view_index)) @@ -733,380 +707,8 @@ void FlexLayoutInternal::AllocateFlexSpace( // Reposition the child controls (taking margins into account) and // calculate remaining space. if (dirty) - UpdateLayoutFromChildren(layout, child_spacing, bounds); - } -} - -const Layout& FlexLayoutInternal::CalculateNewLayout( - const NormalizedSizeBounds& bounds) { - DCHECK(!bounds.cross() || - *bounds.cross() >= layout_.minimum_cross_axis_size()); - std::unique_ptr<Layout> layout = std::make_unique<Layout>(layout_counter_); - layout->interior_margin = Normalize(orientation(), layout_.interior_margin()); - FlexOrderToViewIndexMap order_to_view_index; - const bool main_axis_bounded = bounds.main().has_value(); - - // Step through the children, creating placeholder layout view elements - // and setting up initial minimal visibility. - View* const view = layout_.host(); - for (size_t i = 0; i < view->children().size(); ++i) { - View* child = view->children()[i]; - layout->child_layouts.emplace_back(child, layout_.GetFlexForView(child)); - ChildLayout& child_layout = layout->child_layouts.back(); - - child_layout.excluded = layout_.IsViewExcluded(child); - if (child_layout.excluded) - continue; - - child_layout.margins = Normalize(orientation(), GetMargins(child)); - child_layout.internal_padding = - Normalize(orientation(), GetInternalPadding(child)); - child_layout.preferred_size = - Normalize(orientation(), child->GetPreferredSize()); - - child_layout.hidden_by_owner = layout_.IsHiddenByOwner(child); - child_layout.visible = !child_layout.hidden_by_owner; - if (child_layout.hidden_by_owner) - continue; - - // gfx::Size calculation depends on whether flex is allowed. - if (main_axis_bounded) { - child_layout.available_size = { - 0, GetAvailableCrossAxisSize(*layout, i, bounds)}; - child_layout.current_size = Normalize( - orientation(), - child_layout.flex.rule().Run( - child, Denormalize(orientation(), child_layout.available_size))); - - // We should revisit whether this is a valid assumption for text views - // in vertical layouts. - DCHECK_GE(child_layout.preferred_size.main(), - child_layout.current_size.main()); - - // Keep track of non-hidden flex controls. - const bool can_flex = - (child_layout.flex.weight() > 0 && - child_layout.preferred_size.main() > 0) || - child_layout.current_size.main() < child_layout.preferred_size.main(); - if (can_flex) - order_to_view_index[child_layout.flex.order()].push_back(i); - - } else { - // All non-flex or unbounded controls get preferred size. - child_layout.current_size = child_layout.preferred_size; - } - - child_layout.visible = child_layout.current_size.main() > 0; - - // Actual size and positioning will be set during the final layout - // calculation. - } - - // Do the initial layout update, calculating spacing between children. - ChildViewSpacing child_spacing( - base::BindRepeating(&FlexLayoutInternal::CalculateChildSpacing, - base::Unretained(this), std::cref(*layout))); - UpdateLayoutFromChildren(layout.get(), &child_spacing, bounds); - - if (main_axis_bounded && !order_to_view_index.empty()) { - // Flex up to preferred size. - FlexOrderToViewIndexMap expandable_views; - AllocateFlexSpace(layout.get(), &child_spacing, bounds, order_to_view_index, - &expandable_views); - - // Flex views that can exceed their preferred size. - if (!expandable_views.empty()) - AllocateFlexSpace(layout.get(), &child_spacing, bounds, expandable_views); - } - - const Layout& result = *layout; - layout_cache_.Put(bounds, std::move(layout)); - return result; -} - -bool FlexLayoutInternal::IsLayoutValid(const Layout& cached_layout) const { - // If we've already evaluated a layout for this size and not been - // invalidated since then, then this is a valid layout. - if (cached_layout.layout_id == layout_counter_) - return true; - - // Need to compare preferred child sizes with what we're seeing. - View* const view = layout_.host(); - auto iter = view->children().cbegin(); - for (const ChildLayout& proposed_view_layout : cached_layout.child_layouts) { - // Check that there is another child and that it's the view we expect. - DCHECK(iter != view->children().cend()) - << "Child views should not be removed without clearing the cache."; - - const View* child = *iter++; - - // Ensure child views have not been reordered. - if (child != proposed_view_layout.view) - return false; - - // Ignore hidden and excluded views, unless their status has changed. - const bool excluded = layout_.IsViewExcluded(child); - if (proposed_view_layout.excluded != excluded) - return false; - if (excluded) - continue; - - const bool hidden_by_owner = layout_.IsHiddenByOwner(child); - if (proposed_view_layout.hidden_by_owner != hidden_by_owner) - return false; - if (hidden_by_owner) - continue; - - // Sanity check that a child's visibility hasn't been modified outside - // the layout manager. - if (proposed_view_layout.visible != child->GetVisible()) - return false; - - if (proposed_view_layout.visible) { - // Check that view margins haven't changed for visible controls. - if (GetMargins(child) != - Denormalize(orientation(), proposed_view_layout.margins)) - return false; - - // Same for internal padding. - if (GetInternalPadding(child) != - Denormalize(orientation(), proposed_view_layout.internal_padding)) - return false; - } - - // Check that the control still has the same preferred size given its - // flex and visibility. - if (child->GetPreferredSize() != - Denormalize(orientation(), proposed_view_layout.preferred_size)) - return false; - - const gfx::Size preferred = proposed_view_layout.flex.rule().Run( - child, Denormalize(orientation(), proposed_view_layout.available_size)); - if (preferred != - Denormalize(orientation(), proposed_view_layout.current_size)) - return false; - } - - DCHECK(iter == view->children().cend()) - << "Child views should not be added without clearing the cache."; - - // This layout is still valid. Update the layout counter to show it's valid - // in the current layout context. - cached_layout.layout_id = layout_counter_; - return true; -} - -} // namespace internal - -// FlexLayout -// ------------------------------------------------------------------- - -FlexLayout::FlexLayout() - : internal_(std::make_unique<internal::FlexLayoutInternal>(this)) {} - -FlexLayout::~FlexLayout() = default; - -FlexLayout& FlexLayout::SetOrientation(LayoutOrientation orientation) { - if (orientation != orientation_) { - orientation_ = orientation; - internal_->InvalidateLayout(true); - } - return *this; -} - -FlexLayout& FlexLayout::SetCollapseMargins(bool collapse_margins) { - if (collapse_margins != collapse_margins_) { - collapse_margins_ = collapse_margins; - internal_->InvalidateLayout(true); - } - return *this; -} - -FlexLayout& FlexLayout::SetMainAxisAlignment( - LayoutAlignment main_axis_alignment) { - DCHECK_NE(main_axis_alignment, LayoutAlignment::kStretch) - << "Main axis stretch/justify is not yet supported."; - if (main_axis_alignment_ != main_axis_alignment) { - main_axis_alignment_ = main_axis_alignment; - internal_->InvalidateLayout(true); - } - return *this; -} - -FlexLayout& FlexLayout::SetCrossAxisAlignment( - LayoutAlignment cross_axis_alignment) { - if (cross_axis_alignment_ != cross_axis_alignment) { - cross_axis_alignment_ = cross_axis_alignment; - internal_->InvalidateLayout(true); + UpdateLayoutFromChildren(bounds, data, child_spacing); } - return *this; -} - -FlexLayout& FlexLayout::SetInteriorMargin(const gfx::Insets& interior_margin) { - if (interior_margin_ != interior_margin) { - interior_margin_ = interior_margin; - internal_->InvalidateLayout(true); - } - return *this; -} - -FlexLayout& FlexLayout::SetMinimumCrossAxisSize(int size) { - if (minimum_cross_axis_size_ != size) { - minimum_cross_axis_size_ = size; - internal_->InvalidateLayout(true); - } - return *this; -} - -FlexLayout& FlexLayout::SetDefaultChildMargins(const gfx::Insets& margins) { - if (default_child_margins_ != margins) { - default_child_margins_ = margins; - internal_->InvalidateLayout(true); - } - return *this; -} - -FlexLayout& FlexLayout::SetFlexForView( - const View* view, - const FlexSpecification& flex_specification) { - auto it = child_params_.find(view); - DCHECK(it != child_params_.end()); - it->second.flex_specification = flex_specification; - internal_->InvalidateLayout(true); - return *this; -} - -FlexLayout& FlexLayout::ClearFlexForView(const View* view) { - auto it = child_params_.find(view); - DCHECK(it != child_params_.end()); - it->second.flex_specification.reset(); - internal_->InvalidateLayout(true); - return *this; -} - -FlexLayout& FlexLayout::SetViewExcluded(const View* view, bool excluded) { - auto it = child_params_.find(view); - DCHECK(it != child_params_.end()); - if (excluded != it->second.excluded) { - it->second.excluded = excluded; - InvalidateLayout(); - } - return *this; -} - -FlexLayout& FlexLayout::SetDefaultFlex( - const FlexSpecification& flex_specification) { - default_flex_ = flex_specification; - internal_->InvalidateLayout(true); - return *this; -} - -const FlexSpecification& FlexLayout::GetFlexForView(const View* view) const { - auto it = child_params_.find(view); - DCHECK(it != child_params_.end()); - const base::Optional<FlexSpecification>& spec = it->second.flex_specification; - return spec ? *spec : default_flex_; -} - -bool FlexLayout::IsViewExcluded(const View* view) const { - auto it = child_params_.find(view); - DCHECK(it != child_params_.end()); - return it->second.excluded; -} - -bool FlexLayout::IsHiddenByOwner(const View* view) const { - auto it = child_params_.find(view); - DCHECK(it != child_params_.end()); - return it->second.hidden_by_owner; -} - -gfx::Size FlexLayout::GetPreferredSize(const View* host) const { - DCHECK_EQ(host_, host); - return GetPreferredSize(SizeBounds()); -} - -int FlexLayout::GetPreferredHeightForWidth(const View* host, int width) const { - DCHECK_EQ(host_, host); - return GetPreferredSize(SizeBounds{width, base::nullopt}).height(); -} - -gfx::Size FlexLayout::GetMinimumSize(const View* host) const { - DCHECK_EQ(host_, host); - return GetPreferredSize(SizeBounds{0, 0}); -} - -void FlexLayout::InvalidateLayout() { - internal_->InvalidateLayout(false); -} - -void FlexLayout::Installed(View* host) { - DCHECK(!host_); - host_ = host; - // Add all the existing children when the layout manager is installed. - // If new children are added, ViewAdded() will be called and we'll add data - // there. - for (View* child : host->children()) { - internal::ChildLayoutParams child_layout_params; - child_layout_params.hidden_by_owner = !child->GetVisible(); - child_params_.emplace(child, child_layout_params); - } -} - -void FlexLayout::ViewAdded(View* host, View* view) { - DCHECK_EQ(host_, host); - internal::ChildLayoutParams child_layout_params; - child_layout_params.hidden_by_owner = !view->GetVisible(); - child_params_.emplace(view, child_layout_params); - internal_->InvalidateLayout(true); -} - -void FlexLayout::ViewRemoved(View* host, View* view) { - child_params_.erase(view); - internal_->InvalidateLayout(true); -} - -void FlexLayout::ViewVisibilitySet(View* host, View* view, bool visible) { - DCHECK_EQ(host, host_); - DCHECK_EQ(view->parent(), host); - auto it = child_params_.find(view); - DCHECK(it != child_params_.end()); - const bool hide = !visible; - if (it->second.hidden_by_owner != hide) { - it->second.hidden_by_owner = hide; - if (!it->second.excluded) { - // It's not always obvious to our host that an owner of a child view - // changing its visibility could change a layout. So we'll notify the host - // view that its layout is potentially invalid, and it will in turn call - // InvalidateLayout() on the layout manager (i.e. this object). - host_->InvalidateLayout(); - } - } -} - -// Retrieve the preferred size for the control in the given bounds. -gfx::Size FlexLayout::GetPreferredSize(const SizeBounds& bounds) const { - if (!host_) - return gfx::Size(); - - const gfx::Insets insets = host_->GetInsets(); - SizeBounds content_bounds = bounds; - content_bounds.Enlarge(-insets.width(), -insets.height()); - const internal::Layout& proposed_layout = - internal_->CalculateLayout(content_bounds); - gfx::Size result = Denormalize(orientation(), proposed_layout.total_size); - result.Enlarge(insets.width(), insets.height()); - return result; -} - -void FlexLayout::Layout(View* host) { - DCHECK_EQ(host, host_); - // TODO(dfried): for a handful of views which override GetInsets(), the way - // this method is implemented in View may be incorrect. Please revisit. - gfx::Rect bounds = host_->GetContentsBounds(); - const internal::Layout& layout = - internal_->CalculateLayout(SizeBounds(bounds.size())); - - internal_->DoLayout(layout, bounds); } } // namespace views diff --git a/chromium/ui/views/layout/flex_layout.h b/chromium/ui/views/layout/flex_layout.h index 45c518133f0..5ca0ab23ae1 100644 --- a/chromium/ui/views/layout/flex_layout.h +++ b/chromium/ui/views/layout/flex_layout.h @@ -9,27 +9,25 @@ #include <memory> #include <set> #include <string> +#include <vector> #include "base/compiler_specific.h" #include "base/macros.h" #include "base/optional.h" +#include "ui/base/class_property.h" #include "ui/gfx/geometry/insets.h" #include "ui/views/layout/flex_layout_types.h" -#include "ui/views/layout/layout_manager.h" - -namespace gfx { -class Size; -} // namespace gfx +#include "ui/views/layout/layout_manager_base.h" +#include "ui/views/views_export.h" namespace views { -namespace internal { -struct ChildLayoutParams; -class FlexLayoutInternal; -} // namespace internal - class View; +namespace internal { +class NormalizedSizeBounds; +} + // Provides CSS-like layout for a one-dimensional (vertical or horizontal) // arrangement of child views. Independent alignment can be specified for the // main and cross axes. @@ -69,11 +67,11 @@ class View; // take up its preferred size in the layout. // // The core function of this class is contained in -// GetPreferredSize(maximum_size) and Layout(host). In both cases, a layout will +// GetPreferredSize(maximum_size) and Layout(). In both cases, a layout will // be cached and typically not recalculated as long as none of the layout's // properties or the preferred size or visibility of any of its children has // changed. -class VIEWS_EXPORT FlexLayout : public LayoutManager { +class VIEWS_EXPORT FlexLayout : public LayoutManagerBase { public: FlexLayout(); ~FlexLayout() override; @@ -88,50 +86,124 @@ class VIEWS_EXPORT FlexLayout : public LayoutManager { FlexLayout& SetCrossAxisAlignment(LayoutAlignment cross_axis_alignment); FlexLayout& SetInteriorMargin(const gfx::Insets& interior_margin); FlexLayout& SetMinimumCrossAxisSize(int size); - FlexLayout& SetDefaultChildMargins(const gfx::Insets& margins); - FlexLayout& SetFlexForView(const View* view, - const FlexSpecification& flex_specification); - FlexLayout& ClearFlexForView(const View* view); - FlexLayout& SetDefaultFlex(const FlexSpecification& flex_specification); - - // Set whether a view should be excluded from the layout. Excluded views will - // be completely ignored and must be explicitly placed by the host view. - FlexLayout& SetViewExcluded(const View* view, bool excluded); - - View* host() { return host_; } - const View* host() const { return host_; } + LayoutOrientation orientation() const { return orientation_; } bool collapse_margins() const { return collapse_margins_; } LayoutAlignment main_axis_alignment() const { return main_axis_alignment_; } LayoutAlignment cross_axis_alignment() const { return cross_axis_alignment_; } const gfx::Insets& interior_margin() const { return interior_margin_; } int minimum_cross_axis_size() const { return minimum_cross_axis_size_; } - const gfx::Insets& default_child_margins() const { - return default_child_margins_; - } - const FlexSpecification& default_flex() const { return default_flex_; } - const FlexSpecification& GetFlexForView(const View* view) const; - bool IsViewExcluded(const View* view) const; - bool IsHiddenByOwner(const View* view) const; + // Moves and uses |value| as the default value for layout property |key|. + template <class T, class U> + FlexLayout& SetDefault(const ui::ClassProperty<T>* key, U&& value) { + layout_defaults_.SetProperty(key, std::forward<U>(value)); + return *this; + } - // Retrieve the preferred size for the control in the given bounds. - gfx::Size GetPreferredSize(const SizeBounds& maximum_size) const; + // Copies and uses |value| as the default value for layout property |key|. + template <class T, class U> + FlexLayout& SetDefault(const ui::ClassProperty<T>* key, const U& value) { + layout_defaults_.SetProperty(key, value); + return *this; + } protected: - // views::LayoutManager: - void Installed(View* host) override; - void InvalidateLayout() override; - void ViewAdded(View* host, View* view) override; - void ViewRemoved(View* host, View* view) override; - void ViewVisibilitySet(View* host, View* view, bool visible) override; - void Layout(View* host) override; - gfx::Size GetPreferredSize(const View* host) const override; - gfx::Size GetMinimumSize(const View* host) const override; - int GetPreferredHeightForWidth(const View* host, int width) const override; + // LayoutManagerBase: + ProposedLayout CalculateProposedLayout( + const SizeBounds& size_bounds) const override; private: - friend class internal::FlexLayoutInternal; + struct ChildLayoutParams; + class ChildViewSpacing; + struct FlexLayoutData; + + class PropertyHandler : public ui::PropertyHandler { + public: + explicit PropertyHandler(FlexLayout* layout); + + protected: + // ui::PropertyHandler: + void AfterPropertyChange(const void* key, int64_t old_value) override; + + private: + FlexLayout* const layout_; + }; + + // Maps a flex order (lower = allocated first, and therefore higher priority) + // to the indices of child views within that order that can flex. + // See FlexSpecification::order(). + using FlexOrderToViewIndexMap = std::map<int, std::vector<size_t>>; + + // Calculates a margin between two child views based on each's margin and any + // internal padding present in one or both elements. Uses properties of the + // layout, like whether adjacent margins should be collapsed. + int CalculateMargin(int margin1, int margin2, int internal_padding) const; + + // Calculates the cross-layout space available to a view based on the + // available space and margins. + base::Optional<int> GetAvailableCrossAxisSize( + const FlexLayoutData& layout, + size_t child_index, + const internal::NormalizedSizeBounds& bounds) const; + + // Calculates the preferred spacing between two child views, or between a + // view edge and the first or last visible child views. + int CalculateChildSpacing(const FlexLayoutData& layout, + base::Optional<size_t> child1_index, + base::Optional<size_t> child2_index) const; + + // Calculates the position of each child view and the size of the overall + // layout based on tentative visibilities and sizes for each child. + void UpdateLayoutFromChildren(const internal::NormalizedSizeBounds& bounds, + FlexLayoutData* data, + ChildViewSpacing* child_spacing) const; + + // Applies flex rules to each view in a layout, updating |data| and + // |child_spacing|. + // + // If |expandable_views| is specified, any view requesting more than its + // preferred size will be clamped to its preferred size and be added to + // |expandable_views| for later processing after all other flex space has been + // allocated. + // + // Typically, this method will be called once with |expandable_views| set and + // then again with it null to allocate the remaining space. + void AllocateFlexSpace( + const internal::NormalizedSizeBounds& bounds, + const FlexOrderToViewIndexMap& order_to_index, + FlexLayoutData* data, + ChildViewSpacing* child_spacing, + FlexOrderToViewIndexMap* expandable_views = nullptr) const; + + // Fills out the child entries for |data| and generates some initial size + // and visibility data, and stores off information about which views can + // expand in |flex_order_to_index|. + void InitializeChildData(const internal::NormalizedSizeBounds& bounds, + FlexLayoutData* data, + FlexOrderToViewIndexMap* flex_order_to_index) const; + + // Caclulates the child bounds (in screen coordinates) for each visible child + // in the layout. + void CalculateChildBounds(const SizeBounds& size_bounds, + FlexLayoutData* data) const; + + // Gets the default value for a particular layout property, which will be used + // if the property is not set on a child view being laid out (e.g. + // kMarginsKey). + template <class T> + T* GetDefault(const ui::ClassProperty<T>* key) const { + return layout_defaults_.GetProperty(key); + } + + // Clears the default value for a particular layout property, which will be + // used if the property is not set on a child view being laid out (e.g. + // kMarginsKey). + template <class T> + FlexLayout& ClearDefault(const ui::ClassProperty<T>* key) { + layout_defaults_.ClearProperty(key); + return *this; + } LayoutOrientation orientation_ = LayoutOrientation::kHorizontal; @@ -141,12 +213,6 @@ class VIEWS_EXPORT FlexLayout : public LayoutManager { // Spacing between child views and host view border. gfx::Insets interior_margin_; - // Default spacing to put around child views. - gfx::Insets default_child_margins_; - - // Child-view-specific details (e.g. hidden status, flex rules, etc.) - std::map<const View*, internal::ChildLayoutParams> child_params_; - // The alignment of children in the main axis. This is start by default. LayoutAlignment main_axis_alignment_ = LayoutAlignment::kStart; @@ -156,15 +222,9 @@ class VIEWS_EXPORT FlexLayout : public LayoutManager { // The minimum cross axis size for the layout. int minimum_cross_axis_size_ = 0; - // Flex specification for components with no flex set. - FlexSpecification default_flex_; - - // The view that this FlexLayout is managing the layout for. - views::View* host_ = nullptr; - - // Internal data used to cache layout information, etc. All definitions and - // data are module-private. - std::unique_ptr<internal::FlexLayoutInternal> internal_; + // Default properties for any views that don't have them explicitly set for + // this layout. + PropertyHandler layout_defaults_{this}; DISALLOW_COPY_AND_ASSIGN(FlexLayout); }; diff --git a/chromium/ui/views/layout/flex_layout_types.cc b/chromium/ui/views/layout/flex_layout_types.cc index 7100e688053..6950570972d 100644 --- a/chromium/ui/views/layout/flex_layout_types.cc +++ b/chromium/ui/views/layout/flex_layout_types.cc @@ -9,7 +9,6 @@ #include <utility> #include "base/bind.h" -#include "base/strings/stringprintf.h" #include "ui/gfx/geometry/size.h" #include "ui/views/view.h" @@ -17,12 +16,6 @@ namespace views { namespace { -std::string OptionalToString(const base::Optional<int>& opt) { - if (!opt.has_value()) - return "_"; - return base::StringPrintf("%d", opt.value()); -} - // Default Flex Rules ---------------------------------------------------------- // Interpolates a size between minimum, preferred size, and upper bound based on @@ -97,7 +90,9 @@ gfx::Size GetPreferredSize(MinimumFlexSizeRule minimum_size_rule, // Note that this is an adjustment made for practical considerations, and may // not be "correct" in some absolute sense. Let's revisit at some point. const int preferred_height = - std::max(preferred.height(), view->GetHeightForWidth(width)); + width >= preferred.width() + ? preferred.height() + : std::max(preferred.height(), view->GetHeightForWidth(width)); if (!maximum_size.height()) { // Not having a maximum size is different from having a large available @@ -120,44 +115,6 @@ FlexRule GetDefaultFlexRule( } // namespace -// SizeBounds ------------------------------------------------------------------ - -SizeBounds::SizeBounds() = default; - -SizeBounds::SizeBounds(const base::Optional<int>& width, - const base::Optional<int>& height) - : width_(width), height_(height) {} - -SizeBounds::SizeBounds(const SizeBounds& other) - : width_(other.width()), height_(other.height()) {} - -SizeBounds::SizeBounds(const gfx::Size& other) - : width_(other.width()), height_(other.height()) {} - -void SizeBounds::Enlarge(int width, int height) { - if (width_) - width_ = std::max(0, *width_ + width); - if (height_) - height_ = std::max(0, *height_ + height); -} - -bool SizeBounds::operator==(const SizeBounds& other) const { - return width_ == other.width_ && height_ == other.height_; -} - -bool SizeBounds::operator!=(const SizeBounds& other) const { - return !(*this == other); -} - -bool SizeBounds::operator<(const SizeBounds& other) const { - return std::tie(height_, width_) < std::tie(other.height_, other.width_); -} - -std::string SizeBounds::ToString() const { - return base::StringPrintf("%s x %s", OptionalToString(width()).c_str(), - OptionalToString(height()).c_str()); -} - // FlexSpecification ----------------------------------------------------------- FlexSpecification::FlexSpecification() : rule_(GetDefaultFlexRule()) {} @@ -193,4 +150,112 @@ FlexSpecification FlexSpecification::WithOrder(int order) const { return FlexSpecification(rule_, order, weight_); } +// Inset1D --------------------------------------------------------------------- + +void Inset1D::SetInsets(int leading, int trailing) { + leading_ = leading; + trailing_ = trailing; +} + +void Inset1D::Expand(int leading, int trailing) { + leading_ += leading; + trailing_ += trailing; +} + +bool Inset1D::operator==(const Inset1D& other) const { + return leading_ == other.leading_ && trailing_ == other.trailing_; +} + +bool Inset1D::operator!=(const Inset1D& other) const { + return !(*this == other); +} + +bool Inset1D::operator<(const Inset1D& other) const { + return std::tie(leading_, trailing_) < + std::tie(other.leading_, other.trailing_); +} + +std::string Inset1D::ToString() const { + return base::StringPrintf("%d, %d", leading(), trailing()); +} + +// Span ------------------------------------------------------------------------ + +void Span::SetSpan(int start, int length) { + start_ = start; + length_ = std::max(0, length); +} + +void Span::Expand(int leading, int trailing) { + const int end = this->end(); + set_start(start_ - leading); + set_end(end + trailing); +} + +void Span::Inset(int leading, int trailing) { + Expand(-leading, -trailing); +} + +void Span::Inset(const Inset1D& insets) { + Inset(insets.leading(), insets.trailing()); +} + +void Span::Center(const Span& container, const Inset1D& margins) { + int remaining = container.length() - length(); + + // Case 1: no room for any margins. Just center the span in the container, + // with equal overflow on each side. + if (remaining <= 0) { + set_start(container.start() + std::ceil(remaining * 0.5f)); + return; + } + + // Case 2: room for only part of the margins. + if (margins.size() > remaining) { + float scale = float{remaining} / float{margins.size()}; + set_start(container.start() + std::roundf(scale * margins.leading())); + return; + } + + // Case 3: room for both span and margins. Center the whole unit. + remaining -= margins.size(); + set_start(container.start() + remaining / 2 + margins.leading()); +} + +void Span::Align(const Span& container, + LayoutAlignment alignment, + const Inset1D& margins) { + switch (alignment) { + case LayoutAlignment::kStart: + set_start(container.start() + margins.leading()); + break; + case LayoutAlignment::kEnd: + set_start(container.end() - (margins.trailing() + length())); + break; + case LayoutAlignment::kCenter: + Center(container, margins); + break; + case LayoutAlignment::kStretch: + SetSpan(container.start() + margins.leading(), + std::max(0, container.length() - margins.size())); + break; + } +} + +bool Span::operator==(const Span& other) const { + return start_ == other.start_ && length_ == other.length_; +} + +bool Span::operator!=(const Span& other) const { + return !(*this == other); +} + +bool Span::operator<(const Span& other) const { + return std::tie(start_, length_) < std::tie(other.start_, other.length_); +} + +std::string Span::ToString() const { + return base::StringPrintf("%d [%d]", start(), length()); +} + } // namespace views diff --git a/chromium/ui/views/layout/flex_layout_types.h b/chromium/ui/views/layout/flex_layout_types.h index f018d6ab166..d74f11ad023 100644 --- a/chromium/ui/views/layout/flex_layout_types.h +++ b/chromium/ui/views/layout/flex_layout_types.h @@ -5,11 +5,13 @@ #ifndef UI_VIEWS_LAYOUT_FLEX_LAYOUT_TYPES_H_ #define UI_VIEWS_LAYOUT_FLEX_LAYOUT_TYPES_H_ +#include <algorithm> #include <memory> #include <string> #include "base/callback.h" #include "base/optional.h" +#include "ui/views/layout/layout_types.h" #include "ui/views/views_export.h" namespace gfx { @@ -20,46 +22,9 @@ namespace views { class View; -// Whether a layout is oriented horizontally or vertically. -enum class LayoutOrientation { - kHorizontal, - kVertical, -}; - // Describes how elements should be aligned within a layout. enum class LayoutAlignment { kStart, kCenter, kEnd, kStretch }; -// Stores an optional width and height upper bound. Used when calculating the -// preferred size of a layout pursuant to a maximum available size. -class VIEWS_EXPORT SizeBounds { - public: - SizeBounds(); - SizeBounds(const base::Optional<int>& width, - const base::Optional<int>& height); - explicit SizeBounds(const gfx::Size& size); - SizeBounds(const SizeBounds& other); - - const base::Optional<int>& width() const { return width_; } - void set_width(const base::Optional<int>& width) { width_ = width; } - - const base::Optional<int>& height() const { return height_; } - void set_height(const base::Optional<int>& height) { height_ = height; } - - // Enlarges (or shrinks, if negative) each upper bound that is present by the - // specified amounts. - void Enlarge(int width, int height); - - bool operator==(const SizeBounds& other) const; - bool operator!=(const SizeBounds& other) const; - bool operator<(const SizeBounds& other) const; - - std::string ToString() const; - - private: - base::Optional<int> width_; - base::Optional<int> height_; -}; - // Callback used to specify the size of a child view based on its size bounds. // Create your own custom rules, or use the Minimum|MaximumFlexSizeRule // constants below for common behaviors. @@ -159,6 +124,85 @@ class VIEWS_EXPORT FlexSpecification { int weight_ = 0; }; +// Represents insets in a single dimension. +class Inset1D { + public: + constexpr Inset1D() = default; + constexpr explicit Inset1D(int all) : leading_(all), trailing_(all) {} + constexpr Inset1D(int leading, int trailing) + : leading_(leading), trailing_(trailing) {} + + constexpr int leading() const { return leading_; } + void set_leading(int leading) { leading_ = leading; } + + constexpr int trailing() const { return trailing_; } + void set_trailing(int trailing) { trailing_ = trailing; } + + constexpr int size() const { return leading_ + trailing_; } + + void SetInsets(int leading, int trailing); + void Expand(int delta_leading, int delta_trailing); + + constexpr bool is_empty() const { return leading_ == 0 && trailing_ == 0; } + bool operator==(const Inset1D& other) const; + bool operator!=(const Inset1D& other) const; + bool operator<(const Inset1D& other) const; + + std::string ToString() const; + + private: + int leading_ = 0; + int trailing_ = 0; +}; + +// Represents a line segment in one dimension with a starting point and length. +class Span { + public: + constexpr Span() = default; + constexpr Span(int start, int length) : start_(start), length_(length) {} + + constexpr int start() const { return start_; } + void set_start(int start) { start_ = start; } + + constexpr int length() const { return length_; } + void set_length(int length) { length_ = std::max(0, length); } + + constexpr int end() const { return start_ + length_; } + void set_end(int end) { set_length(end - start_); } + + void SetSpan(int start, int length); + + // Expands the span by |leading| at the front (reducing the value of start() + // if |leading| is positive) and by |trailing| at the end (increasing the + // value of end() if |trailing| is positive). + void Expand(int leading, int trailing); + + // Opposite of Expand(). Shrinks each end of the span by the specified amount. + void Inset(int leading, int trailing); + void Inset(const Inset1D& insets); + + // Centers the span in another span, with optional margins. + // Overflow is handled gracefully. + void Center(const Span& container, const Inset1D& margins = Inset1D()); + + // Aligns the span in another span, with optional margins, using the specified + // alignment. Overflow is handled gracefully. + void Align(const Span& container, + LayoutAlignment alignment, + const Inset1D& margins = Inset1D()); + + constexpr bool is_empty() const { return length_ == 0; } + bool operator==(const Span& other) const; + bool operator!=(const Span& other) const; + bool operator<(const Span& other) const; + + std::string ToString() const; + + private: + int start_ = 0; + int length_ = 0; +}; + } // namespace views #endif // UI_VIEWS_LAYOUT_FLEX_LAYOUT_TYPES_H_ diff --git a/chromium/ui/views/layout/flex_layout_types_internal.cc b/chromium/ui/views/layout/flex_layout_types_internal.cc index aef1542f5bd..f6c636a7b8a 100644 --- a/chromium/ui/views/layout/flex_layout_types_internal.cc +++ b/chromium/ui/views/layout/flex_layout_types_internal.cc @@ -27,114 +27,6 @@ std::string OptionalToString(const base::Optional<int>& opt) { } // namespace -// Span ------------------------------------------------------------------------ - -void Span::SetSpan(int start, int length) { - start_ = start; - length_ = std::max(0, length); -} - -void Span::Expand(int leading, int trailing) { - const int end = this->end(); - set_start(start_ - leading); - set_end(end + trailing); -} - -void Span::Inset(int leading, int trailing) { - Expand(-leading, -trailing); -} - -void Span::Inset(const Inset1D& insets) { - Inset(insets.leading(), insets.trailing()); -} - -void Span::Center(const Span& container, const Inset1D& margins) { - int remaining = container.length() - length(); - - // Case 1: no room for any margins. Just center the span in the container, - // with equal overflow on each side. - if (remaining <= 0) { - set_start(std::ceil(remaining * 0.5f)); - return; - } - - // Case 2: room for only part of the margins. - if (margins.size() > remaining) { - float scale = float{remaining} / float{margins.size()}; - set_start(std::roundf(scale * margins.leading())); - return; - } - - // Case 3: room for both span and margins. Center the whole unit. - remaining -= margins.size(); - set_start(remaining / 2 + margins.leading()); -} - -void Span::Align(const Span& container, - LayoutAlignment alignment, - const Inset1D& margins) { - switch (alignment) { - case LayoutAlignment::kStart: - set_start(container.start() + margins.leading()); - break; - case LayoutAlignment::kEnd: - set_start(container.end() - (margins.trailing() + length())); - break; - case LayoutAlignment::kCenter: - Center(container, margins); - break; - case LayoutAlignment::kStretch: - SetSpan(container.start() + margins.leading(), - std::max(0, container.length() - margins.size())); - break; - } -} - -bool Span::operator==(const Span& other) const { - return start_ == other.start_ && length_ == other.length_; -} - -bool Span::operator!=(const Span& other) const { - return !(*this == other); -} - -bool Span::operator<(const Span& other) const { - return std::tie(start_, length_) < std::tie(other.start_, other.length_); -} - -std::string Span::ToString() const { - return base::StringPrintf("%d [%d]", start(), length()); -} - -// Inset1D --------------------------------------------------------------------- - -void Inset1D::SetInsets(int leading, int trailing) { - leading_ = leading; - trailing_ = trailing; -} - -void Inset1D::Expand(int delta_leading, int delta_trailing) { - leading_ += delta_leading; - trailing_ += delta_trailing; -} - -bool Inset1D::operator==(const Inset1D& other) const { - return leading_ == other.leading_ && trailing_ == other.trailing_; -} - -bool Inset1D::operator!=(const Inset1D& other) const { - return !(*this == other); -} - -bool Inset1D::operator<(const Inset1D& other) const { - return std::tie(leading_, trailing_) < - std::tie(other.leading_, other.trailing_); -} - -std::string Inset1D::ToString() const { - return base::StringPrintf("%d, %d", leading(), trailing()); -} - // NormalizedPoint ------------------------------------------------------------- void NormalizedPoint::SetPoint(int main, int cross) { @@ -250,6 +142,10 @@ void NormalizedSizeBounds::Expand(int main, int cross) { cross_ = std::max(0, *cross_ + cross); } +void NormalizedSizeBounds::Inset(const NormalizedInsets& insets) { + Expand(-insets.main_size(), -insets.cross_size()); +} + bool NormalizedSizeBounds::operator==(const NormalizedSizeBounds& other) const { return main_ == other.main_ && cross_ == other.cross_; } @@ -310,6 +206,7 @@ void NormalizedRect::SetRect(int origin_main, origin_.SetPoint(origin_main, origin_cross); size_.SetSize(size_main, size_cross); } + void NormalizedRect::SetByBounds(int origin_main, int origin_cross, int max_main, @@ -318,17 +215,25 @@ void NormalizedRect::SetByBounds(int origin_main, size_.SetSize(std::max(0, max_main - origin_main), std::max(0, max_cross - origin_cross)); } + void NormalizedRect::Inset(const NormalizedInsets& insets) { Inset(insets.main_leading(), insets.cross_leading(), insets.main_trailing(), insets.cross_trailing()); } + void NormalizedRect::Inset(int main, int cross) { Inset(main, cross, main, cross); } + void NormalizedRect::Inset(int main_leading, int cross_leading, int main_trailing, - int cross_trailing) {} + int cross_trailing) { + origin_.Offset(main_leading, cross_leading); + size_.Enlarge(-(main_leading + main_trailing), + -(cross_leading + cross_trailing)); +} + void NormalizedRect::Offset(int main, int cross) { origin_.Offset(main, cross); } diff --git a/chromium/ui/views/layout/flex_layout_types_internal.h b/chromium/ui/views/layout/flex_layout_types_internal.h index 80b0231fda0..b52361bd390 100644 --- a/chromium/ui/views/layout/flex_layout_types_internal.h +++ b/chromium/ui/views/layout/flex_layout_types_internal.h @@ -9,6 +9,7 @@ #include "base/optional.h" #include "ui/views/layout/flex_layout_types.h" +#include "ui/views/views_export.h" namespace gfx { class Insets; @@ -23,95 +24,13 @@ class SizeBounds; namespace internal { -// Represents insets in a single dimension. -class Inset1D { - public: - constexpr Inset1D() = default; - constexpr explicit Inset1D(int all) : leading_(all), trailing_(all) {} - constexpr Inset1D(int leading, int trailing) - : leading_(leading), trailing_(trailing) {} - - constexpr int leading() const { return leading_; } - void set_leading(int leading) { leading_ = leading; } - - constexpr int trailing() const { return trailing_; } - void set_trailing(int trailing) { trailing_ = trailing; } - - constexpr int size() const { return leading_ + trailing_; } - - void SetInsets(int leading, int trailing); - void Expand(int delta_leading, int delta_trailing); - - constexpr bool is_empty() const { return leading_ == 0 && trailing_ == 0; } - bool operator==(const Inset1D& other) const; - bool operator!=(const Inset1D& other) const; - bool operator<(const Inset1D& other) const; - - std::string ToString() const; - - private: - int leading_ = 0; - int trailing_ = 0; -}; - -// Represents a line segment in one dimension with a starting point and length. -class Span { - public: - constexpr Span() = default; - constexpr Span(int start, int length) : start_(start), length_(length) {} - - constexpr int start() const { return start_; } - void set_start(int start) { start_ = start; } - - constexpr int length() const { return length_; } - void set_length(int length) { - DCHECK_GE(length, 0); - length_ = length; - } - - constexpr int end() const { return start_ + length_; } - void set_end(int end) { - DCHECK_GE(end, start_); - length_ = end - start_; - } - - void SetSpan(int start, int length); - - // Expands the span by |leading| at the front (reducing the value of start() - // if |leading| is positive) and by |trailing| at the end (increasing the - // value of end() if |trailing| is positive). - void Expand(int leading, int trailing); - - // Opposite of Expand(). Shrinks each end of the span by the specified amount. - void Inset(int leading, int trailing); - void Inset(const Inset1D& insets); - - // Centers the span in another span, with optional margins. - // Overflow is handled gracefully. - void Center(const Span& container, const Inset1D& margins = Inset1D()); - - // Aligns the span in another span, with optional margins, using the specified - // alignment. Overflow is handled gracefully. - void Align(const Span& container, - LayoutAlignment alignment, - const Inset1D& margins = Inset1D()); - - constexpr bool is_empty() const { return length_ == 0; } - bool operator==(const Span& other) const; - bool operator!=(const Span& other) const; - bool operator<(const Span& other) const; - - std::string ToString() const; - - private: - int start_ = 0; - int length_ = 0; -}; +// NOTE: classes in this namespace are marked as VIEWS_EXPORT for unit testing +// purposes only and should not be used outside the views library. // Represents a point in layout space - that is, a point on the main and cross // axes of the layout (regardless of whether it is vertically or horizontally // oriented. -class NormalizedPoint { +class VIEWS_EXPORT NormalizedPoint { public: constexpr NormalizedPoint() = default; constexpr NormalizedPoint(int main, int cross) : main_(main), cross_(cross) {} @@ -139,7 +58,7 @@ class NormalizedPoint { // Represents a size in layout space - that is, a size on the main and cross // axes of the layout (regardless of whether it is vertically or horizontally // oriented. -class NormalizedSize { +class VIEWS_EXPORT NormalizedSize { public: constexpr NormalizedSize() = default; constexpr NormalizedSize(int main, int cross) : main_(main), cross_(cross) {} @@ -173,7 +92,7 @@ class NormalizedSize { // Represents insets in layout space - that is, insets on the main and cross // axes of the layout (regardless of whether it is vertically or horizontally // oriented. -class NormalizedInsets { +class VIEWS_EXPORT NormalizedInsets { public: constexpr NormalizedInsets() = default; constexpr explicit NormalizedInsets(int all) : main_(all), cross_(all) {} @@ -230,7 +149,7 @@ class NormalizedInsets { // Represents size bounds in layout space - that is, a set of size bounds using // the main and cross axes of the layout (regardless of whether it is vertically // or horizontally oriented). -class NormalizedSizeBounds { +class VIEWS_EXPORT NormalizedSizeBounds { public: NormalizedSizeBounds(); NormalizedSizeBounds(const base::Optional<int>& main, @@ -245,6 +164,7 @@ class NormalizedSizeBounds { void set_cross(const base::Optional<int>& cross) { cross_ = cross; } void Expand(int main, int cross); + void Inset(const NormalizedInsets& insets); bool operator==(const NormalizedSizeBounds& other) const; bool operator!=(const NormalizedSizeBounds& other) const; @@ -260,7 +180,7 @@ class NormalizedSizeBounds { // Represents a rectangle in layout space - that is, a rectangle whose // dimensions align with the main and cross axis of the layout (regardless of // whether the layout is vertically or horizontally oriented). -class NormalizedRect { +class VIEWS_EXPORT NormalizedRect { public: constexpr NormalizedRect() = default; constexpr NormalizedRect(const NormalizedPoint& origin, diff --git a/chromium/ui/views/layout/flex_layout_types_internal_unittest.cc b/chromium/ui/views/layout/flex_layout_types_internal_unittest.cc new file mode 100644 index 00000000000..8dceb4e515c --- /dev/null +++ b/chromium/ui/views/layout/flex_layout_types_internal_unittest.cc @@ -0,0 +1,50 @@ +// Copyright (c) 2018 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "ui/views/layout/flex_layout_types_internal.h" + +#include "testing/gtest/include/gtest/gtest.h" + +namespace views { +namespace internal { + +TEST(NormalizedRectTest, Inset_NormalizedInsets) { + NormalizedRect rect(1, 2, 10, 11); + constexpr NormalizedInsets kInsets(1, 2, 3, 4); + rect.Inset(kInsets); + EXPECT_EQ(2, rect.origin_main()); + EXPECT_EQ(4, rect.origin_cross()); + EXPECT_EQ(6, rect.size_main()); + EXPECT_EQ(5, rect.size_cross()); +} + +TEST(NormalizedRectTest, Inset_FourValue) { + NormalizedRect rect(1, 2, 10, 11); + rect.Inset(1, 2, 3, 4); + EXPECT_EQ(2, rect.origin_main()); + EXPECT_EQ(4, rect.origin_cross()); + EXPECT_EQ(6, rect.size_main()); + EXPECT_EQ(5, rect.size_cross()); +} + +TEST(NormalizedRectTest, Inset_TwoValue) { + NormalizedRect rect(1, 2, 10, 11); + rect.Inset(3, 4); + EXPECT_EQ(4, rect.origin_main()); + EXPECT_EQ(6, rect.origin_cross()); + EXPECT_EQ(4, rect.size_main()); + EXPECT_EQ(3, rect.size_cross()); +} + +TEST(NormalizedRectTest, Inset_Negative) { + NormalizedRect rect(1, 2, 10, 11); + rect.Inset(-1, -2, -3, -4); + EXPECT_EQ(0, rect.origin_main()); + EXPECT_EQ(0, rect.origin_cross()); + EXPECT_EQ(14, rect.size_main()); + EXPECT_EQ(17, rect.size_cross()); +} + +} // namespace internal +} // namespace views diff --git a/chromium/ui/views/layout/flex_layout_unittest.cc b/chromium/ui/views/layout/flex_layout_unittest.cc index f32ba75608c..48ba06cb200 100644 --- a/chromium/ui/views/layout/flex_layout_unittest.cc +++ b/chromium/ui/views/layout/flex_layout_unittest.cc @@ -7,6 +7,7 @@ #include <stddef.h> #include <algorithm> +#include <utility> #include <vector> #include "base/bind.h" @@ -116,6 +117,7 @@ class FlexLayoutTest : public testing::Test { protected: // Constants re-used in many tests. + static const Insets kSmallInsets; static const Insets kLayoutInsets; static const Insets kLargeInsets; static const Size kChild1Size; @@ -147,6 +149,7 @@ class FlexLayoutTest : public testing::Test { FlexLayout* layout_; }; +const Insets FlexLayoutTest::kSmallInsets{1, 2, 3, 4}; const Insets FlexLayoutTest::kLayoutInsets{5, 6, 7, 9}; const Insets FlexLayoutTest::kLargeInsets{10, 11, 12, 13}; const Size FlexLayoutTest::kChild1Size{12, 10}; @@ -209,13 +212,20 @@ TEST_F(FlexLayoutTest, GetMinimumSize_Empty) { EXPECT_EQ(Size(0, 0), host_->GetMinimumSize()); } -TEST_F(FlexLayoutTest, GetMinimumSize_Empty_ViewInsets) { +TEST_F(FlexLayoutTest, GetMinimumSize_Empty_ViewInsets_Horizontal) { layout_->SetOrientation(LayoutOrientation::kHorizontal); layout_->SetCollapseMargins(false); host_->SetBorder(CreateEmptyBorder(kLayoutInsets)); EXPECT_EQ(Size(15, 12), host_->GetMinimumSize()); } +TEST_F(FlexLayoutTest, GetMinimumSize_Empty_ViewInsets_Vertical) { + layout_->SetOrientation(LayoutOrientation::kVertical); + layout_->SetCollapseMargins(false); + host_->SetBorder(CreateEmptyBorder(kLayoutInsets)); + EXPECT_EQ(Size(15, 12), host_->GetMinimumSize()); +} + TEST_F(FlexLayoutTest, GetMinimumSize_Empty_InternalMargin_Collapsed) { layout_->SetOrientation(LayoutOrientation::kHorizontal); layout_->SetCollapseMargins(true); @@ -235,7 +245,7 @@ TEST_F(FlexLayoutTest, layout_->SetOrientation(LayoutOrientation::kHorizontal); layout_->SetCollapseMargins(false); layout_->SetInteriorMargin(kLayoutInsets); - layout_->SetDefaultChildMargins(gfx::Insets(11, 11)); + layout_->SetDefault(views::kMarginsKey, gfx::Insets(11, 11)); EXPECT_EQ(Size(15, 12), host_->GetMinimumSize()); } @@ -247,8 +257,8 @@ TEST_F(FlexLayoutTest, GetMinimumSize_MinimumCross_Horizontal) { EXPECT_EQ(Size(9, 7), host_->GetMinimumSize()); layout_->SetMinimumCrossAxisSize(10); EXPECT_EQ(Size(9, 10), host_->GetMinimumSize()); - host_->SetBorder(CreateEmptyBorder(2, 2, 2, 2)); - EXPECT_EQ(Size(13, 14), host_->GetMinimumSize()); + host_->SetBorder(CreateEmptyBorder(kSmallInsets)); + EXPECT_EQ(Size(15, 14), host_->GetMinimumSize()); } TEST_F(FlexLayoutTest, GetMinimumSize_MinimumCross_Vertical) { @@ -259,8 +269,8 @@ TEST_F(FlexLayoutTest, GetMinimumSize_MinimumCross_Vertical) { EXPECT_EQ(Size(9, 7), host_->GetMinimumSize()); layout_->SetMinimumCrossAxisSize(10); EXPECT_EQ(Size(10, 7), host_->GetMinimumSize()); - host_->SetBorder(CreateEmptyBorder(2, 2, 2, 2)); - EXPECT_EQ(Size(14, 11), host_->GetMinimumSize()); + host_->SetBorder(CreateEmptyBorder(kSmallInsets)); + EXPECT_EQ(Size(16, 11), host_->GetMinimumSize()); } // Visibility and Inclusion Tests ---------------------------------------------- @@ -315,7 +325,6 @@ TEST_F(FlexLayoutTest, Layout_VisibilitySetBeforeAdd) { View* child3 = AddChild(kChild3Size); host_->Layout(); - EXPECT_EQ(true, layout_->IsHiddenByOwner(child2)); EXPECT_FALSE(child2->GetVisible()); EXPECT_EQ(Rect(6, 5, 12, 10), child1->bounds()); EXPECT_EQ(Rect(18, 5, 17, 13), child3->bounds()); @@ -324,7 +333,6 @@ TEST_F(FlexLayoutTest, Layout_VisibilitySetBeforeAdd) { // This should have no additional effect since the child is already invisible. child2->SetVisible(false); host_->Layout(); - EXPECT_EQ(true, layout_->IsHiddenByOwner(child2)); EXPECT_FALSE(child2->GetVisible()); EXPECT_EQ(Rect(6, 5, 12, 10), child1->bounds()); EXPECT_EQ(Rect(18, 5, 17, 13), child3->bounds()); @@ -334,7 +342,6 @@ TEST_F(FlexLayoutTest, Layout_VisibilitySetBeforeAdd) { host_->Layout(); std::vector<Rect> expected{Rect(6, 5, 12, 10), Rect(18, 5, 13, 11), Rect(31, 5, 17, 13)}; - EXPECT_EQ(false, layout_->IsHiddenByOwner(child2)); EXPECT_TRUE(child2->GetVisible()); EXPECT_EQ(expected, GetChildBounds()); EXPECT_EQ(Size(57, 25), host_->GetPreferredSize()); @@ -351,7 +358,6 @@ TEST_F(FlexLayoutTest, Layout_VisibilitySetAfterAdd) { child2->SetVisible(false); host_->Layout(); - EXPECT_EQ(true, layout_->IsHiddenByOwner(child2)); EXPECT_FALSE(child2->GetVisible()); EXPECT_EQ(Rect(6, 5, 12, 10), child1->bounds()); EXPECT_EQ(Rect(18, 5, 17, 13), child3->bounds()); @@ -361,7 +367,6 @@ TEST_F(FlexLayoutTest, Layout_VisibilitySetAfterAdd) { host_->Layout(); std::vector<Rect> expected{Rect(6, 5, 12, 10), Rect(18, 5, 13, 11), Rect(31, 5, 17, 13)}; - EXPECT_EQ(false, layout_->IsHiddenByOwner(child2)); EXPECT_TRUE(child2->GetVisible()); EXPECT_EQ(expected, GetChildBounds()); EXPECT_EQ(Size(57, 25), host_->GetPreferredSize()); @@ -376,12 +381,11 @@ TEST_F(FlexLayoutTest, View* child1 = AddChild(kChild1Size); View* child2 = AddChild(kChild2Size); View* child3 = AddChild(kChild3Size); - layout_->SetFlexForView(child2, kDropOut); + child2->SetProperty(views::kFlexBehaviorKey, kDropOut); // Layout makes child view invisible due to flex rule. host_->SetSize(Size(40, 25)); host_->Layout(); - EXPECT_EQ(false, layout_->IsHiddenByOwner(child2)); EXPECT_FALSE(child2->GetVisible()); EXPECT_EQ(Rect(6, 5, 12, 10), child1->bounds()); EXPECT_EQ(Rect(18, 5, 17, 13), child3->bounds()); @@ -390,11 +394,7 @@ TEST_F(FlexLayoutTest, // Now we will make child explicitly hidden. child2->SetVisible(false); - EXPECT_EQ(true, layout_->IsHiddenByOwner(child2)); - - // Layout is the same, but we should report that the view is hidden by owner. host_->Layout(); - EXPECT_EQ(true, layout_->IsHiddenByOwner(child2)); EXPECT_FALSE(child2->GetVisible()); EXPECT_EQ(Rect(6, 5, 12, 10), child1->bounds()); EXPECT_EQ(Rect(18, 5, 17, 13), child3->bounds()); @@ -410,20 +410,18 @@ TEST_F(FlexLayoutTest, Layout_Exlcude) { View* child2 = AddChild(kChild2Size); const View* child3 = AddChild(kChild3Size); - layout_->SetViewExcluded(child2, true); + layout_->SetChildViewIgnoredByLayout(child2, true); child2->SetBounds(3, 3, 3, 3); host_->Layout(); - EXPECT_EQ(true, layout_->IsViewExcluded(child2)); EXPECT_EQ(Rect(3, 3, 3, 3), child2->bounds()); EXPECT_EQ(Rect(6, 5, 12, 10), child1->bounds()); EXPECT_EQ(Rect(18, 5, 17, 13), child3->bounds()); EXPECT_EQ(Size(44, 25), host_->GetPreferredSize()); - layout_->SetViewExcluded(child2, false); + layout_->SetChildViewIgnoredByLayout(child2, false); host_->Layout(); std::vector<Rect> expected{Rect(6, 5, 12, 10), Rect(18, 5, 13, 11), Rect(31, 5, 17, 13)}; - EXPECT_EQ(false, layout_->IsViewExcluded(child2)); EXPECT_EQ(expected, GetChildBounds()); EXPECT_EQ(Size(57, 25), host_->GetPreferredSize()); } @@ -585,7 +583,7 @@ TEST_F(FlexLayoutTest, EXPECT_EQ(expected, GetChildBounds()); EXPECT_EQ(Size(57, 25), host_->GetPreferredSize()); - child1->SetProperty(views::kMarginsKey, new Insets(20, 21, 22, 23)); + child1->SetProperty(views::kMarginsKey, Insets(20, 21, 22, 23)); host_->InvalidateLayout(); host_->Layout(); expected = std::vector<Rect>{Rect(27, 25, 12, 10), Rect(62, 5, 13, 11), @@ -593,16 +591,16 @@ TEST_F(FlexLayoutTest, EXPECT_EQ(expected, GetChildBounds()); EXPECT_EQ(Size(101, 64), host_->GetPreferredSize()); - child2->SetProperty(views::kMarginsKey, new Insets(1, 1, 1, 1)); + child2->SetProperty(views::kMarginsKey, Insets(1, 1, 1, 1)); host_->InvalidateLayout(); - layout_->SetDefaultChildMargins(gfx::Insets(0, 3)); + layout_->SetDefault(views::kMarginsKey, gfx::Insets(0, 3)); host_->Layout(); expected = std::vector<Rect>{Rect(27, 25, 12, 10), Rect(63, 6, 13, 11), Rect(80, 5, 17, 13)}; EXPECT_EQ(expected, GetChildBounds()); EXPECT_EQ(Size(109, 64), host_->GetPreferredSize()); - child3->SetProperty(views::kMarginsKey, new Insets(2, 2, 2, 2)); + child3->SetProperty(views::kMarginsKey, Insets(2, 2, 2, 2)); host_->InvalidateLayout(); host_->Layout(); expected = std::vector<Rect>{Rect(27, 25, 12, 10), Rect(63, 6, 13, 11), @@ -617,13 +615,13 @@ TEST_F(FlexLayoutTest, layout_->SetCollapseMargins(false); layout_->SetInteriorMargin(kLayoutInsets); layout_->SetCrossAxisAlignment(LayoutAlignment::kStart); - layout_->SetDefaultChildMargins(gfx::Insets(3)); + layout_->SetDefault(views::kMarginsKey, gfx::Insets(3)); View* child1 = AddChild(kChild1Size); View* child2 = AddChild(kChild2Size); View* child3 = AddChild(kChild3Size); - child1->SetProperty(views::kMarginsKey, new Insets(20, 21, 22, 23)); - child2->SetProperty(views::kMarginsKey, new Insets(1, 1, 1, 1)); - child3->SetProperty(views::kMarginsKey, new Insets(2, 2, 2, 2)); + child1->SetProperty(views::kMarginsKey, Insets(20, 21, 22, 23)); + child2->SetProperty(views::kMarginsKey, Insets(1, 1, 1, 1)); + child3->SetProperty(views::kMarginsKey, Insets(2, 2, 2, 2)); host_->InvalidateLayout(); host_->Layout(); std::vector<Rect> expected{Rect(27, 25, 12, 10), Rect(7, 58, 13, 11), @@ -638,13 +636,13 @@ TEST_F(FlexLayoutTest, layout_->SetCollapseMargins(true); layout_->SetInteriorMargin(kLayoutInsets); layout_->SetCrossAxisAlignment(LayoutAlignment::kStart); - layout_->SetDefaultChildMargins(gfx::Insets(3)); + layout_->SetDefault(views::kMarginsKey, gfx::Insets(3)); View* child1 = AddChild(kChild1Size); View* child2 = AddChild(kChild2Size); View* child3 = AddChild(kChild3Size); - child1->SetProperty(views::kMarginsKey, new Insets(20, 21, 22, 23)); - child2->SetProperty(views::kMarginsKey, new Insets(1, 1, 1, 1)); - child3->SetProperty(views::kMarginsKey, new Insets(2, 2, 2, 2)); + child1->SetProperty(views::kMarginsKey, Insets(20, 21, 22, 23)); + child2->SetProperty(views::kMarginsKey, Insets(1, 1, 1, 1)); + child3->SetProperty(views::kMarginsKey, Insets(2, 2, 2, 2)); host_->InvalidateLayout(); host_->Layout(); std::vector<Rect> expected{Rect(21, 20, 12, 10), Rect(56, 5, 13, 11), @@ -658,13 +656,13 @@ TEST_F(FlexLayoutTest, LayoutMultipleViews_MarginAndSpacing_Collapse_Vertical) { layout_->SetCollapseMargins(true); layout_->SetInteriorMargin(kLayoutInsets); layout_->SetCrossAxisAlignment(LayoutAlignment::kStart); - layout_->SetDefaultChildMargins(gfx::Insets(3)); + layout_->SetDefault(views::kMarginsKey, gfx::Insets(3)); View* child1 = AddChild(kChild1Size); View* child2 = AddChild(kChild2Size); View* child3 = AddChild(kChild3Size); - child1->SetProperty(views::kMarginsKey, new Insets(20, 21, 22, 23)); - child2->SetProperty(views::kMarginsKey, new Insets(1, 1, 1, 1)); - child3->SetProperty(views::kMarginsKey, new Insets(2, 2, 2, 2)); + child1->SetProperty(views::kMarginsKey, Insets(20, 21, 22, 23)); + child2->SetProperty(views::kMarginsKey, Insets(1, 1, 1, 1)); + child3->SetProperty(views::kMarginsKey, Insets(2, 2, 2, 2)); host_->InvalidateLayout(); host_->Layout(); std::vector<Rect> expected{Rect(21, 20, 12, 10), Rect(6, 52, 13, 11), @@ -678,10 +676,10 @@ TEST_F(FlexLayoutTest, LayoutMultipleViews_InteriorPadding) { layout_->SetCollapseMargins(true); layout_->SetInteriorMargin(kLayoutInsets); layout_->SetCrossAxisAlignment(LayoutAlignment::kStart); - layout_->SetDefaultChildMargins(gfx::Insets(10)); + layout_->SetDefault(views::kMarginsKey, gfx::Insets(10)); View* child = AddChild(Size(13, 15)); AddChild(kChild3Size); - child->SetProperty(views::kInternalPaddingKey, new Insets(1, 2, 4, 8)); + child->SetProperty(views::kInternalPaddingKey, Insets(1, 2, 4, 8)); host_->InvalidateLayout(); host_->Layout(); std::vector<Rect> expected{ @@ -697,11 +695,11 @@ TEST_F(FlexLayoutTest, LayoutMultipleViews_InteriorPadding_Margins) { layout_->SetCollapseMargins(true); layout_->SetInteriorMargin(kLayoutInsets); layout_->SetCrossAxisAlignment(LayoutAlignment::kStart); - layout_->SetDefaultChildMargins(gfx::Insets(2)); + layout_->SetDefault(views::kMarginsKey, gfx::Insets(2)); View* child = AddChild(Size(13, 15)); View* child2 = AddChild(kChild3Size); - child->SetProperty(views::kInternalPaddingKey, new Insets(1, 2, 4, 8)); - child2->SetProperty(views::kMarginsKey, new Insets(5, 5, 5, 5)); + child->SetProperty(views::kInternalPaddingKey, Insets(1, 2, 4, 8)); + child2->SetProperty(views::kMarginsKey, Insets(5, 5, 5, 5)); host_->InvalidateLayout(); host_->Layout(); std::vector<Rect> expected{ @@ -717,11 +715,11 @@ TEST_F(FlexLayoutTest, LayoutMultipleViews_InteriorPadding_Additive) { layout_->SetCollapseMargins(true); layout_->SetInteriorMargin(kLayoutInsets); layout_->SetCrossAxisAlignment(LayoutAlignment::kStart); - layout_->SetDefaultChildMargins(gfx::Insets(20)); + layout_->SetDefault(views::kMarginsKey, gfx::Insets(20)); View* child = AddChild(Size(13, 15)); View* child2 = AddChild(kChild3Size); - child->SetProperty(views::kInternalPaddingKey, new Insets(1, 2, 4, 8)); - child2->SetProperty(views::kInternalPaddingKey, new Insets(5, 5, 5, 5)); + child->SetProperty(views::kInternalPaddingKey, Insets(1, 2, 4, 8)); + child2->SetProperty(views::kInternalPaddingKey, Insets(5, 5, 5, 5)); host_->InvalidateLayout(); host_->Layout(); std::vector<Rect> expected{ @@ -732,6 +730,97 @@ TEST_F(FlexLayoutTest, LayoutMultipleViews_InteriorPadding_Additive) { EXPECT_EQ(Size(70, 50), host_->GetPreferredSize()); } +// Host insets tests ----------------------------------------------------------- + +TEST_F(FlexLayoutTest, Layout_HostInsets_Horizontal) { + layout_->SetOrientation(LayoutOrientation::kHorizontal); + host_->SetBorder(CreateEmptyBorder(kLayoutInsets)); + View* child = AddChild(kChild1Size); + host_->Layout(); + EXPECT_EQ(Rect(6, 5, 12, 10), child->bounds()); +} + +TEST_F(FlexLayoutTest, Layout_HostInsets_Vertical) { + layout_->SetOrientation(LayoutOrientation::kVertical); + host_->SetBorder(CreateEmptyBorder(kLayoutInsets)); + View* child = AddChild(kChild1Size); + host_->Layout(); + EXPECT_EQ(Rect(6, 5, 12, 10), child->bounds()); +} + +TEST_F(FlexLayoutTest, Layout_HostInsets_Horizontal_Leading) { + layout_->SetOrientation(LayoutOrientation::kHorizontal); + layout_->SetMainAxisAlignment(LayoutAlignment::kStart); + layout_->SetCrossAxisAlignment(LayoutAlignment::kStart); + host_->SetBorder(CreateEmptyBorder(kLayoutInsets)); + View* child = AddChild(kChild1Size); + host_->SetSize({100, 100}); + EXPECT_EQ(Rect(6, 5, 12, 10), child->bounds()); +} + +TEST_F(FlexLayoutTest, Layout_HostInsets_Vertical_Leading) { + layout_->SetOrientation(LayoutOrientation::kVertical); + layout_->SetMainAxisAlignment(LayoutAlignment::kStart); + layout_->SetCrossAxisAlignment(LayoutAlignment::kStart); + host_->SetBorder(CreateEmptyBorder(kLayoutInsets)); + View* child = AddChild(kChild1Size); + host_->SetSize({100, 100}); + EXPECT_EQ(Rect(6, 5, 12, 10), child->bounds()); +} + +TEST_F(FlexLayoutTest, Layout_HostInsets_Horizontal_Center) { + layout_->SetOrientation(LayoutOrientation::kHorizontal); + layout_->SetMainAxisAlignment(LayoutAlignment::kCenter); + layout_->SetCrossAxisAlignment(LayoutAlignment::kStart); + host_->SetBorder(CreateEmptyBorder(kLayoutInsets)); + View* child = AddChild(kChild1Size); + host_->SetSize({100, 100}); + const int expected_x = + kLayoutInsets.left() + + (host_->size().width() - kChild1Size.width() - kLayoutInsets.width()) / 2; + EXPECT_EQ(Rect(expected_x, 5, 12, 10), child->bounds()); +} + +TEST_F(FlexLayoutTest, Layout_HostInsets_Vertical_Center) { + layout_->SetOrientation(LayoutOrientation::kVertical); + layout_->SetMainAxisAlignment(LayoutAlignment::kCenter); + layout_->SetCrossAxisAlignment(LayoutAlignment::kStart); + host_->SetBorder(CreateEmptyBorder(kLayoutInsets)); + View* child = AddChild(kChild1Size); + host_->SetSize({100, 100}); + const int expected_y = + kLayoutInsets.top() + + (host_->size().height() - kChild1Size.height() - kLayoutInsets.height()) / + 2; + EXPECT_EQ(Rect(6, expected_y, 12, 10), child->bounds()); +} + +TEST_F(FlexLayoutTest, Layout_HostInsets_Horizontal_End) { + layout_->SetOrientation(LayoutOrientation::kHorizontal); + layout_->SetMainAxisAlignment(LayoutAlignment::kEnd); + layout_->SetCrossAxisAlignment(LayoutAlignment::kStart); + host_->SetBorder(CreateEmptyBorder(kLayoutInsets)); + View* child = AddChild(kChild1Size); + host_->SetSize({100, 100}); + const int expected_x = + kLayoutInsets.left() + + (host_->size().width() - kChild1Size.width() - kLayoutInsets.width()); + EXPECT_EQ(Rect(expected_x, 5, 12, 10), child->bounds()); +} + +TEST_F(FlexLayoutTest, Layout_HostInsets_Vertical_End) { + layout_->SetOrientation(LayoutOrientation::kVertical); + layout_->SetMainAxisAlignment(LayoutAlignment::kEnd); + layout_->SetCrossAxisAlignment(LayoutAlignment::kStart); + host_->SetBorder(CreateEmptyBorder(kLayoutInsets)); + View* child = AddChild(kChild1Size); + host_->SetSize({100, 100}); + const int expected_y = + kLayoutInsets.top() + + (host_->size().height() - kChild1Size.height() - kLayoutInsets.height()); + EXPECT_EQ(Rect(6, expected_y, 12, 10), child->bounds()); +} + // Alignment Tests ------------------------------------------------------------- TEST_F(FlexLayoutTest, Layout_CrossStart) { @@ -743,9 +832,9 @@ TEST_F(FlexLayoutTest, Layout_CrossStart) { View* child1 = AddChild(kChild1Size); View* child2 = AddChild(kChild2Size); View* child3 = AddChild(kChild3Size); - child1->SetProperty(views::kMarginsKey, new Insets(kLargeInsets)); - child2->SetProperty(views::kMarginsKey, new Insets(1, 1, 1, 1)); - child3->SetProperty(views::kMarginsKey, new Insets(2, 2, 2, 2)); + child1->SetProperty(views::kMarginsKey, Insets(kLargeInsets)); + child2->SetProperty(views::kMarginsKey, Insets(1, 1, 1, 1)); + child3->SetProperty(views::kMarginsKey, Insets(2, 2, 2, 2)); host_->SetSize(Size(200, 200)); host_->Layout(); EXPECT_EQ(10, child1->origin().y()); @@ -762,9 +851,9 @@ TEST_F(FlexLayoutTest, Layout_CrossCenter) { View* child1 = AddChild(kChild1Size); View* child2 = AddChild(kChild2Size); View* child3 = AddChild(kChild3Size); - child1->SetProperty(views::kMarginsKey, new Insets(kLargeInsets)); - child2->SetProperty(views::kMarginsKey, new Insets(1, 1, 1, 1)); - child3->SetProperty(views::kMarginsKey, new Insets(2, 2, 2, 2)); + child1->SetProperty(views::kMarginsKey, Insets(kLargeInsets)); + child2->SetProperty(views::kMarginsKey, Insets(1, 1, 1, 1)); + child3->SetProperty(views::kMarginsKey, Insets(2, 2, 2, 2)); host_->SetSize(Size(200, 200)); host_->Layout(); EXPECT_EQ(94, child1->origin().y()); @@ -781,9 +870,9 @@ TEST_F(FlexLayoutTest, Layout_CrossEnd) { View* child1 = AddChild(kChild1Size); View* child2 = AddChild(kChild2Size); View* child3 = AddChild(kChild3Size); - child1->SetProperty(views::kMarginsKey, new Insets(kLargeInsets)); - child2->SetProperty(views::kMarginsKey, new Insets(1, 1, 1, 1)); - child3->SetProperty(views::kMarginsKey, new Insets(2, 2, 2, 2)); + child1->SetProperty(views::kMarginsKey, Insets(kLargeInsets)); + child2->SetProperty(views::kMarginsKey, Insets(1, 1, 1, 1)); + child3->SetProperty(views::kMarginsKey, Insets(2, 2, 2, 2)); host_->SetSize(Size(200, 200)); host_->Layout(); EXPECT_EQ(178, child1->origin().y()); @@ -800,9 +889,9 @@ TEST_F(FlexLayoutTest, Layout_CrossStretch) { View* child1 = AddChild(kChild1Size); View* child2 = AddChild(kChild2Size); View* child3 = AddChild(kChild3Size); - child1->SetProperty(views::kMarginsKey, new Insets(kLargeInsets)); - child2->SetProperty(views::kMarginsKey, new Insets(1, 1, 1, 1)); - child3->SetProperty(views::kMarginsKey, new Insets(2, 2, 2, 2)); + child1->SetProperty(views::kMarginsKey, Insets(kLargeInsets)); + child2->SetProperty(views::kMarginsKey, Insets(1, 1, 1, 1)); + child3->SetProperty(views::kMarginsKey, Insets(2, 2, 2, 2)); host_->SetSize(Size(200, 200)); host_->Layout(); EXPECT_EQ(10, child1->origin().y()); @@ -819,13 +908,13 @@ TEST_F(FlexLayoutTest, Layout_AlignStart) { layout_->SetInteriorMargin(kLayoutInsets); layout_->SetMainAxisAlignment(LayoutAlignment::kStart); layout_->SetCrossAxisAlignment(LayoutAlignment::kStart); - layout_->SetDefaultChildMargins(gfx::Insets(3)); + layout_->SetDefault(views::kMarginsKey, gfx::Insets(3)); View* child1 = AddChild(kChild1Size); View* child2 = AddChild(kChild2Size); View* child3 = AddChild(kChild3Size); - child1->SetProperty(views::kMarginsKey, new Insets(20, 21, 22, 23)); - child2->SetProperty(views::kMarginsKey, new Insets(1, 1, 1, 1)); - child3->SetProperty(views::kMarginsKey, new Insets(2, 2, 2, 2)); + child1->SetProperty(views::kMarginsKey, Insets(20, 21, 22, 23)); + child2->SetProperty(views::kMarginsKey, Insets(1, 1, 1, 1)); + child3->SetProperty(views::kMarginsKey, Insets(2, 2, 2, 2)); host_->SetSize(Size(105, 50)); host_->Layout(); std::vector<Rect> expected{Rect(21, 20, 12, 10), Rect(56, 5, 13, 11), @@ -839,13 +928,13 @@ TEST_F(FlexLayoutTest, Layout_AlignCenter) { layout_->SetInteriorMargin(kLayoutInsets); layout_->SetMainAxisAlignment(LayoutAlignment::kCenter); layout_->SetCrossAxisAlignment(LayoutAlignment::kStart); - layout_->SetDefaultChildMargins(gfx::Insets(3)); + layout_->SetDefault(views::kMarginsKey, gfx::Insets(3)); View* child1 = AddChild(kChild1Size); View* child2 = AddChild(kChild2Size); View* child3 = AddChild(kChild3Size); - child1->SetProperty(views::kMarginsKey, new Insets(20, 21, 22, 23)); - child2->SetProperty(views::kMarginsKey, new Insets(1, 1, 1, 1)); - child3->SetProperty(views::kMarginsKey, new Insets(2, 2, 2, 2)); + child1->SetProperty(views::kMarginsKey, Insets(20, 21, 22, 23)); + child2->SetProperty(views::kMarginsKey, Insets(1, 1, 1, 1)); + child3->SetProperty(views::kMarginsKey, Insets(2, 2, 2, 2)); host_->SetSize(Size(105, 50)); host_->Layout(); std::vector<Rect> expected{Rect(25, 20, 12, 10), Rect(60, 5, 13, 11), @@ -859,13 +948,13 @@ TEST_F(FlexLayoutTest, Layout_AlignEnd) { layout_->SetInteriorMargin(kLayoutInsets); layout_->SetMainAxisAlignment(LayoutAlignment::kEnd); layout_->SetCrossAxisAlignment(LayoutAlignment::kStart); - layout_->SetDefaultChildMargins(gfx::Insets(3)); + layout_->SetDefault(views::kMarginsKey, gfx::Insets(3)); View* child1 = AddChild(kChild1Size); View* child2 = AddChild(kChild2Size); View* child3 = AddChild(kChild3Size); - child1->SetProperty(views::kMarginsKey, new Insets(20, 21, 22, 23)); - child2->SetProperty(views::kMarginsKey, new Insets(1, 1, 1, 1)); - child3->SetProperty(views::kMarginsKey, new Insets(2, 2, 2, 2)); + child1->SetProperty(views::kMarginsKey, Insets(20, 21, 22, 23)); + child2->SetProperty(views::kMarginsKey, Insets(1, 1, 1, 1)); + child3->SetProperty(views::kMarginsKey, Insets(2, 2, 2, 2)); host_->SetSize(Size(105, 50)); host_->Layout(); std::vector<Rect> expected{Rect(29, 20, 12, 10), Rect(64, 5, 13, 11), @@ -882,8 +971,8 @@ TEST_F(FlexLayoutTest, Layout_AddDroppedMargins) { View* child1 = AddChild(Size(10, 10)); View* child2 = AddChild(Size(10, 10)); View* child3 = AddChild(Size(10, 10)); - child2->SetProperty(views::kMarginsKey, new Insets(1, 1, 1, 1)); - layout_->SetFlexForView(child2, kDropOut); + child2->SetProperty(views::kMarginsKey, Insets(1, 1, 1, 1)); + child2->SetProperty(views::kFlexBehaviorKey, kDropOut); EXPECT_EQ(Size(30, 20), host_->GetMinimumSize()); host_->SetSize(Size(100, 50)); @@ -899,6 +988,30 @@ TEST_F(FlexLayoutTest, Layout_AddDroppedMargins) { EXPECT_EQ(Rect(15, 5, 10, 10), child3->bounds()); } +TEST_F(FlexLayoutTest, Layout_VerticalAlign_WiderThanTall) { + // This test ensures we do not regress http://crbug.com/983941 + // Previously, the width of the host view was erroneously used when + // calculating excess main-axis size, causing center-alignment in vertical + // layouts in host views that were much wider than tall to be incorrect. + layout_->SetOrientation(LayoutOrientation::kVertical); + layout_->SetCollapseMargins(true); + layout_->SetInteriorMargin(kLayoutInsets); + layout_->SetMainAxisAlignment(LayoutAlignment::kCenter); + layout_->SetCrossAxisAlignment(LayoutAlignment::kStart); + layout_->SetDefault(views::kMarginsKey, gfx::Insets(3)); + View* child1 = AddChild(kChild1Size); + View* child2 = AddChild(kChild2Size); + View* child3 = AddChild(kChild3Size); + child1->SetProperty(views::kMarginsKey, Insets(20, 21, 22, 23)); + child2->SetProperty(views::kMarginsKey, Insets(1, 1, 1, 1)); + child3->SetProperty(views::kMarginsKey, Insets(2, 2, 2, 2)); + host_->SetSize(Size(1000, 100)); + host_->Layout(); + std::vector<Rect> expected{Rect(21, 27, 12, 10), Rect(6, 59, 13, 11), + Rect(6, 72, 17, 13)}; + EXPECT_EQ(expected, GetChildBounds()); +} + // Flex Tests ------------------------------------------------------------------ TEST_F(FlexLayoutTest, Layout_IgnoreMinimumSize_DropViews) { @@ -907,20 +1020,21 @@ TEST_F(FlexLayoutTest, Layout_IgnoreMinimumSize_DropViews) { layout_->SetInteriorMargin(kLayoutInsets); layout_->SetMainAxisAlignment(LayoutAlignment::kStart); layout_->SetCrossAxisAlignment(LayoutAlignment::kStart); - layout_->SetDefaultChildMargins(gfx::Insets(3)); + layout_->SetDefault(views::kMarginsKey, gfx::Insets(3)); View* child1 = AddChild(kChild1Size); View* child2 = AddChild(kChild2Size); View* child3 = AddChild(kChild3Size); - child1->SetProperty(views::kMarginsKey, new Insets(kLargeInsets)); - child2->SetProperty(views::kMarginsKey, new Insets(1, 1, 1, 1)); - child3->SetProperty(views::kMarginsKey, new Insets(2, 2, 2, 2)); + child1->SetProperty(views::kMarginsKey, Insets(kLargeInsets)); + child2->SetProperty(views::kMarginsKey, Insets(1, 1, 1, 1)); + child3->SetProperty(views::kMarginsKey, Insets(2, 2, 2, 2)); host_->SetSize(Size(55, 50)); host_->Layout(); std::vector<Rect> expected{Rect(11, 10, 12, 10), Rect(36, 5, 13, 11), Rect(51, 5, 17, 13)}; EXPECT_EQ(expected, GetChildBounds()); - layout_->SetFlexForView(child1, kDropOut); + child1->SetProperty(views::kFlexBehaviorKey, kDropOut); + host_->InvalidateLayout(); EXPECT_EQ(Size(77, 32), host_->GetPreferredSize()); EXPECT_EQ(Size(47, 25), host_->GetMinimumSize()); host_->Layout(); @@ -930,8 +1044,9 @@ TEST_F(FlexLayoutTest, Layout_IgnoreMinimumSize_DropViews) { EXPECT_EQ(Rect(6, 5, 13, 11), child2->bounds()); EXPECT_EQ(Rect(21, 5, 17, 13), child3->bounds()); - layout_->ClearFlexForView(child1); - layout_->SetFlexForView(child2, kDropOut); + child1->ClearProperty(views::kFlexBehaviorKey); + child2->SetProperty(views::kFlexBehaviorKey, kDropOut); + host_->InvalidateLayout(); EXPECT_EQ(Size(77, 32), host_->GetPreferredSize()); EXPECT_EQ(Size(62, 32), host_->GetMinimumSize()); host_->Layout(); @@ -941,8 +1056,9 @@ TEST_F(FlexLayoutTest, Layout_IgnoreMinimumSize_DropViews) { EXPECT_EQ(Rect(11, 10, 12, 10), child1->bounds()); EXPECT_EQ(Rect(36, 5, 17, 13), child3->bounds()); - layout_->ClearFlexForView(child2); - layout_->SetFlexForView(child3, kDropOut); + child2->ClearProperty(views::kFlexBehaviorKey); + child3->SetProperty(views::kFlexBehaviorKey, kDropOut); + host_->InvalidateLayout(); EXPECT_EQ(Size(77, 32), host_->GetPreferredSize()); EXPECT_EQ(Size(58, 32), host_->GetMinimumSize()); host_->Layout(); @@ -959,17 +1075,17 @@ TEST_F(FlexLayoutTest, Layout_IgnoreMinimumSize_DropInOrder) { layout_->SetInteriorMargin(kLayoutInsets); layout_->SetMainAxisAlignment(LayoutAlignment::kStart); layout_->SetCrossAxisAlignment(LayoutAlignment::kStart); - layout_->SetDefaultChildMargins(gfx::Insets(3)); + layout_->SetDefault(views::kMarginsKey, gfx::Insets(3)); View* child1 = AddChild(kChild1Size); View* child2 = AddChild(kChild2Size); View* child3 = AddChild(kChild3Size); - child1->SetProperty(views::kMarginsKey, new Insets(kLargeInsets)); - child2->SetProperty(views::kMarginsKey, new Insets(1, 1, 1, 1)); - child3->SetProperty(views::kMarginsKey, new Insets(2, 2, 2, 2)); + child1->SetProperty(views::kMarginsKey, Insets(kLargeInsets)); + child2->SetProperty(views::kMarginsKey, Insets(1, 1, 1, 1)); + child3->SetProperty(views::kMarginsKey, Insets(2, 2, 2, 2)); // Set flex separately; we'll test default flex later. - layout_->SetFlexForView(child1, kDropOut); - layout_->SetFlexForView(child2, kDropOut); - layout_->SetFlexForView(child3, kDropOut); + child1->SetProperty(views::kFlexBehaviorKey, kDropOut); + child2->SetProperty(views::kFlexBehaviorKey, kDropOut); + child3->SetProperty(views::kFlexBehaviorKey, kDropOut); EXPECT_EQ(Size(9, 7), host_->GetMinimumSize()); host_->SetSize(Size(100, 50)); @@ -1011,14 +1127,14 @@ TEST_F(FlexLayoutTest, Layout_IgnoreMinimumSize_DropInOrder_DefaultFlex) { layout_->SetInteriorMargin(kLayoutInsets); layout_->SetMainAxisAlignment(LayoutAlignment::kStart); layout_->SetCrossAxisAlignment(LayoutAlignment::kStart); - layout_->SetDefaultChildMargins(gfx::Insets(3)); + layout_->SetDefault(views::kMarginsKey, gfx::Insets(3)); View* child1 = AddChild(kChild1Size); View* child2 = AddChild(kChild2Size); View* child3 = AddChild(kChild3Size); - child1->SetProperty(views::kMarginsKey, new Insets(kLargeInsets)); - child2->SetProperty(views::kMarginsKey, new Insets(1, 1, 1, 1)); - child3->SetProperty(views::kMarginsKey, new Insets(2, 2, 2, 2)); - layout_->SetDefaultFlex(kDropOut); + child1->SetProperty(views::kMarginsKey, Insets(kLargeInsets)); + child2->SetProperty(views::kMarginsKey, Insets(1, 1, 1, 1)); + child3->SetProperty(views::kMarginsKey, Insets(2, 2, 2, 2)); + layout_->SetDefault(views::kFlexBehaviorKey, kDropOut); EXPECT_EQ(Size(9, 7), host_->GetMinimumSize()); host_->SetSize(Size(100, 50)); @@ -1059,15 +1175,15 @@ TEST_F(FlexLayoutTest, Layout_IgnoreMinimumSize_DropByPriority) { layout_->SetInteriorMargin(kLayoutInsets); layout_->SetMainAxisAlignment(LayoutAlignment::kStart); layout_->SetCrossAxisAlignment(LayoutAlignment::kStart); - layout_->SetDefaultChildMargins(gfx::Insets(3)); + layout_->SetDefault(views::kMarginsKey, gfx::Insets(3)); View* child1 = AddChild(kChild1Size); View* child2 = AddChild(kChild2Size); View* child3 = AddChild(kChild3Size); - child1->SetProperty(views::kMarginsKey, new Insets(kLargeInsets)); - child2->SetProperty(views::kMarginsKey, new Insets(1, 1, 1, 1)); - child3->SetProperty(views::kMarginsKey, new Insets(2, 2, 2, 2)); - layout_->SetDefaultFlex(kDropOut); - layout_->SetFlexForView(child3, kDropOutHighPriority); + child1->SetProperty(views::kMarginsKey, Insets(kLargeInsets)); + child2->SetProperty(views::kMarginsKey, Insets(1, 1, 1, 1)); + child3->SetProperty(views::kMarginsKey, Insets(2, 2, 2, 2)); + layout_->SetDefault(views::kFlexBehaviorKey, kDropOut); + child3->SetProperty(views::kFlexBehaviorKey, kDropOutHighPriority); EXPECT_EQ(Size(9, 7), host_->GetMinimumSize()); host_->SetSize(Size(100, 50)); @@ -1101,10 +1217,10 @@ TEST_F(FlexLayoutTest, Layout_Flex_OneViewScales) { layout_->SetInteriorMargin(Insets(5)); layout_->SetMainAxisAlignment(LayoutAlignment::kStart); layout_->SetCrossAxisAlignment(LayoutAlignment::kStart); - layout_->SetDefaultChildMargins(gfx::Insets(5)); + layout_->SetDefault(views::kMarginsKey, gfx::Insets(5)); View* child1 = AddChild(Size(10, 20), Size(5, 5)); View* child2 = AddChild(Size(10, 10)); - layout_->SetFlexForView(child1, kFlex1ScaleToMinimum); + child1->SetProperty(views::kFlexBehaviorKey, kFlex1ScaleToMinimum); host_->SetSize(Size(20, 50)); host_->Layout(); @@ -1128,10 +1244,10 @@ TEST_F(FlexLayoutTest, Layout_Flex_OneViewScales_BelowMinimum) { layout_->SetInteriorMargin(Insets(5)); layout_->SetMainAxisAlignment(LayoutAlignment::kStart); layout_->SetCrossAxisAlignment(LayoutAlignment::kStart); - layout_->SetDefaultChildMargins(gfx::Insets(5)); + layout_->SetDefault(views::kMarginsKey, gfx::Insets(5)); View* child1 = AddChild(Size(10, 20), Size(5, 5)); View* child2 = AddChild(Size(10, 10)); - layout_->SetFlexForView(child1, kFlex1ScaleToMinimum); + child1->SetProperty(views::kFlexBehaviorKey, kFlex1ScaleToMinimum); host_->SetSize(Size(20, 20)); host_->Layout(); @@ -1146,11 +1262,11 @@ TEST_F(FlexLayoutTest, layout_->SetInteriorMargin(Insets(5)); layout_->SetMainAxisAlignment(LayoutAlignment::kStart); layout_->SetCrossAxisAlignment(LayoutAlignment::kStart); - layout_->SetDefaultChildMargins(gfx::Insets(5)); + layout_->SetDefault(views::kMarginsKey, gfx::Insets(5)); View* child1 = AddChild(Size(10, 20), Size(5, 5)); View* child2 = AddChild(Size(10, 10), Size(5, 5)); - layout_->SetFlexForView(child1, kFlex1ScaleToMinimum); - layout_->SetFlexForView(child2, kDropOut); + child1->SetProperty(views::kFlexBehaviorKey, kFlex1ScaleToMinimum); + child2->SetProperty(views::kFlexBehaviorKey, kDropOut); host_->SetSize(Size(20, 20)); host_->Layout(); @@ -1165,11 +1281,11 @@ TEST_F(FlexLayoutTest, layout_->SetInteriorMargin(Insets(5)); layout_->SetMainAxisAlignment(LayoutAlignment::kStart); layout_->SetCrossAxisAlignment(LayoutAlignment::kStart); - layout_->SetDefaultChildMargins(gfx::Insets(5)); + layout_->SetDefault(views::kMarginsKey, gfx::Insets(5)); View* child1 = AddChild(Size(10, 20), Size(5, 5)); View* child2 = AddChild(Size(10, 10), Size(5, 5)); - layout_->SetFlexForView(child1, kFlex1ScaleToMinimum); - layout_->SetFlexForView(child2, kFlex1ScaleToZero); + child1->SetProperty(views::kFlexBehaviorKey, kFlex1ScaleToMinimum); + child2->SetProperty(views::kFlexBehaviorKey, kFlex1ScaleToZero); host_->SetSize(Size(20, 19)); host_->Layout(); @@ -1185,8 +1301,8 @@ TEST_F(FlexLayoutTest, Layout_Flex_TwoChildViews_EqualWeight) { layout_->SetInteriorMargin(Insets(5)); layout_->SetMainAxisAlignment(LayoutAlignment::kStart); layout_->SetCrossAxisAlignment(LayoutAlignment::kStart); - layout_->SetDefaultChildMargins(gfx::Insets(5)); - layout_->SetDefaultFlex(kFlex1ScaleToMinimum); + layout_->SetDefault(views::kMarginsKey, gfx::Insets(5)); + layout_->SetDefault(views::kFlexBehaviorKey, kFlex1ScaleToMinimum); View* child1 = AddChild(Size(20, 10), Size(5, 5)); View* child2 = AddChild(Size(20, 10), Size(5, 5)); @@ -1207,8 +1323,8 @@ TEST_F(FlexLayoutTest, Layout_Flex_TwoChildViews_DefaultFlex) { layout_->SetInteriorMargin(Insets(5)); layout_->SetMainAxisAlignment(LayoutAlignment::kStart); layout_->SetCrossAxisAlignment(LayoutAlignment::kStart); - layout_->SetDefaultChildMargins(gfx::Insets(5)); - layout_->SetDefaultFlex(kFlex1ScaleToMinimum); + layout_->SetDefault(views::kMarginsKey, gfx::Insets(5)); + layout_->SetDefault(views::kFlexBehaviorKey, kFlex1ScaleToMinimum); View* child1 = AddChild(Size(20, 10), Size(5, 5)); View* child2 = AddChild(Size(20, 10), Size(5, 5)); @@ -1229,11 +1345,11 @@ TEST_F(FlexLayoutTest, Layout_Flex_TwoChildViews_UnequalWeight) { layout_->SetInteriorMargin(Insets(5)); layout_->SetMainAxisAlignment(LayoutAlignment::kStart); layout_->SetCrossAxisAlignment(LayoutAlignment::kStart); - layout_->SetDefaultChildMargins(gfx::Insets(5)); + layout_->SetDefault(views::kMarginsKey, gfx::Insets(5)); View* child1 = AddChild(Size(20, 10), Size(5, 5)); View* child2 = AddChild(Size(20, 10), Size(5, 5)); - layout_->SetFlexForView(child1, kFlex1ScaleToMinimum); - layout_->SetFlexForView(child2, kFlex2ScaleToMinimum); + child1->SetProperty(views::kFlexBehaviorKey, kFlex1ScaleToMinimum); + child2->SetProperty(views::kFlexBehaviorKey, kFlex2ScaleToMinimum); host_->SetSize(Size(45, 20)); host_->Layout(); @@ -1247,11 +1363,11 @@ TEST_F(FlexLayoutTest, Layout_Flex_TwoChildViews_UnequalWeight_FirstAtMax) { layout_->SetInteriorMargin(Insets(5)); layout_->SetMainAxisAlignment(LayoutAlignment::kStart); layout_->SetCrossAxisAlignment(LayoutAlignment::kStart); - layout_->SetDefaultChildMargins(gfx::Insets(5)); + layout_->SetDefault(views::kMarginsKey, gfx::Insets(5)); View* child1 = AddChild(Size(20, 10), Size(5, 5)); View* child2 = AddChild(Size(20, 10), Size(5, 5)); - layout_->SetFlexForView(child1, kFlex2ScaleToMinimum); - layout_->SetFlexForView(child2, kFlex1ScaleToMinimum); + child1->SetProperty(views::kFlexBehaviorKey, kFlex2ScaleToMinimum); + child2->SetProperty(views::kFlexBehaviorKey, kFlex1ScaleToMinimum); host_->SetSize(Size(50, 20)); host_->Layout(); @@ -1265,11 +1381,11 @@ TEST_F(FlexLayoutTest, Layout_Flex_TwoChildViews_UnequalWeight_SecondAtMax) { layout_->SetInteriorMargin(Insets(5)); layout_->SetMainAxisAlignment(LayoutAlignment::kStart); layout_->SetCrossAxisAlignment(LayoutAlignment::kStart); - layout_->SetDefaultChildMargins(gfx::Insets(5)); + layout_->SetDefault(views::kMarginsKey, gfx::Insets(5)); View* child1 = AddChild(Size(20, 10), Size(5, 5)); View* child2 = AddChild(Size(20, 10), Size(5, 5)); - layout_->SetFlexForView(child1, kFlex1ScaleToMinimum); - layout_->SetFlexForView(child2, kFlex2ScaleToMinimum); + child1->SetProperty(views::kFlexBehaviorKey, kFlex1ScaleToMinimum); + child2->SetProperty(views::kFlexBehaviorKey, kFlex2ScaleToMinimum); host_->SetSize(Size(50, 20)); host_->Layout(); @@ -1286,11 +1402,12 @@ TEST_F(FlexLayoutTest, Layout_Flex_TwoChildViews_Priority) { layout_->SetInteriorMargin(Insets(5)); layout_->SetMainAxisAlignment(LayoutAlignment::kStart); layout_->SetCrossAxisAlignment(LayoutAlignment::kStart); - layout_->SetDefaultChildMargins(gfx::Insets(5)); + layout_->SetDefault(views::kMarginsKey, gfx::Insets(5)); View* child1 = AddChild(Size(20, 10), Size(5, 5)); View* child2 = AddChild(Size(20, 10), Size(5, 5)); - layout_->SetFlexForView(child1, kFlex1ScaleToMinimum); - layout_->SetFlexForView(child2, kFlex1ScaleToMinimumHighPriority); + child1->SetProperty(views::kFlexBehaviorKey, kFlex1ScaleToMinimum); + child2->SetProperty(views::kFlexBehaviorKey, + kFlex1ScaleToMinimumHighPriority); host_->SetSize(Size(50, 20)); host_->Layout(); @@ -1310,11 +1427,12 @@ TEST_F(FlexLayoutTest, layout_->SetInteriorMargin(Insets(5)); layout_->SetMainAxisAlignment(LayoutAlignment::kStart); layout_->SetCrossAxisAlignment(LayoutAlignment::kStart); - layout_->SetDefaultChildMargins(gfx::Insets(5)); + layout_->SetDefault(views::kMarginsKey, gfx::Insets(5)); View* child1 = AddChild(Size(20, 10), Size(5, 5)); View* child2 = AddChild(Size(20, 10), Size(5, 5)); - layout_->SetFlexForView(child1, kFlex1ScaleToZero); - layout_->SetFlexForView(child2, kFlex1ScaleToMinimumHighPriority); + child1->SetProperty(views::kFlexBehaviorKey, kFlex1ScaleToZero); + child2->SetProperty(views::kFlexBehaviorKey, + kFlex1ScaleToMinimumHighPriority); host_->SetSize(Size(35, 20)); host_->Layout(); @@ -1328,9 +1446,9 @@ TEST_F(FlexLayoutTest, Layout_FlexRule_UnboundedSnapToMinimum) { layout_->SetInteriorMargin(Insets(5)); layout_->SetMainAxisAlignment(LayoutAlignment::kStart); layout_->SetCrossAxisAlignment(LayoutAlignment::kStart); - layout_->SetDefaultChildMargins(gfx::Insets(5)); + layout_->SetDefault(views::kMarginsKey, gfx::Insets(5)); View* child = AddChild(Size(20, 10), Size(5, 5)); - layout_->SetFlexForView(child, kUnboundedSnapToMinimum); + child->SetProperty(views::kFlexBehaviorKey, kUnboundedSnapToMinimum); host_->SetSize(Size(35, 25)); host_->Layout(); @@ -1361,9 +1479,10 @@ TEST_F(FlexLayoutTest, Layout_FlexRule_UnboundedScaleToMinimumSnapToZero) { layout_->SetInteriorMargin(Insets(5)); layout_->SetMainAxisAlignment(LayoutAlignment::kStart); layout_->SetCrossAxisAlignment(LayoutAlignment::kStart); - layout_->SetDefaultChildMargins(gfx::Insets(5)); + layout_->SetDefault(views::kMarginsKey, gfx::Insets(5)); View* child = AddChild(Size(20, 10), Size(5, 5)); - layout_->SetFlexForView(child, kUnboundedScaleToMinimumSnapToZero); + child->SetProperty(views::kFlexBehaviorKey, + kUnboundedScaleToMinimumSnapToZero); host_->SetSize(Size(35, 25)); host_->Layout(); @@ -1403,11 +1522,11 @@ TEST_F(FlexLayoutTest, Layout_FlexRule_UnboundedScaleToZero) { layout_->SetInteriorMargin(Insets(5)); layout_->SetMainAxisAlignment(LayoutAlignment::kStart); layout_->SetCrossAxisAlignment(LayoutAlignment::kStart); - layout_->SetDefaultChildMargins(gfx::Insets(5)); + layout_->SetDefault(views::kMarginsKey, gfx::Insets(5)); // Because we are using a flex rule that scales all the way to zero, ensure // that the child view's minimum size is *not* respected. View* child = AddChild(Size(20, 10), Size(5, 5)); - layout_->SetFlexForView(child, kUnboundedScaleToZero); + child->SetProperty(views::kFlexBehaviorKey, kUnboundedScaleToZero); host_->SetSize(Size(35, 25)); host_->Layout(); @@ -1456,9 +1575,10 @@ TEST_F(FlexLayoutTest, layout_->SetMainAxisAlignment(LayoutAlignment::kStart); layout_->SetCrossAxisAlignment(LayoutAlignment::kStart); View* child1 = AddChild(kLargeSize, kSmallSize); - layout_->SetFlexForView(child1, kUnboundedScaleToMinimumHighPriority); + child1->SetProperty(views::kFlexBehaviorKey, + kUnboundedScaleToMinimumHighPriority); View* child2 = AddChild(kSmallSize); - layout_->SetFlexForView(child2, kDropOut); + child2->SetProperty(views::kFlexBehaviorKey, kDropOut); // When there is no room for the second view, it drops out. host_->SetSize(Size(4, 5)); @@ -1494,9 +1614,10 @@ TEST_F(FlexLayoutTest, Layout_FlexRule_TwoPassScaling_StopAtPreferredSize) { layout_->SetMainAxisAlignment(LayoutAlignment::kStart); layout_->SetCrossAxisAlignment(LayoutAlignment::kStart); View* child1 = AddChild(kLargeSize, kSmallSize); - layout_->SetFlexForView(child1, kUnboundedScaleToMinimumHighPriority); + child1->SetProperty(views::kFlexBehaviorKey, + kUnboundedScaleToMinimumHighPriority); View* child2 = AddChild(kSmallSize); - layout_->SetFlexForView(child2, kDropOut); + child2->SetProperty(views::kFlexBehaviorKey, kDropOut); constexpr Size kEnoughSpace(kSmallSize.width() + kLargeSize.width(), kLargeSize.height()); @@ -1517,9 +1638,10 @@ TEST_F(FlexLayoutTest, Layout_FlexRule_TwoPassScaling_GrowPastPreferredSize) { layout_->SetMainAxisAlignment(LayoutAlignment::kStart); layout_->SetCrossAxisAlignment(LayoutAlignment::kStart); View* child1 = AddChild(kLargeSize, kSmallSize); - layout_->SetFlexForView(child1, kUnboundedScaleToMinimumHighPriority); + child1->SetProperty(views::kFlexBehaviorKey, + kUnboundedScaleToMinimumHighPriority); View* child2 = AddChild(kSmallSize); - layout_->SetFlexForView(child2, kDropOut); + child2->SetProperty(views::kFlexBehaviorKey, kDropOut); constexpr int kExtra = 7; constexpr Size kExtraSpace(kSmallSize.width() + kLargeSize.width() + kExtra, @@ -1544,9 +1666,11 @@ TEST_F(FlexLayoutTest, // Because we are using a flex rule that scales all the way to zero, ensure // that the child view's minimum size is *not* respected. View* child1 = AddChild(kLargeSize, kSmallSize); - layout_->SetFlexForView(child1, kUnboundedScaleToMinimumHighPriority); + child1->SetProperty(views::kFlexBehaviorKey, + kUnboundedScaleToMinimumHighPriority); View* child2 = AddChild(kLargeSize, kSmallSize); - layout_->SetFlexForView(child2, kUnboundedScaleToMinimumHighPriority); + child2->SetProperty(views::kFlexBehaviorKey, + kUnboundedScaleToMinimumHighPriority); constexpr int kExtra = 8; constexpr Size kExtraSpace(2 * kLargeSize.width() + kExtra, @@ -1572,9 +1696,10 @@ TEST_F(FlexLayoutTest, // Because we are using a flex rule that scales all the way to zero, ensure // that the child view's minimum size is *not* respected. View* child1 = AddChild(kLargeSize, kSmallSize); - layout_->SetFlexForView(child1, kUnboundedScaleToMinimumHighPriority); + child1->SetProperty(views::kFlexBehaviorKey, + kUnboundedScaleToMinimumHighPriority); View* child2 = AddChild(kLargeSize, kSmallSize); - layout_->SetFlexForView(child2, kUnboundedScaleToMinimum); + child2->SetProperty(views::kFlexBehaviorKey, kUnboundedScaleToMinimum); constexpr int kExtra = 8; constexpr Size kExtraSpace(2 * kLargeSize.width() + kExtra, @@ -1594,9 +1719,9 @@ TEST_F(FlexLayoutTest, Layout_FlexRule_CustomFlexRule) { layout_->SetInteriorMargin(Insets(5)); layout_->SetMainAxisAlignment(LayoutAlignment::kStart); layout_->SetCrossAxisAlignment(LayoutAlignment::kStart); - layout_->SetDefaultChildMargins(gfx::Insets(5)); + layout_->SetDefault(views::kMarginsKey, gfx::Insets(5)); View* child = AddChild(Size(kFullSize, kFullSize)); - layout_->SetFlexForView(child, kCustomFlex); + child->SetProperty(views::kFlexBehaviorKey, kCustomFlex); host_->SetSize(Size(100, 100)); host_->Layout(); @@ -1629,10 +1754,10 @@ TEST_F(FlexLayoutTest, Layout_FlexRule_CustomFlexRule_WithNonFlex) { layout_->SetInteriorMargin(Insets(5)); layout_->SetMainAxisAlignment(LayoutAlignment::kStart); layout_->SetCrossAxisAlignment(LayoutAlignment::kStart); - layout_->SetDefaultChildMargins(gfx::Insets(5)); + layout_->SetDefault(views::kMarginsKey, gfx::Insets(5)); View* child = AddChild(Size(kFullSize, kFullSize)); AddChild(Size(10, 10)); - layout_->SetFlexForView(child, kCustomFlex); + child->SetProperty(views::kFlexBehaviorKey, kCustomFlex); host_->SetSize(Size(100, 100)); host_->Layout(); @@ -1660,9 +1785,9 @@ TEST_F(FlexLayoutTest, Layout_FlexRule_CustomFlexRule_ShrinkToZero) { layout_->SetInteriorMargin(Insets(5)); layout_->SetMainAxisAlignment(LayoutAlignment::kStart); layout_->SetCrossAxisAlignment(LayoutAlignment::kStart); - layout_->SetDefaultChildMargins(gfx::Insets(5)); + layout_->SetDefault(views::kMarginsKey, gfx::Insets(5)); View* child = AddChild(Size(kFullSize, kFullSize)); - layout_->SetFlexForView(child, kCustomFlexSnapToZero); + child->SetProperty(views::kFlexBehaviorKey, kCustomFlexSnapToZero); host_->SetSize(Size(100, 100)); host_->Layout(); @@ -1691,11 +1816,12 @@ TEST_F(FlexLayoutTest, Layout_OnlyCallsSetViewVisibilityWhenNecessary) { layout_->SetInteriorMargin(Insets(5)); layout_->SetMainAxisAlignment(LayoutAlignment::kStart); layout_->SetCrossAxisAlignment(LayoutAlignment::kStart); - layout_->SetDefaultChildMargins(gfx::Insets(5)); + layout_->SetDefault(views::kMarginsKey, gfx::Insets(5)); MockView* child1 = AddChild(Size(20, 10), Size(5, 5)); MockView* child2 = AddChild(Size(20, 10), Size(5, 5)); - layout_->SetFlexForView(child1, kFlex1ScaleToZero); - layout_->SetFlexForView(child2, kFlex1ScaleToMinimumHighPriority); + child1->SetProperty(views::kFlexBehaviorKey, kFlex1ScaleToZero); + child2->SetProperty(views::kFlexBehaviorKey, + kFlex1ScaleToMinimumHighPriority); child1->ResetCounts(); child2->ResetCounts(); @@ -1747,7 +1873,7 @@ class FlexLayoutCrossAxisFitTest : public FlexLayoutTest { for (size_t i = 0; i < kNumChildren; ++i) { View* const child = AddChild(kChildSizes[i]); - child->SetProperty(kMarginsKey, new gfx::Insets(kChildMargins[i])); + child->SetProperty(kMarginsKey, gfx::Insets(kChildMargins[i])); child_views_.push_back(child); } @@ -1889,19 +2015,19 @@ TEST_F(NestedFlexLayoutTest, Layout_OppositeOrientation) { layout_->SetOrientation(LayoutOrientation::kHorizontal) .SetCollapseMargins(false) .SetCrossAxisAlignment(LayoutAlignment::kStart) - .SetDefaultChildMargins(gfx::Insets(2, 3, 4, 5)) + .SetDefault(views::kMarginsKey, gfx::Insets(2, 3, 4, 5)) .SetInteriorMargin(gfx::Insets(4, 3, 2, 1)); layout(1) ->SetOrientation(LayoutOrientation::kVertical) .SetCollapseMargins(true) - .SetDefaultChildMargins(gfx::Insets(2)) + .SetDefault(views::kMarginsKey, gfx::Insets(2)) .SetInteriorMargin(gfx::Insets(1)); layout(2) ->SetOrientation(LayoutOrientation::kVertical) .SetCollapseMargins(true) - .SetDefaultChildMargins(gfx::Insets(1)) + .SetDefault(views::kMarginsKey, gfx::Insets(1)) .SetInteriorMargin(gfx::Insets(2)); EXPECT_EQ(gfx::Size(39, 29), host_->GetPreferredSize()); @@ -1925,19 +2051,19 @@ TEST_F(NestedFlexLayoutTest, Layout_SameOrientation) { layout_->SetOrientation(LayoutOrientation::kHorizontal) .SetCollapseMargins(false) .SetCrossAxisAlignment(LayoutAlignment::kStart) - .SetDefaultChildMargins(gfx::Insets(2, 3, 4, 5)) + .SetDefault(views::kMarginsKey, gfx::Insets(2, 3, 4, 5)) .SetInteriorMargin(gfx::Insets(4, 3, 2, 1)); layout(1) ->SetOrientation(LayoutOrientation::kHorizontal) .SetCollapseMargins(true) - .SetDefaultChildMargins(gfx::Insets(2)) + .SetDefault(views::kMarginsKey, gfx::Insets(2)) .SetInteriorMargin(gfx::Insets(1)); layout(2) ->SetOrientation(LayoutOrientation::kHorizontal) .SetCollapseMargins(true) - .SetDefaultChildMargins(gfx::Insets(1)) + .SetDefault(views::kMarginsKey, gfx::Insets(1)) .SetInteriorMargin(gfx::Insets(2)); EXPECT_EQ(gfx::Size(53, 22), host_->GetPreferredSize()); @@ -1964,26 +2090,26 @@ TEST_F(NestedFlexLayoutTest, Layout_Flex) { layout_->SetOrientation(LayoutOrientation::kHorizontal) .SetCollapseMargins(true) .SetCrossAxisAlignment(LayoutAlignment::kStart) - .SetDefaultChildMargins(gfx::Insets(2)) - .SetInteriorMargin(gfx::Insets(2)) - .SetFlexForView(child(1), flex_specification) - .SetFlexForView(child(2), flex_specification); + .SetDefault(views::kMarginsKey, gfx::Insets(2)) + .SetInteriorMargin(gfx::Insets(2)); + child(1)->SetProperty(views::kFlexBehaviorKey, flex_specification); + child(2)->SetProperty(views::kFlexBehaviorKey, flex_specification); layout(1) ->SetOrientation(LayoutOrientation::kHorizontal) .SetCollapseMargins(true) - .SetDefaultChildMargins(gfx::Insets(2)) - .SetInteriorMargin(gfx::Insets(2)) - .SetFlexForView(grandchild(1, 1), flex_specification) - .SetFlexForView(grandchild(1, 2), flex_specification); + .SetDefault(views::kMarginsKey, gfx::Insets(2)) + .SetInteriorMargin(gfx::Insets(2)); + grandchild(1, 1)->SetProperty(views::kFlexBehaviorKey, flex_specification); + grandchild(1, 2)->SetProperty(views::kFlexBehaviorKey, flex_specification); layout(2) ->SetOrientation(LayoutOrientation::kHorizontal) .SetCollapseMargins(true) - .SetDefaultChildMargins(gfx::Insets(2)) - .SetInteriorMargin(gfx::Insets(2)) - .SetFlexForView(grandchild(2, 1), flex_specification) - .SetFlexForView(grandchild(2, 2), flex_specification); + .SetDefault(views::kMarginsKey, gfx::Insets(2)) + .SetInteriorMargin(gfx::Insets(2)); + grandchild(2, 1)->SetProperty(views::kFlexBehaviorKey, flex_specification); + grandchild(2, 2)->SetProperty(views::kFlexBehaviorKey, flex_specification); EXPECT_EQ(gfx::Size(40, 14), host_->GetPreferredSize()); host_->SetSize(gfx::Size(20, 15)); diff --git a/chromium/ui/views/layout/grid_layout.cc b/chromium/ui/views/layout/grid_layout.cc index c556543755b..9056d526f79 100644 --- a/chromium/ui/views/layout/grid_layout.cc +++ b/chromium/ui/views/layout/grid_layout.cc @@ -516,7 +516,7 @@ void ColumnSet::AccumulateMasterColumns() { DCHECK(master_columns_.empty()); for (const auto& column : columns_) { Column* master_column = column->GetLastMasterColumn(); - if (master_column && !base::ContainsValue(master_columns_, master_column)) { + if (master_column && !base::Contains(master_columns_, master_column)) { master_columns_.push_back(master_column); } // At this point, GetLastMasterColumn may not == master_column @@ -772,9 +772,7 @@ bool ColumnSet::CanUseMinimum(const ViewState& view_state) const { // GridLayout ------------------------------------------------------------- -GridLayout::GridLayout(View* host) : host_(host) { - DCHECK(host); -} +GridLayout::GridLayout() = default; GridLayout::~GridLayout() = default; @@ -818,32 +816,64 @@ void GridLayout::SkipColumns(int col_count) { SkipPaddingColumns(); } -void GridLayout::AddView(View* view) { - AddView(view, 1, 1); +void GridLayout::AddExistingView(View* view, int col_span, int row_span) { + DCHECK(view->parent() && view->parent() == host_) + << "Use AddView() to add a new View that isn't already parented to " + "|host_|."; + DCHECK(current_row_col_set_ && + next_column_ < current_row_col_set_->num_columns()); + Column* column = current_row_col_set_->columns_[next_column_].get(); + AddExistingView(view, col_span, row_span, column->h_align(), + column->v_align()); } -void GridLayout::AddView(View* view, int col_span, int row_span) { +void GridLayout::AddViewImpl(std::unique_ptr<View> view, + int col_span, + int row_span) { DCHECK(current_row_col_set_ && next_column_ < current_row_col_set_->num_columns()); Column* column = current_row_col_set_->columns_[next_column_].get(); - AddView(view, col_span, row_span, column->h_align(), column->v_align()); + AddViewImpl(std::move(view), col_span, row_span, column->h_align(), + column->v_align(), 0, 0); } -void GridLayout::AddView(View* view, int col_span, int row_span, - Alignment h_align, Alignment v_align) { - AddView(view, col_span, row_span, h_align, v_align, 0, 0); +void GridLayout::AddExistingView(View* view, + int col_span, + int row_span, + Alignment h_align, + Alignment v_align, + int pref_width, + int pref_height) { + DCHECK(view->parent() && view->parent() == host_) + << "Use AddView() to add a new View that isn't already parented to " + "|host_|."; + DCHECK(current_row_col_set_ && col_span > 0 && row_span > 0 && + (next_column_ + col_span) <= current_row_col_set_->num_columns()); + // We don't support baseline alignment of views spanning rows. Please add if + // you need it. + DCHECK(v_align != BASELINE || row_span == 1); + AddViewState(std::make_unique<ViewState>( + current_row_col_set_, view, next_column_, current_row_, col_span, + row_span, h_align, v_align, pref_width, pref_height)); } -void GridLayout::AddView(View* view, int col_span, int row_span, - Alignment h_align, Alignment v_align, - int pref_width, int pref_height) { +void GridLayout::AddViewImpl(std::unique_ptr<View> view, + int col_span, + int row_span, + Alignment h_align, + Alignment v_align, + int pref_width, + int pref_height) { DCHECK(current_row_col_set_ && col_span > 0 && row_span > 0 && (next_column_ + col_span) <= current_row_col_set_->num_columns()); // We don't support baseline alignment of views spanning rows. Please add if // you need it. DCHECK(v_align != BASELINE || row_span == 1); + adding_view_ = true; + View* view_ptr = host_->AddChildView(std::move(view)); + adding_view_ = false; AddViewState(std::make_unique<ViewState>( - current_row_col_set_, view, next_column_, current_row_, col_span, + current_row_col_set_, view_ptr, next_column_, current_row_, col_span, row_span, h_align, v_align, pref_width, pref_height)); } @@ -872,7 +902,7 @@ static void CalculateSize(int pref_size, GridLayout::Alignment alignment, } void GridLayout::Installed(View* host) { - DCHECK(host_ == host); + host_ = host; } void GridLayout::ViewAdded(View* host, View* view) { @@ -1047,13 +1077,7 @@ void GridLayout::CalculateMasterColumnsIfNecessary() const { } void GridLayout::AddViewState(std::unique_ptr<ViewState> view_state) { - DCHECK(view_state->view && (view_state->view->parent() == nullptr || - view_state->view->parent() == host_)); - if (!view_state->view->parent()) { - adding_view_ = true; - host_->AddChildView(view_state->view); - adding_view_ = false; - } + DCHECK(view_state->view && view_state->view->parent() == host_); remaining_row_span_ = std::max(remaining_row_span_, view_state->row_span); next_column_ += view_state->col_span; current_row_col_set_->AddViewState(view_state.get()); diff --git a/chromium/ui/views/layout/grid_layout.h b/chromium/ui/views/layout/grid_layout.h index cd046c70163..3d591436a53 100644 --- a/chromium/ui/views/layout/grid_layout.h +++ b/chromium/ui/views/layout/grid_layout.h @@ -111,7 +111,7 @@ class VIEWS_EXPORT GridLayout : public LayoutManager { USE_PREF }; - explicit GridLayout(View* host); + GridLayout(); ~GridLayout() override; // See class description for what this does. @@ -145,31 +145,50 @@ class VIEWS_EXPORT GridLayout : public LayoutManager { // contain any views. void SkipColumns(int col_count); - // Adds a view using the default alignment from the column. The added - // view has a column and row span of 1. - // As a convenience this adds the view to the host. The view becomes owned - // by the host, and NOT this GridLayout. - void AddView(View* view); - // Adds a view using the default alignment from the column. // As a convenience this adds the view to the host. The view becomes owned // by the host, and NOT this GridLayout. - void AddView(View* view, int col_span, int row_span); + template <typename T> + T* AddView(std::unique_ptr<T> view, int col_span = 1, int row_span = 1) { + T* result = view.get(); + AddViewImpl(std::move(view), col_span, row_span); + return result; + } - // Adds a view with the specified alignment and spans. - // As a convenience this adds the view to the host. The view becomes owned - // by the host, and NOT this GridLayout. - void AddView(View* view, int col_span, int row_span, Alignment h_align, - Alignment v_align); + // Adds a view to the layout using the default alignment from the column. + // NOTE: The |view| must already be present and owned by the host. + void AddExistingView(View* view, int col_span = 1, int row_span = 1); // Adds a view with the specified alignment and spans. If // pref_width/pref_height is > 0 then the preferred width/height of the view // is fixed to the specified value. // As a convenience this adds the view to the host. The view becomes owned // by the host, and NOT this GridLayout. - void AddView(View* view, int col_span, int row_span, - Alignment h_align, Alignment v_align, - int pref_width, int pref_height); + template <typename T> + T* AddView(std::unique_ptr<T> view, + int col_span, + int row_span, + Alignment h_align, + Alignment v_align, + int pref_width = 0, + int pref_height = 0) { + T* result = view.get(); + AddViewImpl(std::move(view), col_span, row_span, h_align, v_align, + pref_width, pref_height); + return result; + } + + // Adds a view to the layout with the specified alignment and spans. If + // pref_width/pref_height is > 0 then the preferred width/height of the view + // is fixed to the specified value. + // NOTE: The |view| must already be present and owned by the host; + void AddExistingView(View* view, + int col_span, + int row_span, + Alignment h_align, + Alignment v_align, + int pref_width = 0, + int pref_height = 0); // Notification we've been installed on a particular host. Checks that host // is the same as the View supplied in the constructor. @@ -205,8 +224,20 @@ class VIEWS_EXPORT GridLayout : public LayoutManager { // a description of what a master column is. void CalculateMasterColumnsIfNecessary() const; - // This is called internally from AddView. It adds the ViewState to the - // appropriate structures, and updates internal fields such as next_column_. + // These are called internally from AddView<T>. + void AddViewImpl(std::unique_ptr<View> view, int col_span, int row_span); + + void AddViewImpl(std::unique_ptr<View> view, + int col_span, + int row_span, + Alignment h_align, + Alignment v_align, + int pref_width, + int pref_height); + + // This is called internally from AddView & AddViewState above. It adds the + // ViewState to the appropriate structures and updates the internal fields + // such as next_column_. void AddViewState(std::unique_ptr<ViewState> view_state); // Adds the Row to rows_, as well as updating next_column_, @@ -228,8 +259,8 @@ class VIEWS_EXPORT GridLayout : public LayoutManager { // Returns the column set of the last non-padding row. ColumnSet* GetLastValidColumnSet(); - // The view we were created with. We don't own this. - View* const host_; + // The View this is installed on. + View* host_ = nullptr; // Whether or not we've calculated the master/linked columns. mutable bool calculated_master_columns_ = false; diff --git a/chromium/ui/views/layout/grid_layout_unittest.cc b/chromium/ui/views/layout/grid_layout_unittest.cc index b35944a52b2..8b4dc47fba7 100644 --- a/chromium/ui/views/layout/grid_layout_unittest.cc +++ b/chromium/ui/views/layout/grid_layout_unittest.cc @@ -4,6 +4,9 @@ #include "ui/views/layout/grid_layout.h" +#include <memory> +#include <utility> + #include "base/compiler_specific.h" #include "base/test/gtest_util.h" #include "testing/gtest/include/gtest/gtest.h" @@ -21,8 +24,8 @@ void ExpectViewBoundsEquals(int x, int y, int w, int h, EXPECT_EQ(h, view->height()); } -View* CreateSizedView(const gfx::Size& size) { - auto* view = new View(); +std::unique_ptr<View> CreateSizedView(const gfx::Size& size) { + auto view = std::make_unique<View>(); view->SetPreferredSize(size); return view; } @@ -42,8 +45,9 @@ class MinSizeView : public View { DISALLOW_COPY_AND_ASSIGN(MinSizeView); }; -View* CreateViewWithMinAndPref(const gfx::Size& min, const gfx::Size& pref) { - MinSizeView* view = new MinSizeView(min); +std::unique_ptr<MinSizeView> CreateViewWithMinAndPref(const gfx::Size& min, + const gfx::Size& pref) { + auto view = std::make_unique<MinSizeView>(min); view->SetPreferredSize(pref); return view; } @@ -99,52 +103,47 @@ class FlexibleView : public View { class GridLayoutTest : public testing::Test { public: - GridLayoutTest() { - layout_ = - host_.SetLayoutManager(std::make_unique<views::GridLayout>(&host_)); + GridLayoutTest() : host_(std::make_unique<View>()) { + layout_ = host_->SetLayoutManager(std::make_unique<views::GridLayout>()); } - void RemoveAll() { host_.RemoveAllChildViews(false); } - - gfx::Size GetPreferredSize() { return layout_->GetPreferredSize(&host_); } + gfx::Size GetPreferredSize() { + return layout_->GetPreferredSize(host_.get()); + } - View& host() { return host_; } + View* host() { return host_.get(); } GridLayout* layout() { return layout_; } private: - View host_; + std::unique_ptr<View> host_; GridLayout* layout_; }; class GridLayoutAlignmentTest : public testing::Test { public: - GridLayoutAlignmentTest() { - layout_ = - host_.SetLayoutManager(std::make_unique<views::GridLayout>(&host_)); - v1_.SetPreferredSize(gfx::Size(10, 20)); + GridLayoutAlignmentTest() : host_(std::make_unique<View>()) { + layout_ = host_->SetLayoutManager(std::make_unique<views::GridLayout>()); } - void RemoveAll() { host_.RemoveAllChildViews(false); } - void TestAlignment(GridLayout::Alignment alignment, gfx::Rect* bounds) { + 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); layout_->StartRow(1, 0); - layout_->AddView(&v1_); - gfx::Size pref = layout_->GetPreferredSize(&host_); + auto* v1_ptr = layout_->AddView(std::move(v1)); + gfx::Size pref = layout_->GetPreferredSize(host_.get()); EXPECT_EQ(gfx::Size(10, 20), pref); - host_.SetBounds(0, 0, 100, 100); - layout_->Layout(&host_); - *bounds = v1_.bounds(); - RemoveAll(); + host_->SetBounds(0, 0, 100, 100); + layout_->Layout(host_.get()); + *bounds = v1_ptr->bounds(); } - View& host() { return host_; } + View* host() { return host_.get(); } GridLayout* layout() { return layout_; } private: - View host_; - View v1_; + std::unique_ptr<View> host_; GridLayout* layout_; }; @@ -173,38 +172,28 @@ TEST_F(GridLayoutAlignmentTest, Trailing) { } TEST_F(GridLayoutTest, TwoColumns) { - View v1; - v1.SetPreferredSize(gfx::Size(10, 20)); - View v2; - v2.SetPreferredSize(gfx::Size(20, 20)); + auto v1 = CreateSizedView(gfx::Size(10, 20)); + auto v2 = CreateSizedView(gfx::Size(20, 20)); ColumnSet* c1 = layout()->AddColumnSet(0); c1->AddColumn(GridLayout::LEADING, GridLayout::LEADING, 0, GridLayout::USE_PREF, 0, 0); c1->AddColumn(GridLayout::LEADING, GridLayout::LEADING, 0, GridLayout::USE_PREF, 0, 0); layout()->StartRow(0, 0); - layout()->AddView(&v1); - layout()->AddView(&v2); + auto* v1_ptr = layout()->AddView(std::move(v1)); + auto* v2_ptr = layout()->AddView(std::move(v2)); gfx::Size pref = GetPreferredSize(); EXPECT_EQ(gfx::Size(30, 20), pref); - host().SetBounds(0, 0, pref.width(), pref.height()); - layout()->Layout(&host()); - ExpectViewBoundsEquals(0, 0, 10, 20, &v1); - ExpectViewBoundsEquals(10, 0, 20, 20, &v2); - - RemoveAll(); + host()->SetBounds(0, 0, pref.width(), pref.height()); + layout()->Layout(host()); + ExpectViewBoundsEquals(0, 0, 10, 20, v1_ptr); + ExpectViewBoundsEquals(10, 0, 20, 20, v2_ptr); } // Test linked column sizes, and the column size limit. TEST_F(GridLayoutTest, LinkedSizes) { - View v1; - v1.SetPreferredSize(gfx::Size(10, 20)); - View v2; - v2.SetPreferredSize(gfx::Size(20, 20)); - View v3; - v3.SetPreferredSize(gfx::Size(0, 20)); ColumnSet* c1 = layout()->AddColumnSet(0); // Fill widths. @@ -216,9 +205,9 @@ TEST_F(GridLayoutTest, LinkedSizes) { 0, 0); layout()->StartRow(0, 0); - layout()->AddView(&v1); - layout()->AddView(&v2); - layout()->AddView(&v3); + auto* v1 = layout()->AddView(CreateSizedView(gfx::Size(10, 20))); + auto* v2 = layout()->AddView(CreateSizedView(gfx::Size(20, 20))); + auto* v3 = layout()->AddView(CreateSizedView(gfx::Size(0, 20))); // Link all the columns. c1->LinkColumnSizes(0, 1, 2, -1); @@ -227,130 +216,108 @@ TEST_F(GridLayoutTest, LinkedSizes) { // |v1| and |v3| should obtain the same width as |v2|, since |v2| is largest. pref = GetPreferredSize(); EXPECT_EQ(gfx::Size(20 + 20 + 20, 20), pref); - host().SetBounds(0, 0, pref.width(), pref.height()); - layout()->Layout(&host()); - ExpectViewBoundsEquals(0, 0, 20, 20, &v1); - ExpectViewBoundsEquals(20, 0, 20, 20, &v2); - ExpectViewBoundsEquals(40, 0, 20, 20, &v3); + host()->SetBounds(0, 0, pref.width(), pref.height()); + layout()->Layout(host()); + ExpectViewBoundsEquals(0, 0, 20, 20, v1); + ExpectViewBoundsEquals(20, 0, 20, 20, v2); + ExpectViewBoundsEquals(40, 0, 20, 20, v3); // If the limit is zero, behaves as though the columns are not linked. c1->set_linked_column_size_limit(0); pref = GetPreferredSize(); EXPECT_EQ(gfx::Size(10 + 20 + 0, 20), pref); - host().SetBounds(0, 0, pref.width(), pref.height()); - layout()->Layout(&host()); - ExpectViewBoundsEquals(0, 0, 10, 20, &v1); - ExpectViewBoundsEquals(10, 0, 20, 20, &v2); - ExpectViewBoundsEquals(30, 0, 0, 20, &v3); + host()->SetBounds(0, 0, pref.width(), pref.height()); + layout()->Layout(host()); + ExpectViewBoundsEquals(0, 0, 10, 20, v1); + ExpectViewBoundsEquals(10, 0, 20, 20, v2); + ExpectViewBoundsEquals(30, 0, 0, 20, v3); // Set a size limit. c1->set_linked_column_size_limit(40); - v1.SetPreferredSize(gfx::Size(35, 20)); + v1->SetPreferredSize(gfx::Size(35, 20)); // |v1| now dominates, but it is still below the limit. pref = GetPreferredSize(); EXPECT_EQ(gfx::Size(35 + 35 + 35, 20), pref); - host().SetBounds(0, 0, pref.width(), pref.height()); - layout()->Layout(&host()); - ExpectViewBoundsEquals(0, 0, 35, 20, &v1); - ExpectViewBoundsEquals(35, 0, 35, 20, &v2); - ExpectViewBoundsEquals(70, 0, 35, 20, &v3); + host()->SetBounds(0, 0, pref.width(), pref.height()); + layout()->Layout(host()); + ExpectViewBoundsEquals(0, 0, 35, 20, v1); + ExpectViewBoundsEquals(35, 0, 35, 20, v2); + ExpectViewBoundsEquals(70, 0, 35, 20, v3); // Go over the limit. |v1| shouldn't influence size at all, but the others // should still be linked to the next largest width. - v1.SetPreferredSize(gfx::Size(45, 20)); + v1->SetPreferredSize(gfx::Size(45, 20)); pref = GetPreferredSize(); EXPECT_EQ(gfx::Size(45 + 20 + 20, 20), pref); - host().SetBounds(0, 0, pref.width(), pref.height()); - layout()->Layout(&host()); - ExpectViewBoundsEquals(0, 0, 45, 20, &v1); - ExpectViewBoundsEquals(45, 0, 20, 20, &v2); - ExpectViewBoundsEquals(65, 0, 20, 20, &v3); - - RemoveAll(); + host()->SetBounds(0, 0, pref.width(), pref.height()); + layout()->Layout(host()); + ExpectViewBoundsEquals(0, 0, 45, 20, v1); + ExpectViewBoundsEquals(45, 0, 20, 20, v2); + ExpectViewBoundsEquals(65, 0, 20, 20, v3); } TEST_F(GridLayoutTest, ColSpan1) { - View v1; - v1.SetPreferredSize(gfx::Size(100, 20)); - View v2; - v2.SetPreferredSize(gfx::Size(10, 40)); ColumnSet* c1 = layout()->AddColumnSet(0); c1->AddColumn(GridLayout::LEADING, GridLayout::LEADING, 0, GridLayout::USE_PREF, 0, 0); c1->AddColumn(GridLayout::LEADING, GridLayout::LEADING, 1, GridLayout::USE_PREF, 0, 0); layout()->StartRow(0, 0); - layout()->AddView(&v1, 2, 1); + auto* v1 = layout()->AddView(CreateSizedView(gfx::Size(100, 20)), 2, 1); layout()->StartRow(0, 0); - layout()->AddView(&v2); + auto* v2 = layout()->AddView(CreateSizedView(gfx::Size(10, 40))); gfx::Size pref = GetPreferredSize(); EXPECT_EQ(gfx::Size(100, 60), pref); - host().SetBounds(0, 0, pref.width(), pref.height()); - layout()->Layout(&host()); - ExpectViewBoundsEquals(0, 0, 100, 20, &v1); - ExpectViewBoundsEquals(0, 20, 10, 40, &v2); - - RemoveAll(); + host()->SetBounds(0, 0, pref.width(), pref.height()); + layout()->Layout(host()); + ExpectViewBoundsEquals(0, 0, 100, 20, v1); + ExpectViewBoundsEquals(0, 20, 10, 40, v2); } TEST_F(GridLayoutTest, ColSpan2) { - View v1; - v1.SetPreferredSize(gfx::Size(100, 20)); - View v2; - v2.SetPreferredSize(gfx::Size(10, 20)); ColumnSet* c1 = layout()->AddColumnSet(0); c1->AddColumn(GridLayout::LEADING, GridLayout::LEADING, 1, GridLayout::USE_PREF, 0, 0); c1->AddColumn(GridLayout::LEADING, GridLayout::LEADING, 0, GridLayout::USE_PREF, 0, 0); layout()->StartRow(0, 0); - layout()->AddView(&v1, 2, 1); + auto* v1 = layout()->AddView(CreateSizedView(gfx::Size(100, 20)), 2, 1); layout()->StartRow(0, 0); layout()->SkipColumns(1); - layout()->AddView(&v2); + auto* v2 = layout()->AddView(CreateSizedView(gfx::Size(10, 20))); gfx::Size pref = GetPreferredSize(); EXPECT_EQ(gfx::Size(100, 40), pref); - host().SetBounds(0, 0, pref.width(), pref.height()); - layout()->Layout(&host()); - ExpectViewBoundsEquals(0, 0, 100, 20, &v1); - ExpectViewBoundsEquals(90, 20, 10, 20, &v2); - - RemoveAll(); + host()->SetBounds(0, 0, pref.width(), pref.height()); + layout()->Layout(host()); + ExpectViewBoundsEquals(0, 0, 100, 20, v1); + ExpectViewBoundsEquals(90, 20, 10, 20, v2); } TEST_F(GridLayoutTest, ColSpan3) { - View v1; - v1.SetPreferredSize(gfx::Size(100, 20)); - View v2; - v2.SetPreferredSize(gfx::Size(10, 20)); - View v3; - v3.SetPreferredSize(gfx::Size(10, 20)); ColumnSet* c1 = layout()->AddColumnSet(0); c1->AddColumn(GridLayout::LEADING, GridLayout::LEADING, 0, GridLayout::USE_PREF, 0, 0); c1->AddColumn(GridLayout::LEADING, GridLayout::LEADING, 0, GridLayout::USE_PREF, 0, 0); layout()->StartRow(0, 0); - layout()->AddView(&v1, 2, 1); + auto* v1 = layout()->AddView(CreateSizedView(gfx::Size(100, 20)), 2, 1); layout()->StartRow(0, 0); - layout()->AddView(&v2); - layout()->AddView(&v3); + auto* v2 = layout()->AddView(CreateSizedView(gfx::Size(10, 20))); + auto* v3 = layout()->AddView(CreateSizedView(gfx::Size(10, 20))); gfx::Size pref = GetPreferredSize(); EXPECT_EQ(gfx::Size(100, 40), pref); - host().SetBounds(0, 0, pref.width(), pref.height()); - layout()->Layout(&host()); - ExpectViewBoundsEquals(0, 0, 100, 20, &v1); - ExpectViewBoundsEquals(0, 20, 10, 20, &v2); - ExpectViewBoundsEquals(50, 20, 10, 20, &v3); - - RemoveAll(); + host()->SetBounds(0, 0, pref.width(), pref.height()); + layout()->Layout(host()); + ExpectViewBoundsEquals(0, 0, 100, 20, v1); + ExpectViewBoundsEquals(0, 20, 10, 20, v2); + ExpectViewBoundsEquals(50, 20, 10, 20, v3); } @@ -362,28 +329,20 @@ TEST_F(GridLayoutTest, ColSpan4) { set->AddColumn(GridLayout::LEADING, GridLayout::LEADING, 0, GridLayout::USE_PREF, 0, 0); - View v1; - v1.SetPreferredSize(gfx::Size(10, 10)); - View v2; - v2.SetPreferredSize(gfx::Size(10, 10)); - View v3; - v3.SetPreferredSize(gfx::Size(25, 20)); layout()->StartRow(0, 0); - layout()->AddView(&v1); - layout()->AddView(&v2); + auto* v1 = layout()->AddView(CreateSizedView(gfx::Size(10, 10))); + auto* v2 = layout()->AddView(CreateSizedView(gfx::Size(10, 10))); layout()->StartRow(0, 0); - layout()->AddView(&v3, 2, 1); + auto* v3 = layout()->AddView(CreateSizedView(gfx::Size(25, 20)), 2, 1); gfx::Size pref = GetPreferredSize(); EXPECT_EQ(gfx::Size(25, 30), pref); - host().SetBounds(0, 0, pref.width(), pref.height()); - layout()->Layout(&host()); - ExpectViewBoundsEquals(0, 0, 10, 10, &v1); - ExpectViewBoundsEquals(12, 0, 10, 10, &v2); - ExpectViewBoundsEquals(0, 10, 25, 20, &v3); - - RemoveAll(); + host()->SetBounds(0, 0, pref.width(), pref.height()); + layout()->Layout(host()); + ExpectViewBoundsEquals(0, 0, 10, 10, v1); + ExpectViewBoundsEquals(12, 0, 10, 10, v2); + ExpectViewBoundsEquals(0, 10, 25, 20, v3); } // Verifies the sizing of a view that doesn't start in the first column @@ -398,31 +357,20 @@ TEST_F(GridLayoutTest, ColSpanStartSecondColumn) { set->AddColumn(GridLayout::FILL, GridLayout::FILL, 0, GridLayout::FIXED, 10, 0); - View v1; - v1.SetPreferredSize(gfx::Size(10, 10)); - View v2; - v2.SetPreferredSize(gfx::Size(20, 10)); - layout()->StartRow(0, 0); - layout()->AddView(&v1); - layout()->AddView(&v2, 2, 1); + auto* v1 = layout()->AddView(CreateSizedView(gfx::Size(10, 10))); + auto* v2 = layout()->AddView(CreateSizedView(gfx::Size(20, 10)), 2, 1); gfx::Size pref = GetPreferredSize(); EXPECT_EQ(gfx::Size(30, 10), pref); - host().SetBounds(0, 0, pref.width(), pref.height()); - layout()->Layout(&host()); - ExpectViewBoundsEquals(0, 0, 10, 10, &v1); - ExpectViewBoundsEquals(10, 0, 20, 10, &v2); - - RemoveAll(); + host()->SetBounds(0, 0, pref.width(), pref.height()); + layout()->Layout(host()); + ExpectViewBoundsEquals(0, 0, 10, 10, v1); + ExpectViewBoundsEquals(10, 0, 20, 10, v2); } TEST_F(GridLayoutTest, SameSizeColumns) { - View v1; - v1.SetPreferredSize(gfx::Size(50, 20)); - View v2; - v2.SetPreferredSize(gfx::Size(10, 10)); ColumnSet* c1 = layout()->AddColumnSet(0); c1->AddColumn(GridLayout::LEADING, GridLayout::LEADING, 0, GridLayout::USE_PREF, 0, 0); @@ -430,73 +378,53 @@ TEST_F(GridLayoutTest, SameSizeColumns) { 0, GridLayout::USE_PREF, 0, 0); c1->LinkColumnSizes(0, 1, -1); layout()->StartRow(0, 0); - layout()->AddView(&v1); - layout()->AddView(&v2); + auto* v1 = layout()->AddView(CreateSizedView(gfx::Size(50, 20))); + auto* v2 = layout()->AddView(CreateSizedView(gfx::Size(10, 10))); gfx::Size pref = GetPreferredSize(); EXPECT_EQ(gfx::Size(100, 20), pref); - host().SetBounds(0, 0, pref.width(), pref.height()); - layout()->Layout(&host()); - ExpectViewBoundsEquals(0, 0, 50, 20, &v1); - ExpectViewBoundsEquals(50, 0, 10, 10, &v2); - - RemoveAll(); + host()->SetBounds(0, 0, pref.width(), pref.height()); + layout()->Layout(host()); + ExpectViewBoundsEquals(0, 0, 50, 20, v1); + ExpectViewBoundsEquals(50, 0, 10, 10, v2); } TEST_F(GridLayoutTest, HorizontalResizeTest1) { - View v1; - v1.SetPreferredSize(gfx::Size(50, 20)); - View v2; - v2.SetPreferredSize(gfx::Size(10, 10)); ColumnSet* c1 = layout()->AddColumnSet(0); c1->AddColumn(GridLayout::FILL, GridLayout::LEADING, 1, GridLayout::USE_PREF, 0, 0); c1->AddColumn(GridLayout::LEADING, GridLayout::LEADING, 0, GridLayout::USE_PREF, 0, 0); layout()->StartRow(0, 0); - layout()->AddView(&v1); - layout()->AddView(&v2); + auto* v1 = layout()->AddView(CreateSizedView(gfx::Size(50, 20))); + auto* v2 = layout()->AddView(CreateSizedView(gfx::Size(10, 10))); - host().SetBounds(0, 0, 110, 20); - layout()->Layout(&host()); - ExpectViewBoundsEquals(0, 0, 100, 20, &v1); - ExpectViewBoundsEquals(100, 0, 10, 10, &v2); - - RemoveAll(); + host()->SetBounds(0, 0, 110, 20); + layout()->Layout(host()); + ExpectViewBoundsEquals(0, 0, 100, 20, v1); + ExpectViewBoundsEquals(100, 0, 10, 10, v2); } TEST_F(GridLayoutTest, HorizontalResizeTest2) { - View v1; - v1.SetPreferredSize(gfx::Size(50, 20)); - View v2; - v2.SetPreferredSize(gfx::Size(10, 10)); ColumnSet* c1 = layout()->AddColumnSet(0); c1->AddColumn(GridLayout::FILL, GridLayout::LEADING, 1, GridLayout::USE_PREF, 0, 0); c1->AddColumn(GridLayout::TRAILING, GridLayout::LEADING, 1, GridLayout::USE_PREF, 0, 0); layout()->StartRow(0, 0); - layout()->AddView(&v1); - layout()->AddView(&v2); - - host().SetBounds(0, 0, 120, 20); - layout()->Layout(&host()); - ExpectViewBoundsEquals(0, 0, 80, 20, &v1); - ExpectViewBoundsEquals(110, 0, 10, 10, &v2); + auto* v1 = layout()->AddView(CreateSizedView(gfx::Size(50, 20))); + auto* v2 = layout()->AddView(CreateSizedView(gfx::Size(10, 10))); - RemoveAll(); + host()->SetBounds(0, 0, 120, 20); + layout()->Layout(host()); + ExpectViewBoundsEquals(0, 0, 80, 20, v1); + ExpectViewBoundsEquals(110, 0, 10, 10, v2); } // Tests that space leftover due to rounding is distributed to the last // resizable column. TEST_F(GridLayoutTest, HorizontalResizeTest3) { - View v1; - v1.SetPreferredSize(gfx::Size(10, 10)); - View v2; - v2.SetPreferredSize(gfx::Size(10, 10)); - View v3; - v3.SetPreferredSize(gfx::Size(10, 10)); ColumnSet* c1 = layout()->AddColumnSet(0); c1->AddColumn(GridLayout::FILL, GridLayout::LEADING, 1, GridLayout::USE_PREF, 0, 0); @@ -505,65 +433,53 @@ TEST_F(GridLayoutTest, HorizontalResizeTest3) { c1->AddColumn(GridLayout::TRAILING, GridLayout::LEADING, 0, GridLayout::USE_PREF, 0, 0); layout()->StartRow(0, 0); - layout()->AddView(&v1); - layout()->AddView(&v2); - layout()->AddView(&v3); - - host().SetBounds(0, 0, 31, 10); - layout()->Layout(&host()); - ExpectViewBoundsEquals(0, 0, 10, 10, &v1); - ExpectViewBoundsEquals(10, 0, 11, 10, &v2); - ExpectViewBoundsEquals(21, 0, 10, 10, &v3); + auto* v1 = layout()->AddView(CreateSizedView(gfx::Size(10, 10))); + auto* v2 = layout()->AddView(CreateSizedView(gfx::Size(10, 10))); + auto* v3 = layout()->AddView(CreateSizedView(gfx::Size(10, 10))); - RemoveAll(); + host()->SetBounds(0, 0, 31, 10); + layout()->Layout(host()); + ExpectViewBoundsEquals(0, 0, 10, 10, v1); + ExpectViewBoundsEquals(10, 0, 11, 10, v2); + ExpectViewBoundsEquals(21, 0, 10, 10, v3); } TEST_F(GridLayoutTest, TestVerticalResize1) { - View v1; - v1.SetPreferredSize(gfx::Size(50, 20)); - View v2; - v2.SetPreferredSize(gfx::Size(10, 10)); ColumnSet* c1 = layout()->AddColumnSet(0); c1->AddColumn(GridLayout::FILL, GridLayout::FILL, 1, GridLayout::USE_PREF, 0, 0); layout()->StartRow(1, 0); - layout()->AddView(&v1); + auto* v1 = layout()->AddView(CreateSizedView(gfx::Size(50, 20))); layout()->StartRow(0, 0); - layout()->AddView(&v2); + auto* v2 = layout()->AddView(CreateSizedView(gfx::Size(10, 10))); gfx::Size pref = GetPreferredSize(); EXPECT_EQ(gfx::Size(50, 30), pref); - host().SetBounds(0, 0, 50, 100); - layout()->Layout(&host()); - ExpectViewBoundsEquals(0, 0, 50, 90, &v1); - ExpectViewBoundsEquals(0, 90, 50, 10, &v2); - - RemoveAll(); + host()->SetBounds(0, 0, 50, 100); + layout()->Layout(host()); + ExpectViewBoundsEquals(0, 0, 50, 90, v1); + ExpectViewBoundsEquals(0, 90, 50, 10, v2); } TEST_F(GridLayoutTest, Border) { - host().SetBorder(CreateEmptyBorder(1, 2, 3, 4)); - View v1; - v1.SetPreferredSize(gfx::Size(10, 20)); + host()->SetBorder(CreateEmptyBorder(1, 2, 3, 4)); ColumnSet* c1 = layout()->AddColumnSet(0); c1->AddColumn(GridLayout::LEADING, GridLayout::LEADING, 0, GridLayout::USE_PREF, 0, 0); layout()->StartRow(0, 0); - layout()->AddView(&v1); + auto* v1 = layout()->AddView(CreateSizedView(gfx::Size(10, 20))); gfx::Size pref = GetPreferredSize(); EXPECT_EQ(gfx::Size(16, 24), pref); - host().SetBounds(0, 0, pref.width(), pref.height()); - layout()->Layout(&host()); - ExpectViewBoundsEquals(2, 1, 10, 20, &v1); - - RemoveAll(); + host()->SetBounds(0, 0, pref.width(), pref.height()); + layout()->Layout(host()); + ExpectViewBoundsEquals(2, 1, 10, 20, v1); } TEST_F(GridLayoutTest, FixedSize) { - host().SetBorder(CreateEmptyBorder(2, 2, 2, 2)); + host()->SetBorder(CreateEmptyBorder(2, 2, 2, 2)); ColumnSet* set = layout()->AddColumnSet(0); @@ -584,9 +500,9 @@ TEST_F(GridLayoutTest, FixedSize) { layout()->AddView(CreateSizedView(gfx::Size(kPrefWidth, kPrefHeight))); } - layout()->Layout(&host()); + layout()->Layout(host()); - auto i = host().children().cbegin(); + auto i = host()->children().cbegin(); for (size_t row = 0; row < kRowCount; ++row) { for (size_t column = 0; column < kColumnCount; ++column, ++i) { ExpectViewBoundsEquals( @@ -635,14 +551,13 @@ TEST_F(GridLayoutTest, RowSpan) { layout()->AddView(CreateSizedView(gfx::Size(20, 10))); layout()->AddView(CreateSizedView(gfx::Size(20, 40)), 1, 2); layout()->StartRow(1, 0); - View* s3 = CreateSizedView(gfx::Size(20, 10)); - layout()->AddView(s3); + auto* s3 = layout()->AddView(CreateSizedView(gfx::Size(20, 10))); gfx::Size pref = GetPreferredSize(); EXPECT_EQ(gfx::Size(40, 40), pref); - host().SetBounds(0, 0, pref.width(), pref.height()); - layout()->Layout(&host()); + host()->SetBounds(0, 0, pref.width(), pref.height()); + layout()->Layout(host()); ExpectViewBoundsEquals(0, 10, 20, 10, s3); } @@ -656,8 +571,7 @@ TEST_F(GridLayoutTest, RowSpan2) { layout()->StartRow(0, 0); layout()->AddView(CreateSizedView(gfx::Size(20, 20))); - View* s3 = CreateSizedView(gfx::Size(64, 64)); - layout()->AddView(s3, 1, 3); + auto* s3 = layout()->AddView(CreateSizedView(gfx::Size(64, 64)), 1, 3); layout()->AddPaddingRow(0, 10); @@ -667,8 +581,8 @@ TEST_F(GridLayoutTest, RowSpan2) { gfx::Size pref = GetPreferredSize(); EXPECT_EQ(gfx::Size(84, 64), pref); - host().SetBounds(0, 0, pref.width(), pref.height()); - layout()->Layout(&host()); + host()->SetBounds(0, 0, pref.width(), pref.height()); + layout()->Layout(host()); ExpectViewBoundsEquals(20, 0, 64, 64, s3); } @@ -681,16 +595,16 @@ TEST_F(GridLayoutTest, FixedViewWidth) { 0,GridLayout::USE_PREF, 0, 0); layout()->StartRow(0, 0); - View* view = CreateSizedView(gfx::Size(30, 40)); - layout()->AddView(view, 1, 1, GridLayout::LEADING, GridLayout::LEADING, 10, - 0); + auto* view = + layout()->AddView(CreateSizedView(gfx::Size(30, 40)), 1, 1, + GridLayout::LEADING, GridLayout::LEADING, 10, 0); gfx::Size pref = GetPreferredSize(); EXPECT_EQ(10, pref.width()); EXPECT_EQ(40, pref.height()); - host().SetBounds(0, 0, pref.width(), pref.height()); - layout()->Layout(&host()); + host()->SetBounds(0, 0, pref.width(), pref.height()); + layout()->Layout(host()); ExpectViewBoundsEquals(0, 0, 10, 40, view); } @@ -703,16 +617,16 @@ TEST_F(GridLayoutTest, FixedViewHeight) { 0,GridLayout::USE_PREF, 0, 0); layout()->StartRow(0, 0); - View* view = CreateSizedView(gfx::Size(30, 40)); - layout()->AddView(view, 1, 1, GridLayout::LEADING, GridLayout::LEADING, 0, - 10); + auto* view = + layout()->AddView(CreateSizedView(gfx::Size(30, 40)), 1, 1, + GridLayout::LEADING, GridLayout::LEADING, 0, 10); gfx::Size pref = GetPreferredSize(); EXPECT_EQ(30, pref.width()); EXPECT_EQ(10, pref.height()); - host().SetBounds(0, 0, pref.width(), pref.height()); - layout()->Layout(&host()); + host()->SetBounds(0, 0, pref.width(), pref.height()); + layout()->Layout(host()); ExpectViewBoundsEquals(0, 0, 30, 10, view); } @@ -728,17 +642,15 @@ TEST_F(GridLayoutTest, ColumnSpanResizing) { layout()->StartRow(0, 0); // span_view spans two columns and is twice as big the views added below. - View* span_view = CreateSizedView(gfx::Size(12, 40)); - layout()->AddView(span_view, 2, 1, GridLayout::LEADING, GridLayout::LEADING); + View* span_view = layout()->AddView(CreateSizedView(gfx::Size(12, 40)), 2, 1, + GridLayout::LEADING, GridLayout::LEADING); layout()->StartRow(0, 0); - View* view1 = CreateSizedView(gfx::Size(2, 40)); - View* view2 = CreateSizedView(gfx::Size(4, 40)); - layout()->AddView(view1); - layout()->AddView(view2); + auto* view1 = layout()->AddView(CreateSizedView(gfx::Size(2, 40))); + auto* view2 = layout()->AddView(CreateSizedView(gfx::Size(4, 40))); - host().SetBounds(0, 0, 12, 80); - layout()->Layout(&host()); + host()->SetBounds(0, 0, 12, 80); + layout()->Layout(host()); ExpectViewBoundsEquals(0, 0, 12, 40, span_view); @@ -770,20 +682,20 @@ TEST_F(GridLayoutTest, ColumnResizingOnGetPreferredSize) { // Make a row containing a flexible view that trades width for height. layout()->StartRow(0, 0); - View* view1 = new FlexibleView(100); - layout()->AddView(view1, 1, 1, GridLayout::FILL, GridLayout::LEADING); + layout()->AddView(std::make_unique<FlexibleView>(100), 1, 1, GridLayout::FILL, + GridLayout::LEADING); // The second row contains a view of fixed size that will enforce a column // width of 20 pixels. layout()->StartRow(0, 1); - View* view2 = CreateSizedView(gfx::Size(20, 20)); - layout()->AddView(view2, 1, 1, GridLayout::FILL, GridLayout::LEADING); + layout()->AddView(CreateSizedView(gfx::Size(20, 20)), 1, 1, GridLayout::FILL, + GridLayout::LEADING); // Add another flexible view in row three in order to ensure column set // ordering doesn't influence sizing behaviour. layout()->StartRow(0, 2); - View* view3 = new FlexibleView(40); - layout()->AddView(view3, 1, 1, GridLayout::FILL, GridLayout::LEADING); + layout()->AddView(std::make_unique<FlexibleView>(40), 1, 1, GridLayout::FILL, + GridLayout::LEADING); // We expect a height of 50: 30 from the variable width view in the first row // plus 20 from the statically sized view in the second row. The flexible @@ -792,13 +704,11 @@ TEST_F(GridLayoutTest, ColumnResizingOnGetPreferredSize) { } TEST_F(GridLayoutTest, MinimumPreferredSize) { - View v1; - v1.SetPreferredSize(gfx::Size(10, 20)); ColumnSet* set = layout()->AddColumnSet(0); set->AddColumn(GridLayout::FILL, GridLayout::FILL, 0, GridLayout::USE_PREF, 0, 0); layout()->StartRow(0, 0); - layout()->AddView(&v1); + layout()->AddView(CreateSizedView(gfx::Size(10, 20))); gfx::Size pref = GetPreferredSize(); EXPECT_EQ(gfx::Size(10, 20), pref); @@ -806,8 +716,6 @@ TEST_F(GridLayoutTest, MinimumPreferredSize) { layout()->set_minimum_size(gfx::Size(40, 40)); pref = GetPreferredSize(); EXPECT_EQ(gfx::Size(40, 40), pref); - - RemoveAll(); } // Test that attempting a Layout() while nested in AddView() causes a DCHECK. @@ -818,17 +726,15 @@ TEST_F(GridLayoutTest, LayoutOnAddDeath) { set->AddColumn(GridLayout::FILL, GridLayout::FILL, 0, GridLayout::USE_PREF, 0, 0); layout()->StartRow(0, 0); - LayoutOnAddView view; - EXPECT_DCHECK_DEATH(layout()->AddView(&view)); + auto view = std::make_unique<LayoutOnAddView>(); + EXPECT_DCHECK_DEATH(layout()->AddView(std::move(view))); // Death tests use fork(), so nothing should be added here. - EXPECT_FALSE(view.parent()); + EXPECT_FALSE(view->parent()); // If the View has nothing to change, adding should succeed. - view.set_target_size(view.GetPreferredSize()); - layout()->AddView(&view); - EXPECT_TRUE(view.parent()); - - RemoveAll(); + view->set_target_size(view->GetPreferredSize()); + auto* view_ptr = layout()->AddView(std::move(view)); + EXPECT_TRUE(view_ptr->parent()); } TEST_F(GridLayoutTest, ColumnMinForcesPreferredWidth) { @@ -838,8 +744,7 @@ TEST_F(GridLayoutTest, ColumnMinForcesPreferredWidth) { set->AddColumn(GridLayout::FILL, GridLayout::FILL, 5, GridLayout::USE_PREF, 0, 100); layout()->StartRow(0, 0); - View* view1 = CreateSizedView(gfx::Size(20, 10)); - layout()->AddView(view1); + layout()->AddView(CreateSizedView(gfx::Size(20, 10))); EXPECT_EQ(gfx::Size(100, 10), GetPreferredSize()); } @@ -855,26 +760,25 @@ TEST_F(GridLayoutTest, HonorsColumnMin) { set->AddColumn(GridLayout::FILL, GridLayout::FILL, 5, GridLayout::USE_PREF, 0, 0); layout()->StartRow(0, 0); - View* view1 = CreateViewWithMinAndPref(gfx::Size(10, 10), gfx::Size(125, 10)); - layout()->AddView(view1); - - View* view2 = CreateViewWithMinAndPref(gfx::Size(10, 10), gfx::Size(50, 10)); - layout()->AddView(view2); + View* view1 = layout()->AddView( + CreateViewWithMinAndPref(gfx::Size(10, 10), gfx::Size(125, 10))); + View* view2 = layout()->AddView( + CreateViewWithMinAndPref(gfx::Size(10, 10), gfx::Size(50, 10))); EXPECT_EQ(gfx::Size(175, 10), GetPreferredSize()); - host().SetBounds(0, 0, 175, 0); - layout()->Layout(&host()); + host()->SetBounds(0, 0, 175, 0); + layout()->Layout(host()); EXPECT_EQ(gfx::Rect(0, 0, 125, 10), view1->bounds()); EXPECT_EQ(gfx::Rect(125, 0, 50, 10), view2->bounds()); - host().SetBounds(0, 0, 125, 0); - layout()->Layout(&host()); + host()->SetBounds(0, 0, 125, 0); + layout()->Layout(host()); EXPECT_EQ(gfx::Rect(0, 0, 100, 10), view1->bounds()); EXPECT_EQ(gfx::Rect(100, 0, 25, 10), view2->bounds()); - host().SetBounds(0, 0, 120, 0); - layout()->Layout(&host()); + host()->SetBounds(0, 0, 120, 0); + layout()->Layout(host()); EXPECT_EQ(gfx::Rect(0, 0, 100, 10), view1->bounds()); EXPECT_EQ(gfx::Rect(100, 0, 20, 10), view2->bounds()); } @@ -889,15 +793,13 @@ TEST_F(GridLayoutTest, TwoViewsOneSizeSmallerThanMinimum) { set->AddColumn(GridLayout::FILL, GridLayout::FILL, 5, GridLayout::USE_PREF, 0, 0); layout()->StartRow(0, 0); - View* view1 = CreateViewWithMinAndPref(gfx::Size(20, 10), gfx::Size(100, 10)); - layout()->AddView(view1); - - View* view2 = - CreateViewWithMinAndPref(gfx::Size(100, 10), gfx::Size(100, 10)); - layout()->AddView(view2); + View* view1 = layout()->AddView( + CreateViewWithMinAndPref(gfx::Size(20, 10), gfx::Size(100, 10))); + View* view2 = layout()->AddView( + CreateViewWithMinAndPref(gfx::Size(100, 10), gfx::Size(100, 10))); - host().SetBounds(0, 0, 110, 0); - layout()->Layout(&host()); + host()->SetBounds(0, 0, 110, 0); + layout()->Layout(host()); EXPECT_EQ(gfx::Rect(0, 0, 20, 10), view1->bounds()); EXPECT_EQ(gfx::Rect(20, 0, 100, 10), view2->bounds()); } @@ -912,44 +814,43 @@ TEST_F(GridLayoutTest, TwoViewsBothSmallerThanMinimumDifferentResizeWeights) { set->AddColumn(GridLayout::FILL, GridLayout::FILL, 2, GridLayout::USE_PREF, 0, 0); layout()->StartRow(0, 0); - View* view1 = CreateViewWithMinAndPref(gfx::Size(91, 10), gfx::Size(100, 10)); - layout()->AddView(view1); - - View* view2 = CreateViewWithMinAndPref(gfx::Size(10, 10), gfx::Size(100, 10)); - layout()->AddView(view2); + View* view1 = layout()->AddView( + CreateViewWithMinAndPref(gfx::Size(91, 10), gfx::Size(100, 10))); + View* view2 = layout()->AddView( + CreateViewWithMinAndPref(gfx::Size(10, 10), gfx::Size(100, 10))); // 200 is the preferred, each should get their preferred width. - host().SetBounds(0, 0, 200, 0); - layout()->Layout(&host()); + host()->SetBounds(0, 0, 200, 0); + layout()->Layout(host()); EXPECT_EQ(gfx::Rect(0, 0, 100, 10), view1->bounds()); EXPECT_EQ(gfx::Rect(100, 0, 100, 10), view2->bounds()); // 1 pixel smaller than pref. - host().SetBounds(0, 0, 199, 0); - layout()->Layout(&host()); + host()->SetBounds(0, 0, 199, 0); + layout()->Layout(host()); EXPECT_EQ(gfx::Rect(0, 0, 99, 10), view1->bounds()); EXPECT_EQ(gfx::Rect(99, 0, 100, 10), view2->bounds()); // 10 pixels smaller than pref. - host().SetBounds(0, 0, 190, 0); - layout()->Layout(&host()); + host()->SetBounds(0, 0, 190, 0); + layout()->Layout(host()); EXPECT_EQ(gfx::Rect(0, 0, 92, 10), view1->bounds()); EXPECT_EQ(gfx::Rect(92, 0, 98, 10), view2->bounds()); // 11 pixels smaller than pref. - host().SetBounds(0, 0, 189, 0); - layout()->Layout(&host()); + host()->SetBounds(0, 0, 189, 0); + layout()->Layout(host()); EXPECT_EQ(gfx::Rect(0, 0, 91, 10), view1->bounds()); EXPECT_EQ(gfx::Rect(91, 0, 98, 10), view2->bounds()); // 12 pixels smaller than pref. - host().SetBounds(0, 0, 188, 0); - layout()->Layout(&host()); + host()->SetBounds(0, 0, 188, 0); + layout()->Layout(host()); EXPECT_EQ(gfx::Rect(0, 0, 91, 10), view1->bounds()); EXPECT_EQ(gfx::Rect(91, 0, 97, 10), view2->bounds()); - host().SetBounds(0, 0, 5, 0); - layout()->Layout(&host()); + host()->SetBounds(0, 0, 5, 0); + layout()->Layout(host()); EXPECT_EQ(gfx::Rect(0, 0, 91, 10), view1->bounds()); EXPECT_EQ(gfx::Rect(91, 0, 10, 10), view2->bounds()); } @@ -962,19 +863,19 @@ TEST_F(GridLayoutTest, TwoViewsOneColumnUsePrefOtherFixed) { set->AddColumn(GridLayout::FILL, GridLayout::FILL, 2, GridLayout::FIXED, 100, 0); layout()->StartRow(0, 0); - View* view1 = CreateViewWithMinAndPref(gfx::Size(10, 10), gfx::Size(100, 10)); - layout()->AddView(view1); - View* view2 = CreateViewWithMinAndPref(gfx::Size(10, 10), gfx::Size(100, 10)); - layout()->AddView(view2); + View* view1 = layout()->AddView( + CreateViewWithMinAndPref(gfx::Size(10, 10), gfx::Size(100, 10))); + View* view2 = layout()->AddView( + CreateViewWithMinAndPref(gfx::Size(10, 10), gfx::Size(100, 10))); - host().SetBounds(0, 0, 120, 0); - layout()->Layout(&host()); + host()->SetBounds(0, 0, 120, 0); + layout()->Layout(host()); EXPECT_EQ(gfx::Rect(0, 0, 20, 10), view1->bounds()); // Even though column 2 has a resize percent, it's FIXED, so it won't shrink. EXPECT_EQ(gfx::Rect(20, 0, 100, 10), view2->bounds()); - host().SetBounds(0, 0, 10, 0); - layout()->Layout(&host()); + host()->SetBounds(0, 0, 10, 0); + layout()->Layout(host()); EXPECT_EQ(gfx::Rect(0, 0, 10, 10), view1->bounds()); EXPECT_EQ(gfx::Rect(10, 0, 100, 10), view2->bounds()); } @@ -987,23 +888,24 @@ TEST_F(GridLayoutTest, TwoViewsBothColumnsResizableOneViewFixedWidthMin) { set->AddColumn(GridLayout::FILL, GridLayout::FILL, 1, GridLayout::USE_PREF, 0, 0); layout()->StartRow(0, 0); - View* view1 = CreateViewWithMinAndPref(gfx::Size(10, 10), gfx::Size(100, 10)); - layout()->AddView(view1); - View* view2 = CreateViewWithMinAndPref(gfx::Size(10, 10), gfx::Size(100, 10)); - layout()->AddView(view2, 1, 1, GridLayout::FILL, GridLayout::FILL, 50, 10); + View* view1 = layout()->AddView( + CreateViewWithMinAndPref(gfx::Size(10, 10), gfx::Size(100, 10))); + View* view2 = layout()->AddView( + CreateViewWithMinAndPref(gfx::Size(10, 10), gfx::Size(100, 10)), 1, 1, + GridLayout::FILL, GridLayout::FILL, 50, 10); - host().SetBounds(0, 0, 80, 0); - layout()->Layout(&host()); + host()->SetBounds(0, 0, 80, 0); + layout()->Layout(host()); EXPECT_EQ(gfx::Rect(0, 0, 30, 10), view1->bounds()); EXPECT_EQ(gfx::Rect(30, 0, 50, 10), view2->bounds()); - host().SetBounds(0, 0, 70, 0); - layout()->Layout(&host()); + host()->SetBounds(0, 0, 70, 0); + layout()->Layout(host()); EXPECT_EQ(gfx::Rect(0, 0, 20, 10), view1->bounds()); EXPECT_EQ(gfx::Rect(20, 0, 50, 10), view2->bounds()); - host().SetBounds(0, 0, 10, 0); - layout()->Layout(&host()); + host()->SetBounds(0, 0, 10, 0); + layout()->Layout(host()); EXPECT_EQ(gfx::Rect(0, 0, 10, 10), view1->bounds()); EXPECT_EQ(gfx::Rect(10, 0, 50, 10), view2->bounds()); } @@ -1028,15 +930,13 @@ TEST_F(GridLayoutTest, HeightForWidthCalledWhenNotGivenPreferredWidth) { 0, 0); layout()->StartRow(0, 0); const int pref_height = 100; - // |view| is owned by parent. - SettablePreferredHeightView* view = - new SettablePreferredHeightView(pref_height); + auto view = std::make_unique<SettablePreferredHeightView>(pref_height); const gfx::Size pref(10, 20); view->SetPreferredSize(pref); - layout()->AddView(view); + layout()->AddView(std::move(view)); EXPECT_EQ(pref, GetPreferredSize()); - EXPECT_EQ(pref_height, host().GetHeightForWidth(5)); + EXPECT_EQ(pref_height, host()->GetHeightForWidth(5)); } } // namespace views diff --git a/chromium/ui/views/layout/interpolating_layout_manager.cc b/chromium/ui/views/layout/interpolating_layout_manager.cc new file mode 100644 index 00000000000..496b934a108 --- /dev/null +++ b/chromium/ui/views/layout/interpolating_layout_manager.cc @@ -0,0 +1,251 @@ +// 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. + +#include "ui/views/layout/interpolating_layout_manager.h" + +#include <memory> +#include <utility> + +#include "ui/gfx/animation/tween.h" +#include "ui/views/view.h" + +namespace views { + +namespace { + +using ChildLayout = LayoutManagerBase::ChildLayout; +using ProposedLayout = LayoutManagerBase::ProposedLayout; + +// Returns a layout that's linearly interpolated between |first_layout| and +// |second_layout| by |percent|. See gfx::Tween::LinearIntValueBetween() for +// the exact math involved. +ProposedLayout Interpolate(double value, + const ProposedLayout& start, + const ProposedLayout& target) { + ProposedLayout layout; + const size_t num_children = start.child_layouts.size(); + DCHECK_EQ(num_children, target.child_layouts.size()); + + // Interpolate the host size. + // TODO(dfried): Add direct gfx::Size interpolation to gfx::Tween. + const gfx::Size size = + gfx::Tween::SizeValueBetween(value, start.host_size, target.host_size); + layout.host_size = gfx::Size(size.width(), size.height()); + + // Interpolate the child bounds. + for (size_t i = 0; i < num_children; ++i) { + const ChildLayout& start_child = start.child_layouts[i]; + const ChildLayout& target_child = target.child_layouts[i]; + DCHECK_EQ(start_child.child_view, target_child.child_view); + layout.child_layouts.emplace_back( + ChildLayout{start_child.child_view, + gfx::Tween::RectValueBetween(value, start_child.bounds, + target_child.bounds), + start_child.visible && target_child.visible}); + } + return layout; +} + +} // namespace + +InterpolatingLayoutManager::InterpolatingLayoutManager() {} +InterpolatingLayoutManager::~InterpolatingLayoutManager() = default; + +InterpolatingLayoutManager& InterpolatingLayoutManager::SetOrientation( + LayoutOrientation orientation) { + if (orientation_ != orientation) { + orientation_ = orientation; + InvalidateLayout(); + } + return *this; +} + +void InterpolatingLayoutManager::AddLayoutInternal( + std::unique_ptr<LayoutManagerBase> engine, + const Span& interpolation_range) { + DCHECK(engine); + + SyncStateTo(engine.get()); + auto result = embedded_layouts_.emplace( + std::make_pair(interpolation_range, std::move(engine))); + DCHECK(result.second) << "Cannot replace existing layout manager for " + << interpolation_range.ToString(); + +#if DCHECK_IS_ON() + // Sanity checking to ensure interpolation ranges do not overlap (we can only + // interpolate between two layouts currently). + auto next = result.first; + ++next; + if (next != embedded_layouts_.end()) + DCHECK_GE(next->first.start(), interpolation_range.end()); + if (result.first != embedded_layouts_.begin()) { + auto prev = result.first; + --prev; + DCHECK_LE(prev->first.end(), interpolation_range.start()); + } +#endif // DCHECK_IS_ON() +} + +InterpolatingLayoutManager::LayoutInterpolation +InterpolatingLayoutManager::GetInterpolation( + const SizeBounds& size_bounds) const { + DCHECK(!embedded_layouts_.empty()); + + LayoutInterpolation result; + + const base::Optional<int> dimension = + orientation_ == LayoutOrientation::kHorizontal ? size_bounds.width() + : size_bounds.height(); + + // Find the larger layout that overlaps the target size. + auto match = dimension ? embedded_layouts_.upper_bound({*dimension, 0}) + : embedded_layouts_.end(); + DCHECK(match != embedded_layouts_.begin()) + << "No layout set for primary dimension size " + << (dimension ? *dimension : -1) << "; first layout starts at " + << match->first.ToString(); + result.first = (--match)->second.get(); + + // If the target size falls in an interpolation range, get the other layout. + const Span& first_span = match->first; + if (dimension && first_span.end() > *dimension) { + DCHECK(match != embedded_layouts_.begin()) + << "Primary dimension size " << (dimension ? *dimension : -1) + << " falls into interpolation range " << match->first.ToString() + << " but there is no smaller layout to interpolate with."; + result.second = (--match)->second.get(); + result.percent_second = + float{first_span.end() - *dimension} / float{first_span.length()}; + } + + return result; +} + +LayoutManagerBase::ProposedLayout +InterpolatingLayoutManager::CalculateProposedLayout( + const SizeBounds& size_bounds) const { + // For interpolating layout we will never call this method except for fully- + // specified sizes. + DCHECK(size_bounds.width()); + DCHECK(size_bounds.height()); + const gfx::Size size(*size_bounds.width(), *size_bounds.height()); + + const LayoutInterpolation interpolation = GetInterpolation(size_bounds); + const ProposedLayout first = interpolation.first->GetProposedLayout(size); + + if (!interpolation.second) + return first; + + // If the target size falls in an interpolation range, get the other layout. + const ProposedLayout second = interpolation.second->GetProposedLayout(size); + return Interpolate(interpolation.percent_second, first, second); +} + +void InterpolatingLayoutManager::SetDefaultLayout( + LayoutManagerBase* default_layout) { + if (default_layout_ == default_layout) + return; + + // Make sure we already own the layout. + DCHECK(embedded_layouts_.end() != + std::find_if(embedded_layouts_.begin(), embedded_layouts_.end(), + [=](const auto& pair) { + return pair.second.get() == default_layout; + })); + default_layout_ = default_layout; + InvalidateLayout(); +} + +gfx::Size InterpolatingLayoutManager::GetPreferredSize(const View* host) const { + DCHECK_EQ(host_view(), host); + DCHECK(host); + return GetDefaultLayout()->GetPreferredSize(host); +} + +gfx::Size InterpolatingLayoutManager::GetMinimumSize(const View* host) const { + DCHECK_EQ(host_view(), host); + DCHECK(host); + return GetSmallestLayout()->GetMinimumSize(host); +} + +int InterpolatingLayoutManager::GetPreferredHeightForWidth(const View* host, + int width) const { + // It is in general not possible to determine what the correct + // height-for-width trade-off is while interpolating between two already- + // generated layouts because the values tend to rely on the behavior of + // individual child views at specific dimensions. + // + // The two reasonable choices are to use the larger of the two values (with + // the understanding that the height of the view may "pop" at the edge of the + // interpolation range), or to interpolate between the heights and hope that + // the result is fairly close to what we would want. + // + // We have opted for the second approach because it provides a smoother visual + // experience; if this doesn't work in practice we can look at other options. + + const LayoutInterpolation interpolation = + GetInterpolation({width, base::nullopt}); + const int first = + interpolation.first->GetPreferredHeightForWidth(host, width); + if (!interpolation.second) + return first; + + const int second = + interpolation.second->GetPreferredHeightForWidth(host, width); + return gfx::Tween::LinearIntValueBetween(interpolation.percent_second, first, + second); +} + +void InterpolatingLayoutManager::InvalidateLayout() { + LayoutManagerBase::InvalidateLayout(); + for (auto& embedded : embedded_layouts_) + embedded.second->InvalidateLayout(); +} + +void InterpolatingLayoutManager::SetChildViewIgnoredByLayout(View* child_view, + bool ignored) { + LayoutManagerBase::SetChildViewIgnoredByLayout(child_view, ignored); + for (auto& embedded : embedded_layouts_) + embedded.second->SetChildViewIgnoredByLayout(child_view, ignored); +} + +void InterpolatingLayoutManager::Installed(View* host_view) { + LayoutManagerBase::Installed(host_view); + for (auto& embedded : embedded_layouts_) + embedded.second->Installed(host_view); +} + +void InterpolatingLayoutManager::ViewAdded(View* host_view, View* child_view) { + LayoutManagerBase::ViewAdded(host_view, child_view); + for (auto& embedded : embedded_layouts_) + embedded.second->ViewAdded(host_view, child_view); +} + +void InterpolatingLayoutManager::ViewRemoved(View* host_view, + View* child_view) { + LayoutManagerBase::ViewRemoved(host_view, child_view); + for (auto& embedded : embedded_layouts_) + embedded.second->ViewRemoved(host_view, child_view); +} + +void InterpolatingLayoutManager::ViewVisibilitySet(View* host, + View* view, + bool visible) { + LayoutManagerBase::ViewVisibilitySet(host, view, visible); + for (auto& embedded : embedded_layouts_) + embedded.second->ViewVisibilitySet(host, view, visible); +} + +const LayoutManagerBase* InterpolatingLayoutManager::GetDefaultLayout() const { + DCHECK(!embedded_layouts_.empty()); + return default_layout_ ? default_layout_ + : embedded_layouts_.rbegin()->second.get(); +} + +const LayoutManagerBase* InterpolatingLayoutManager::GetSmallestLayout() const { + DCHECK(!embedded_layouts_.empty()); + return embedded_layouts_.begin()->second.get(); +} + +} // namespace views diff --git a/chromium/ui/views/layout/interpolating_layout_manager.h b/chromium/ui/views/layout/interpolating_layout_manager.h new file mode 100644 index 00000000000..0cc084cafa8 --- /dev/null +++ b/chromium/ui/views/layout/interpolating_layout_manager.h @@ -0,0 +1,132 @@ +// 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. + +#ifndef UI_VIEWS_LAYOUT_INTERPOLATING_LAYOUT_MANAGER_H_ +#define UI_VIEWS_LAYOUT_INTERPOLATING_LAYOUT_MANAGER_H_ + +#include <map> +#include <memory> +#include <utility> + +#include "ui/views/layout/flex_layout_types.h" +#include "ui/views/layout/layout_manager_base.h" + +namespace views { + +// Layout which interpolates between multiple embedded LayoutManagerBase +// layouts. +// +// An InterpolatingLayoutManager has a default layout, which applies at the +// smallest layout size along the layout's major axis (defined by |orientation|) +// and additional layouts, which phase in at some larger size. If only the +// default layout is set, the layout is functionally equivalent to the default +// layout. +// +// An example: +// +// InterpolatingLayoutManager* e = +// new InterpolatingLayoutManager(LayoutOrientation::kHorizontal); +// e->AddLayout(std::make_unique<CompactLayout>()); +// e->AddLayout(std::make_unique<NormalLayout>(), {50, 0}); +// e->AddLayout(std::make_unique<SpaciousLayout>(), {100, 50}); +// +// Now as the view expands, the different layouts are used: +// +// 0 50 100 150 +// | Compact | Normal | Norm <~> Spa | Spacious -> +// +// In the range from 100 to 150 (exclusive), an interpolation of the Normal and +// Spacious layouts is used. When interpolation happens in this way, the +// visibility of views is the conjunction of the visibilities in each layout, so +// if either layout hides a view then the interpolated layout also hides it. +// Since this can produce some unwanted visual results, we recommend making sure +// that over the interpolation range, visibility matches up between the layouts +// on either side. +// +// Note that behavior when interpolation ranges overlap is undefined, but will +// be guaranteed to at least be the result of mixing two adjacent layouts that +// fall over the range in a way that is not completely irrational. +class VIEWS_EXPORT InterpolatingLayoutManager : public LayoutManagerBase { + public: + InterpolatingLayoutManager(); + ~InterpolatingLayoutManager() override; + + InterpolatingLayoutManager& SetOrientation(LayoutOrientation orientation); + LayoutOrientation orientation() const { return orientation_; } + + // Adds a layout which starts and finished phasing in at |start_interpolation| + // and |end_interpolation|, respectively. Currently, having more than one + // layout's interpolation range overlapping results in undefined behavior. + // + // This object retains ownership of the layout engine, but the method returns + // a typed raw pointer to the added layout engine. + template <class T> + T* AddLayout(std::unique_ptr<T> layout_manager, + const Span& interpolation_range = Span()) { + T* const temp = layout_manager.get(); + AddLayoutInternal(std::move(layout_manager), interpolation_range); + return temp; + } + + // Specifies which layout is default (i.e. will be used for determining + // preferred layout size). If you do not set this, the largest layout will be + // used. + void SetDefaultLayout(LayoutManagerBase* default_layout); + + // LayoutManagerBase: + gfx::Size GetPreferredSize(const View* host) const override; + gfx::Size GetMinimumSize(const View* host) const override; + int GetPreferredHeightForWidth(const View* host, int width) const override; + void InvalidateLayout() override; + void SetChildViewIgnoredByLayout(View* child_view, bool ignored) override; + + protected: + // LayoutManagerBase: + void Installed(View* host_view) override; + void ViewAdded(View* host_view, View* child_view) override; + void ViewRemoved(View* host_view, View* child_view) override; + void ViewVisibilitySet(View* host, View* view, bool visible) override; + ProposedLayout CalculateProposedLayout( + const SizeBounds& size_bounds) const override; + + private: + // Describes an interpolation between two layouts as a pointer to each and + // a percentage of distance between them to interpolate linearly to. + struct LayoutInterpolation { + LayoutManagerBase* first = nullptr; + LayoutManagerBase* second = nullptr; + + // The closer this number is to zero, the more of |first| is used; the + // closer to 1.0f, the more of |second|. If the value is 0, |second| may be + // null. + float percent_second = 0.0f; + }; + + void AddLayoutInternal(std::unique_ptr<LayoutManagerBase> layout, + const Span& interpolation_range); + + // Given a set of size bounds and the current layout's orientation, returns + // a LayoutInterpolation providing the two layouts to interpolate between. + // If only one layout applies, only |right| is set and |percent| is set to 1. + LayoutInterpolation GetInterpolation(const SizeBounds& bounds) const; + + // Returns the default layout, or the largest layout if the default has not + // been set. + const LayoutManagerBase* GetDefaultLayout() const; + + // Returns the smallest layout; useful for calculating minimum layout size. + const LayoutManagerBase* GetSmallestLayout() const; + + LayoutOrientation orientation_ = LayoutOrientation::kHorizontal; + + // Maps from interpolation range to embedded layout. + std::map<Span, std::unique_ptr<LayoutManagerBase>> embedded_layouts_; + LayoutManagerBase* default_layout_ = nullptr; + + DISALLOW_COPY_AND_ASSIGN(InterpolatingLayoutManager); +}; + +} // namespace views + +#endif // UI_VIEWS_LAYOUT_INTERPOLATING_LAYOUT_MANAGER_H_ diff --git a/chromium/ui/views/layout/interpolating_layout_manager_unittest.cc b/chromium/ui/views/layout/interpolating_layout_manager_unittest.cc new file mode 100644 index 00000000000..cf44cda7c8a --- /dev/null +++ b/chromium/ui/views/layout/interpolating_layout_manager_unittest.cc @@ -0,0 +1,323 @@ +// 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. + +#include "ui/views/layout/interpolating_layout_manager.h" + +#include <memory> + +#include "testing/gtest/include/gtest/gtest.h" +#include "ui/gfx/animation/tween.h" +#include "ui/views/test/test_views.h" +#include "ui/views/view.h" + +namespace views { + +namespace { + +class TestLayout : public LayoutManagerBase { + public: + explicit TestLayout(int size = 0) : size_(size) {} + + int num_layouts_generated() const { return num_layouts_generated_; } + + ProposedLayout CalculateProposedLayout( + const SizeBounds& size_bounds) const override { + ++num_layouts_generated_; + ProposedLayout layout; + int x = size_; + for (auto it = host_view()->children().begin(); + it != host_view()->children().end(); ++it) { + if (!IsChildIncludedInLayout(*it)) + continue; + ChildLayout child_layout; + child_layout.child_view = *it; + child_layout.visible = true; + child_layout.bounds = gfx::Rect(x, 1, size_, size_); + layout.child_layouts.push_back(child_layout); + x += size_ + 1; + } + layout.host_size = {x, 2 + size_}; + return layout; + } + + private: + const int size_; + mutable int num_layouts_generated_ = 0; +}; + +void CompareProposedLayouts(const LayoutManagerBase::ProposedLayout& left, + const LayoutManagerBase::ProposedLayout& right) { + EXPECT_EQ(left.host_size, right.host_size); + EXPECT_EQ(left.child_layouts.size(), right.child_layouts.size()); + for (auto left_it = left.child_layouts.begin(), + right_it = right.child_layouts.begin(); + left_it != left.child_layouts.end() && + right_it != right.child_layouts.end(); + ++left_it, ++right_it) { + EXPECT_EQ(left_it->child_view, right_it->child_view); + EXPECT_EQ(left_it->visible, right_it->visible); + if (left_it->visible) + EXPECT_EQ(left_it->bounds, right_it->bounds); + } +} + +} // anonymous namespace + +class InterpolatingLayoutManagerTest : public testing::Test { + public: + void SetUp() override { + host_view_ = std::make_unique<View>(); + layout_manager_ = host_view_->SetLayoutManager( + std::make_unique<InterpolatingLayoutManager>()); + } + + InterpolatingLayoutManager* layout_manager() { return layout_manager_; } + View* host_view() { return host_view_.get(); } + + private: + InterpolatingLayoutManager* layout_manager_ = nullptr; + std::unique_ptr<View> host_view_; +}; + +TEST_F(InterpolatingLayoutManagerTest, AddLayout) { + TestLayout* const first_layout = + layout_manager()->AddLayout(std::make_unique<TestLayout>()); + EXPECT_EQ(0, first_layout->num_layouts_generated()); + layout_manager()->GetProposedLayout({0, 0}); + EXPECT_EQ(1, first_layout->num_layouts_generated()); +} + +TEST_F(InterpolatingLayoutManagerTest, AddLayout_CheckZeroAndUnbounded) { + TestLayout* const first_layout = + layout_manager()->AddLayout(std::make_unique<TestLayout>()); + TestLayout* const second_layout = + layout_manager()->AddLayout(std::make_unique<TestLayout>(), {5, 0}); + EXPECT_EQ(0, first_layout->num_layouts_generated()); + EXPECT_EQ(0, second_layout->num_layouts_generated()); + layout_manager()->GetPreferredSize(host_view()); + EXPECT_EQ(0, first_layout->num_layouts_generated()); + EXPECT_EQ(1, second_layout->num_layouts_generated()); + layout_manager()->GetMinimumSize(host_view()); + EXPECT_EQ(1, first_layout->num_layouts_generated()); + EXPECT_EQ(1, second_layout->num_layouts_generated()); +} + +TEST_F(InterpolatingLayoutManagerTest, GetProposedLayout_HardBoundary) { + TestLayout* const first_layout = + layout_manager()->AddLayout(std::make_unique<TestLayout>()); + TestLayout* const second_layout = + layout_manager()->AddLayout(std::make_unique<TestLayout>(), {5, 0}); + EXPECT_EQ(0, first_layout->num_layouts_generated()); + EXPECT_EQ(0, second_layout->num_layouts_generated()); + layout_manager()->GetProposedLayout({5, 2}); + EXPECT_EQ(0, first_layout->num_layouts_generated()); + EXPECT_EQ(1, second_layout->num_layouts_generated()); + layout_manager()->GetProposedLayout({4, 2}); + EXPECT_EQ(1, first_layout->num_layouts_generated()); + EXPECT_EQ(1, second_layout->num_layouts_generated()); +} + +TEST_F(InterpolatingLayoutManagerTest, GetProposedLayout_SoftBoudnary) { + TestLayout* const first_layout = + layout_manager()->AddLayout(std::make_unique<TestLayout>()); + TestLayout* const second_layout = + layout_manager()->AddLayout(std::make_unique<TestLayout>(), {4, 2}); + EXPECT_EQ(0, first_layout->num_layouts_generated()); + EXPECT_EQ(0, second_layout->num_layouts_generated()); + layout_manager()->GetProposedLayout({5, 2}); + EXPECT_EQ(1, first_layout->num_layouts_generated()); + EXPECT_EQ(1, second_layout->num_layouts_generated()); + layout_manager()->GetProposedLayout({4, 2}); + EXPECT_EQ(2, first_layout->num_layouts_generated()); + EXPECT_EQ(1, second_layout->num_layouts_generated()); + layout_manager()->GetProposedLayout({6, 6}); + EXPECT_EQ(2, first_layout->num_layouts_generated()); + EXPECT_EQ(2, second_layout->num_layouts_generated()); +} + +TEST_F(InterpolatingLayoutManagerTest, GetProposedLayout_MultipleLayouts) { + TestLayout* const first_layout = + layout_manager()->AddLayout(std::make_unique<TestLayout>()); + TestLayout* const second_layout = + layout_manager()->AddLayout(std::make_unique<TestLayout>(), {4, 2}); + TestLayout* const third_layout = + layout_manager()->AddLayout(std::make_unique<TestLayout>(), {6, 2}); + EXPECT_EQ(0, first_layout->num_layouts_generated()); + EXPECT_EQ(0, second_layout->num_layouts_generated()); + EXPECT_EQ(0, third_layout->num_layouts_generated()); + layout_manager()->GetProposedLayout({5, 2}); + EXPECT_EQ(1, first_layout->num_layouts_generated()); + EXPECT_EQ(1, second_layout->num_layouts_generated()); + EXPECT_EQ(0, third_layout->num_layouts_generated()); + layout_manager()->GetProposedLayout({6, 3}); + EXPECT_EQ(1, first_layout->num_layouts_generated()); + EXPECT_EQ(2, second_layout->num_layouts_generated()); + EXPECT_EQ(0, third_layout->num_layouts_generated()); + layout_manager()->GetProposedLayout({7, 6}); + EXPECT_EQ(1, first_layout->num_layouts_generated()); + EXPECT_EQ(3, second_layout->num_layouts_generated()); + EXPECT_EQ(1, third_layout->num_layouts_generated()); + layout_manager()->GetProposedLayout({20, 3}); + EXPECT_EQ(1, first_layout->num_layouts_generated()); + EXPECT_EQ(3, second_layout->num_layouts_generated()); + EXPECT_EQ(2, third_layout->num_layouts_generated()); +} + +TEST_F(InterpolatingLayoutManagerTest, InvalidateLayout) { + static const gfx::Size kLayoutSize(5, 5); + + TestLayout* const first_layout = + layout_manager()->AddLayout(std::make_unique<TestLayout>()); + TestLayout* const second_layout = + layout_manager()->AddLayout(std::make_unique<TestLayout>(), {4, 2}); + host_view()->SetSize(kLayoutSize); + EXPECT_EQ(1, first_layout->num_layouts_generated()); + EXPECT_EQ(1, second_layout->num_layouts_generated()); + host_view()->Layout(); + EXPECT_EQ(1, first_layout->num_layouts_generated()); + EXPECT_EQ(1, second_layout->num_layouts_generated()); + layout_manager()->InvalidateLayout(); + host_view()->Layout(); + EXPECT_EQ(2, first_layout->num_layouts_generated()); + EXPECT_EQ(2, second_layout->num_layouts_generated()); + host_view()->Layout(); + EXPECT_EQ(2, first_layout->num_layouts_generated()); + EXPECT_EQ(2, second_layout->num_layouts_generated()); +} + +TEST_F(InterpolatingLayoutManagerTest, SetOrientation) { + TestLayout* const first_layout = + layout_manager()->AddLayout(std::make_unique<TestLayout>()); + TestLayout* const second_layout = + layout_manager()->AddLayout(std::make_unique<TestLayout>(), {4, 2}); + layout_manager()->SetOrientation(LayoutOrientation::kVertical); + EXPECT_EQ(0, first_layout->num_layouts_generated()); + EXPECT_EQ(0, second_layout->num_layouts_generated()); + layout_manager()->GetProposedLayout({2, 6}); + EXPECT_EQ(0, first_layout->num_layouts_generated()); + EXPECT_EQ(1, second_layout->num_layouts_generated()); + layout_manager()->GetProposedLayout({3, 5}); + EXPECT_EQ(1, first_layout->num_layouts_generated()); + EXPECT_EQ(2, second_layout->num_layouts_generated()); + layout_manager()->GetProposedLayout({10, 3}); + EXPECT_EQ(2, first_layout->num_layouts_generated()); + EXPECT_EQ(2, second_layout->num_layouts_generated()); +} + +TEST_F(InterpolatingLayoutManagerTest, GetMinimumSize) { + TestLayout* const first_layout = + layout_manager()->AddLayout(std::make_unique<TestLayout>(5)); + layout_manager()->AddLayout(std::make_unique<TestLayout>(10), {5, 5}); + + // Minimum size should be equal to the default layout. + EXPECT_EQ(first_layout->GetMinimumSize(host_view()), + layout_manager()->GetMinimumSize(host_view())); +} + +TEST_F(InterpolatingLayoutManagerTest, GetPreferredSize_NoDefaultLayout) { + layout_manager()->AddLayout(std::make_unique<TestLayout>(5)); + TestLayout* const second_layout = + layout_manager()->AddLayout(std::make_unique<TestLayout>(10), {5, 5}); + + // Preferred size should be equal to the largest layout. + EXPECT_EQ(second_layout->GetPreferredSize(host_view()), + layout_manager()->GetPreferredSize(host_view())); +} + +TEST_F(InterpolatingLayoutManagerTest, GetPreferredSize_UsesDefaultLayout) { + TestLayout* const first_layout = + layout_manager()->AddLayout(std::make_unique<TestLayout>(5)); + layout_manager()->AddLayout(std::make_unique<TestLayout>(10), {5, 5}); + layout_manager()->SetDefaultLayout(first_layout); + + // Preferred size should be equal to the largest layout. + EXPECT_EQ(first_layout->GetPreferredSize(host_view()), + layout_manager()->GetPreferredSize(host_view())); +} + +TEST_F(InterpolatingLayoutManagerTest, GetPreferredHeightForWidth_Vertical) { + layout_manager()->SetOrientation(LayoutOrientation::kVertical); + layout_manager()->AddLayout(std::make_unique<TestLayout>(5)); + TestLayout* const second_layout = + layout_manager()->AddLayout(std::make_unique<TestLayout>(10), {5, 5}); + + // Vertical means preferred height for width applies to largest layout. + EXPECT_EQ(second_layout->GetPreferredHeightForWidth(host_view(), 7), + layout_manager()->GetPreferredHeightForWidth(host_view(), 7)); + EXPECT_EQ(second_layout->GetPreferredHeightForWidth(host_view(), 3), + layout_manager()->GetPreferredHeightForWidth(host_view(), 3)); + EXPECT_EQ(second_layout->GetPreferredHeightForWidth(host_view(), 10), + layout_manager()->GetPreferredHeightForWidth(host_view(), 10)); +} + +TEST_F(InterpolatingLayoutManagerTest, GetPreferredHeightForWidth_Horizontal) { + layout_manager()->SetOrientation(LayoutOrientation::kHorizontal); + TestLayout* const first_layout = + layout_manager()->AddLayout(std::make_unique<TestLayout>(5)); + TestLayout* const second_layout = + layout_manager()->AddLayout(std::make_unique<TestLayout>(10), {5, 5}); + + // Horizontal means preferred height for width is interpolated. + // Note that the layout doesn't actually flex height with varying width, so we + // can use constant reference values. + const int default_height = + first_layout->GetPreferredHeightForWidth(host_view(), 7); + const int other_height = + second_layout->GetPreferredHeightForWidth(host_view(), 7); + EXPECT_EQ(default_height, + layout_manager()->GetPreferredHeightForWidth(host_view(), 5)); + EXPECT_EQ(other_height, + layout_manager()->GetPreferredHeightForWidth(host_view(), 10)); + EXPECT_EQ(int{default_height * 0.4f + other_height * 0.6f}, + layout_manager()->GetPreferredHeightForWidth(host_view(), 8)); +} + +TEST_F(InterpolatingLayoutManagerTest, GetProposedLayout) { + View* const child_view = host_view()->AddChildView(std::make_unique<View>()); + TestLayout* const first_layout = + layout_manager()->AddLayout(std::make_unique<TestLayout>(5)); + TestLayout* const second_layout = + layout_manager()->AddLayout(std::make_unique<TestLayout>(10), {5, 6}); + + constexpr gfx::Size kSmallSize{5, 10}; + constexpr gfx::Size kLargeSize{11, 10}; + constexpr gfx::Size kOneThirdSize{7, 10}; + constexpr gfx::Size kOneHalfSize{8, 10}; + const LayoutManagerBase::ProposedLayout expected_default = + first_layout->GetProposedLayout(kSmallSize); + const LayoutManagerBase::ProposedLayout expected_other = + second_layout->GetProposedLayout(kLargeSize); + + CompareProposedLayouts(expected_default, + layout_manager()->GetProposedLayout(kSmallSize)); + CompareProposedLayouts(expected_other, + layout_manager()->GetProposedLayout(kLargeSize)); + + LayoutManagerBase::ProposedLayout actual = + layout_manager()->GetProposedLayout(kOneThirdSize); + EXPECT_EQ(gfx::Tween::SizeValueBetween(0.3333, expected_default.host_size, + expected_other.host_size), + actual.host_size); + ASSERT_EQ(1U, actual.child_layouts.size()); + EXPECT_EQ(child_view, actual.child_layouts[0].child_view); + EXPECT_EQ(true, actual.child_layouts[0].visible); + EXPECT_EQ(gfx::Tween::RectValueBetween( + 0.3333, expected_default.child_layouts[0].bounds, + expected_other.child_layouts[0].bounds), + actual.child_layouts[0].bounds); + + actual = layout_manager()->GetProposedLayout(kOneHalfSize); + EXPECT_EQ(gfx::Tween::SizeValueBetween(0.5, expected_default.host_size, + expected_other.host_size), + actual.host_size); + ASSERT_EQ(1U, actual.child_layouts.size()); + EXPECT_EQ(child_view, actual.child_layouts[0].child_view); + EXPECT_EQ(true, actual.child_layouts[0].visible); + EXPECT_EQ(gfx::Tween::RectValueBetween( + 0.5, expected_default.child_layouts[0].bounds, + expected_other.child_layouts[0].bounds), + actual.child_layouts[0].bounds); +} + +} // namespace views diff --git a/chromium/ui/views/layout/layout_manager_base.cc b/chromium/ui/views/layout/layout_manager_base.cc new file mode 100644 index 00000000000..1992d5cbd9f --- /dev/null +++ b/chromium/ui/views/layout/layout_manager_base.cc @@ -0,0 +1,171 @@ +// 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. + +#include "ui/views/layout/layout_manager_base.h" + +#include <utility> + +#include "base/logging.h" +#include "ui/views/view.h" + +namespace views { + +LayoutManagerBase::ProposedLayout::ProposedLayout() = default; +LayoutManagerBase::ProposedLayout::ProposedLayout(const ProposedLayout& other) = + default; +LayoutManagerBase::ProposedLayout::ProposedLayout(ProposedLayout&& other) = + default; +LayoutManagerBase::ProposedLayout::~ProposedLayout() = default; +LayoutManagerBase::ProposedLayout& LayoutManagerBase::ProposedLayout::operator=( + const ProposedLayout& other) = default; +LayoutManagerBase::ProposedLayout& LayoutManagerBase::ProposedLayout::operator=( + ProposedLayout&& other) = default; + +LayoutManagerBase::~LayoutManagerBase() = default; + +gfx::Size LayoutManagerBase::GetPreferredSize(const View* host) const { + DCHECK_EQ(host_view_, host); + if (!preferred_size_) + preferred_size_ = CalculateProposedLayout(SizeBounds()).host_size; + return *preferred_size_; +} + +gfx::Size LayoutManagerBase::GetMinimumSize(const View* host) const { + DCHECK_EQ(host_view_, host); + if (!minimum_size_) + minimum_size_ = CalculateProposedLayout(SizeBounds(0, 0)).host_size; + return *minimum_size_; +} + +int LayoutManagerBase::GetPreferredHeightForWidth(const View* host, + int width) const { + if (!last_height_for_width_ || last_height_for_width_->width() != width) { + const int height = CalculateProposedLayout(SizeBounds(width, base::nullopt)) + .host_size.height(); + last_height_for_width_ = gfx::Size(width, height); + } + + return last_height_for_width_->height(); +} + +void LayoutManagerBase::Layout(View* host) { + DCHECK_EQ(host_view_, host); + const gfx::Size size = host->size(); + ApplyLayout(GetProposedLayout(size)); +} + +void LayoutManagerBase::InvalidateLayout() { + minimum_size_.reset(); + preferred_size_.reset(); + last_height_for_width_.reset(); + last_requested_size_.reset(); +} + +LayoutManagerBase::ProposedLayout LayoutManagerBase::GetProposedLayout( + const gfx::Size& host_size) const { + if (!last_requested_size_ || *last_requested_size_ != host_size) { + last_requested_size_ = host_size; + last_layout_ = CalculateProposedLayout(SizeBounds(host_size)); + } + return last_layout_; +} + +void LayoutManagerBase::SetChildViewIgnoredByLayout(View* child_view, + bool ignored) { + auto it = child_infos_.find(child_view); + DCHECK(it != child_infos_.end()); + if (it->second.ignored == ignored) + return; + + it->second.ignored = ignored; + InvalidateLayout(); +} + +bool LayoutManagerBase::IsChildViewIgnoredByLayout( + const View* child_view) const { + auto it = child_infos_.find(child_view); + DCHECK(it != child_infos_.end()); + return it->second.ignored; +} + +void LayoutManagerBase::Installed(View* host_view) { + DCHECK(host_view); + DCHECK(!host_view_); + DCHECK(child_infos_.empty()); + + host_view_ = host_view; + for (auto it = host_view->children().begin(); + it != host_view->children().end(); ++it) { + child_infos_.emplace(*it, ChildInfo{(*it)->GetVisible(), false}); + } +} + +void LayoutManagerBase::ViewAdded(View* host, View* view) { + DCHECK_EQ(host_view_, host); + DCHECK(!base::Contains(child_infos_, view)); + ChildInfo to_add{view->GetVisible(), false}; + child_infos_.emplace(view, to_add); + if (to_add.can_be_visible) + InvalidateLayout(); +} + +void LayoutManagerBase::ViewRemoved(View* host, View* view) { + DCHECK_EQ(host_view_, host); + auto it = child_infos_.find(view); + DCHECK(it != child_infos_.end()); + const bool removed_visible = it->second.can_be_visible && !it->second.ignored; + child_infos_.erase(it); + if (removed_visible) + InvalidateLayout(); +} + +void LayoutManagerBase::ViewVisibilitySet(View* host, + View* view, + bool visible) { + DCHECK_EQ(host_view_, host); + auto it = child_infos_.find(view); + DCHECK(it != child_infos_.end()); + if (it->second.can_be_visible == visible) + return; + + it->second.can_be_visible = visible; + if (!it->second.ignored) + InvalidateLayout(); +} + +LayoutManagerBase::LayoutManagerBase() = default; + +bool LayoutManagerBase::IsChildIncludedInLayout(const View* child) const { + const auto it = child_infos_.find(child); + DCHECK(it != child_infos_.end()); + return !it->second.ignored && it->second.can_be_visible; +} + +void LayoutManagerBase::ApplyLayout(const ProposedLayout& layout) { + for (auto& child_layout : layout.child_layouts) { + DCHECK_EQ(host_view_, child_layout.child_view->parent()); + + // Since we have a non-const reference to the parent here, we can safely use + // a non-const reference to the child. + View* const child_view = child_layout.child_view; + if (child_view->GetVisible() != child_layout.visible) + SetViewVisibility(child_view, child_layout.visible); + if (child_layout.visible) + child_view->SetBoundsRect(child_layout.bounds); + } +} + +void LayoutManagerBase::SyncStateTo(LayoutManagerBase* other) const { + if (host_view_) { + other->Installed(host_view_); + for (View* child_view : host_view_->children()) { + const ChildInfo& child_info = child_infos_.find(child_view)->second; + other->SetChildViewIgnoredByLayout(child_view, child_info.ignored); + other->ViewVisibilitySet(host_view_, child_view, + child_info.can_be_visible); + } + } +} + +} // namespace views diff --git a/chromium/ui/views/layout/layout_manager_base.h b/chromium/ui/views/layout/layout_manager_base.h new file mode 100644 index 00000000000..5ff97926bc5 --- /dev/null +++ b/chromium/ui/views/layout/layout_manager_base.h @@ -0,0 +1,125 @@ +// 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. + +#ifndef UI_VIEWS_LAYOUT_LAYOUT_MANAGER_BASE_H_ +#define UI_VIEWS_LAYOUT_LAYOUT_MANAGER_BASE_H_ + +#include <map> +#include <memory> +#include <set> +#include <utility> +#include <vector> + +#include "base/optional.h" +#include "ui/gfx/geometry/insets.h" +#include "ui/gfx/geometry/rect.h" +#include "ui/gfx/geometry/size.h" +#include "ui/views/layout/layout_manager.h" +#include "ui/views/layout/layout_types.h" +#include "ui/views/views_export.h" + +namespace views { + +class View; + +// Base class for layout managers that can do layout calculation separately +// from layout application. Derived classes must implement +// CalculateProposedLayout(). Used in interpolating and animating layouts. +class VIEWS_EXPORT LayoutManagerBase : public LayoutManager { + public: + // Represents layout information for a child view within a host being laid + // out. + struct VIEWS_EXPORT ChildLayout { + View* child_view = nullptr; + gfx::Rect bounds; + bool visible = false; + }; + + // Contains a full layout specification for the children of the host view. + struct VIEWS_EXPORT ProposedLayout { + ProposedLayout(); + ~ProposedLayout(); + ProposedLayout(const ProposedLayout& other); + ProposedLayout(ProposedLayout&& other); + ProposedLayout& operator=(const ProposedLayout& other); + ProposedLayout& operator=(ProposedLayout&& other); + + // The size of the host view given the size bounds for this layout. If both + // dimensions of the size bounds are specified, this will be the same size. + gfx::Size host_size; + + // Contains an entry for each child view included in the layout. + std::vector<ChildLayout> child_layouts; + }; + + ~LayoutManagerBase() override; + + View* host_view() { return host_view_; } + const View* host_view() const { return host_view_; } + + // Fetches a proposed layout for a host view with size |host_size|. If the + // result had already been calculated, a cached value may be returned. + ProposedLayout GetProposedLayout(const gfx::Size& host_size) const; + + // Excludes a specific view from the layout when doing layout calculations. + // Useful when a child view is meant to be displayed but has its size and + // position managed elsewhere in code. By default, all child views are + // included in the layout unless they are hidden. + virtual void SetChildViewIgnoredByLayout(View* child_view, bool ignored); + virtual bool IsChildViewIgnoredByLayout(const View* child_view) const; + + // LayoutManager: + gfx::Size GetPreferredSize(const View* host) const override; + gfx::Size GetMinimumSize(const View* host) const override; + int GetPreferredHeightForWidth(const View* host, int width) const override; + void Layout(View* host) override; + void InvalidateLayout() override; + void Installed(View* host) override; + void ViewAdded(View* host, View* view) override; + void ViewRemoved(View* host, View* view) override; + void ViewVisibilitySet(View* host, View* view, bool visible) override; + + protected: + LayoutManagerBase(); + + bool IsChildIncludedInLayout(const View* child) const; + + // Creates a proposed layout for the host view, including bounds and + // visibility for all children currently included in the layout. + virtual ProposedLayout CalculateProposedLayout( + const SizeBounds& size_bounds) const = 0; + + // Applies |layout| to the children of the host view. + void ApplyLayout(const ProposedLayout& layout); + + // Can be used by derived classes to ensure that state is correctly + // transferred to child LayoutManagerBase instances in a composite layout + // (interpolating or animating layouts, etc.) + void SyncStateTo(LayoutManagerBase* other) const; + + private: + // Holds bookkeeping data used to determine inclusion of children in the + // layout. + struct ChildInfo { + bool can_be_visible = true; + bool ignored = false; + }; + + View* host_view_ = nullptr; + std::map<const View*, ChildInfo> child_infos_; + + // Do some really simple caching because layout generation can cost as much + // as 1ms or more for complex views. + mutable base::Optional<gfx::Size> minimum_size_; + mutable base::Optional<gfx::Size> preferred_size_; + mutable base::Optional<gfx::Size> last_height_for_width_; + mutable base::Optional<gfx::Size> last_requested_size_; + mutable ProposedLayout last_layout_; + + DISALLOW_COPY_AND_ASSIGN(LayoutManagerBase); +}; + +} // namespace views + +#endif // UI_VIEWS_LAYOUT_LAYOUT_MANAGER_BASE_H_ diff --git a/chromium/ui/views/layout/layout_manager_base_unittest.cc b/chromium/ui/views/layout/layout_manager_base_unittest.cc new file mode 100644 index 00000000000..36e39551ad7 --- /dev/null +++ b/chromium/ui/views/layout/layout_manager_base_unittest.cc @@ -0,0 +1,479 @@ +// 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. + +#include "ui/views/layout/layout_manager_base.h" + +#include <algorithm> + +#include "testing/gtest/include/gtest/gtest.h" +#include "ui/views/test/test_layout_manager.h" +#include "ui/views/test/test_views.h" +#include "ui/views/view.h" + +namespace views { + +// Test LayoutManagerBase-specific functionality: + +namespace { + +constexpr gfx::Size kMinimumSize(40, 50); +constexpr gfx::Size kPreferredSize(100, 90); + +// Dummy class that minimally implements LayoutManagerBase for basic +// functionality testing. +class TestLayoutManagerBase : public LayoutManagerBase { + public: + std::vector<const View*> GetIncludedChildViews() const { + std::vector<const View*> included; + std::copy_if(host_view()->children().begin(), host_view()->children().end(), + std::back_inserter(included), [=](const View* child) { + return IsChildIncludedInLayout(child); + }); + return included; + } + + // LayoutManagerBase: + ProposedLayout CalculateProposedLayout( + const SizeBounds& size_bounds) const override { + ProposedLayout layout; + layout.host_size.set_width( + std::max(kMinimumSize.width(), + size_bounds.width().value_or(kPreferredSize.width()))); + layout.host_size.set_height( + std::max(kMinimumSize.height(), + size_bounds.height().value_or(kPreferredSize.height()))); + return layout; + } +}; + +void ExpectSameViews(const std::vector<const View*>& expected, + const std::vector<const View*>& actual) { + EXPECT_EQ(expected.size(), actual.size()); + for (size_t i = 0; i < expected.size(); ++i) { + EXPECT_EQ(expected[i], actual[i]); + } +} + +} // namespace + +TEST(LayoutManagerBaseTest, GetMinimumSize) { + TestLayoutManagerBase layout; + EXPECT_EQ(kMinimumSize, layout.GetMinimumSize(nullptr)); +} + +TEST(LayoutManagerBaseTest, GetPreferredSize) { + TestLayoutManagerBase layout; + EXPECT_EQ(kPreferredSize, layout.GetPreferredSize(nullptr)); +} + +TEST(LayoutManagerBaseTest, GetPreferredHeightForWidth) { + constexpr int kWidth = 45; + TestLayoutManagerBase layout; + EXPECT_EQ(kPreferredSize.height(), + layout.GetPreferredHeightForWidth(nullptr, kWidth)); +} + +TEST(LayoutManagerBaseTest, Installed) { + TestLayoutManagerBase layout; + EXPECT_EQ(nullptr, layout.host_view()); + + View view; + layout.Installed(&view); + EXPECT_EQ(&view, layout.host_view()); +} + +TEST(LayoutManagerBaseTest, SetChildIncludedInLayout) { + View view; + View* const child1 = view.AddChildView(std::make_unique<View>()); + View* const child2 = view.AddChildView(std::make_unique<View>()); + View* const child3 = view.AddChildView(std::make_unique<View>()); + + TestLayoutManagerBase layout; + layout.Installed(&view); + + // All views should be present. + ExpectSameViews({child1, child2, child3}, layout.GetIncludedChildViews()); + + // Remove one. + layout.SetChildViewIgnoredByLayout(child2, true); + ExpectSameViews({child1, child3}, layout.GetIncludedChildViews()); + + // Remove another. + layout.SetChildViewIgnoredByLayout(child1, true); + ExpectSameViews({child3}, layout.GetIncludedChildViews()); + + // Removing it again should have no effect. + layout.SetChildViewIgnoredByLayout(child1, true); + ExpectSameViews({child3}, layout.GetIncludedChildViews()); + + // Add one back. + layout.SetChildViewIgnoredByLayout(child1, false); + ExpectSameViews({child1, child3}, layout.GetIncludedChildViews()); + + // Adding it back again should have no effect. + layout.SetChildViewIgnoredByLayout(child1, false); + ExpectSameViews({child1, child3}, layout.GetIncludedChildViews()); + + // Add the other view back. + layout.SetChildViewIgnoredByLayout(child2, false); + ExpectSameViews({child1, child2, child3}, layout.GetIncludedChildViews()); +} + +// Test LayoutManager functionality of LayoutManagerBase: + +namespace { + +constexpr int kChildViewPadding = 5; +constexpr gfx::Size kSquarishSize(10, 11); +constexpr gfx::Size kLongSize(20, 8); +constexpr gfx::Size kTallSize(4, 22); +constexpr gfx::Size kLargeSize(30, 28); + +// This layout layout lays out included child views in the upper-left of the +// host view with kChildViewPadding around them. Views that will not fit are +// made invisible. Child views are expected to overlap as they all have the +// same top-left corner. +class MockLayoutManagerBase : public LayoutManagerBase { + public: + int num_invalidations() const { return num_invalidations_; } + int num_layouts_generated() const { return num_layouts_generated_; } + + // LayoutManagerBase: + ProposedLayout CalculateProposedLayout( + const SizeBounds& size_bounds) const override { + ProposedLayout layout; + layout.host_size = {kChildViewPadding, kChildViewPadding}; + for (auto it = host_view()->children().begin(); + it != host_view()->children().end(); ++it) { + if (!IsChildIncludedInLayout(*it)) + continue; + const gfx::Size preferred_size = (*it)->GetPreferredSize(); + bool visible = false; + gfx::Rect bounds; + const int required_width = preferred_size.width() + 2 * kChildViewPadding; + const int required_height = + preferred_size.height() + 2 * kChildViewPadding; + if ((!size_bounds.width() || required_width <= *size_bounds.width()) && + (!size_bounds.height() || required_height <= *size_bounds.height())) { + visible = true; + bounds = gfx::Rect(kChildViewPadding, kChildViewPadding, + preferred_size.width(), preferred_size.height()); + layout.host_size.set_width(std::max( + layout.host_size.width(), bounds.right() + kChildViewPadding)); + layout.host_size.set_height(std::max( + layout.host_size.height(), bounds.bottom() + kChildViewPadding)); + } + layout.child_layouts.push_back({*it, bounds, visible}); + } + ++num_layouts_generated_; + return layout; + } + + void InvalidateLayout() override { + LayoutManagerBase::InvalidateLayout(); + ++num_invalidations_; + } + + using LayoutManagerBase::ApplyLayout; + + private: + mutable int num_layouts_generated_ = 0; + mutable int num_invalidations_ = 0; +}; + +// Base for tests that evaluate the LayoutManager functionality of +// LayoutManagerBase (rather than the LayoutManagerBase-specific behavior). +class LayoutManagerBaseManagerTest : public testing::Test { + public: + void SetUp() override { + host_view_ = std::make_unique<View>(); + layout_manager_ = + host_view_->SetLayoutManager(std::make_unique<MockLayoutManagerBase>()); + } + + View* AddChildView(gfx::Size preferred_size) { + auto child = std::make_unique<StaticSizedView>(preferred_size); + return host_view_->AddChildView(std::move(child)); + } + + View* host_view() { return host_view_.get(); } + MockLayoutManagerBase* layout_manager() { return layout_manager_; } + View* child(int index) { return host_view_->children().at(index); } + + private: + std::unique_ptr<View> host_view_; + MockLayoutManagerBase* layout_manager_; +}; + +} // namespace + +TEST_F(LayoutManagerBaseManagerTest, ApplyLayout) { + AddChildView(gfx::Size()); + AddChildView(gfx::Size()); + AddChildView(gfx::Size()); + + // We don't want to set the size of the host view because it will trigger a + // superfluous layout, so we'll just keep the old size and make sure it + // doesn't change. + const gfx::Size old_size = host_view()->size(); + + LayoutManagerBase::ProposedLayout layout; + // This should be ignored. + layout.host_size = {123, 456}; + + // Set the child visibility and bounds. + constexpr gfx::Rect kChild1Bounds(3, 4, 10, 15); + constexpr gfx::Rect kChild3Bounds(20, 21, 12, 14); + layout.child_layouts.push_back( + LayoutManagerBase::ChildLayout{child(0), kChild1Bounds, true}); + layout.child_layouts.push_back( + LayoutManagerBase::ChildLayout{child(1), gfx::Rect(), false}); + layout.child_layouts.push_back( + LayoutManagerBase::ChildLayout{child(2), kChild3Bounds, true}); + + layout_manager()->ApplyLayout(layout); + + EXPECT_TRUE(child(0)->GetVisible()); + EXPECT_EQ(kChild1Bounds, child(0)->bounds()); + EXPECT_FALSE(child(1)->GetVisible()); + EXPECT_TRUE(child(2)->GetVisible()); + EXPECT_EQ(kChild3Bounds, child(2)->bounds()); + EXPECT_EQ(old_size, host_view()->size()); +} + +TEST_F(LayoutManagerBaseManagerTest, ApplyLayout_SkipsOmittedViews) { + AddChildView(gfx::Size()); + AddChildView(gfx::Size()); + AddChildView(gfx::Size()); + + LayoutManagerBase::ProposedLayout layout; + // Set the child visibility and bounds. + constexpr gfx::Rect kChild1Bounds(3, 4, 10, 15); + constexpr gfx::Rect kChild2Bounds(1, 2, 3, 4); + layout.child_layouts.push_back( + LayoutManagerBase::ChildLayout{child(0), kChild1Bounds, true}); + layout.child_layouts.push_back( + LayoutManagerBase::ChildLayout{child(2), gfx::Rect(), false}); + + // We'll set the second child separately. + child(1)->SetVisible(true); + child(1)->SetBoundsRect(kChild2Bounds); + + layout_manager()->ApplyLayout(layout); + + EXPECT_TRUE(child(0)->GetVisible()); + EXPECT_EQ(kChild1Bounds, child(0)->bounds()); + EXPECT_TRUE(child(1)->GetVisible()); + EXPECT_EQ(kChild2Bounds, child(1)->bounds()); + EXPECT_FALSE(child(2)->GetVisible()); +} + +TEST_F(LayoutManagerBaseManagerTest, Install) { + EXPECT_EQ(host_view(), layout_manager()->host_view()); +} + +TEST_F(LayoutManagerBaseManagerTest, GetMinimumSize) { + AddChildView(kSquarishSize); + AddChildView(kLongSize); + AddChildView(kTallSize); + EXPECT_EQ(gfx::Size(kChildViewPadding, kChildViewPadding), + host_view()->GetMinimumSize()); +} + +TEST_F(LayoutManagerBaseManagerTest, GetPreferredSize) { + AddChildView(kSquarishSize); + AddChildView(kLongSize); + AddChildView(kTallSize); + const gfx::Size expected(kLongSize.width() + 2 * kChildViewPadding, + kTallSize.height() + 2 * kChildViewPadding); + EXPECT_EQ(expected, host_view()->GetPreferredSize()); +} + +TEST_F(LayoutManagerBaseManagerTest, GetPreferredHeightForWidth) { + AddChildView(kSquarishSize); + AddChildView(kLargeSize); + const int expected = kSquarishSize.height() + 2 * kChildViewPadding; + EXPECT_EQ(expected, + layout_manager()->GetPreferredHeightForWidth(host_view(), 20)); + EXPECT_EQ(1, layout_manager()->num_layouts_generated()); + layout_manager()->GetPreferredHeightForWidth(host_view(), 20); + EXPECT_EQ(1, layout_manager()->num_layouts_generated()); + layout_manager()->GetPreferredHeightForWidth(host_view(), 25); + EXPECT_EQ(2, layout_manager()->num_layouts_generated()); +} + +TEST_F(LayoutManagerBaseManagerTest, InvalidateLayout) { + // Some invalidation could have been triggered during setup. + const int old_num_invalidations = layout_manager()->num_invalidations(); + + host_view()->InvalidateLayout(); + EXPECT_EQ(old_num_invalidations + 1, layout_manager()->num_invalidations()); +} + +TEST_F(LayoutManagerBaseManagerTest, Layout) { + constexpr gfx::Point kUpperLeft(kChildViewPadding, kChildViewPadding); + AddChildView(kSquarishSize); + AddChildView(kLongSize); + AddChildView(kTallSize); + + // This should fit all of the child views and trigger layout. + host_view()->SetSize({40, 40}); + EXPECT_EQ(1, layout_manager()->num_layouts_generated()); + EXPECT_EQ(gfx::Rect(kUpperLeft, child(0)->GetPreferredSize()), + child(0)->bounds()); + EXPECT_TRUE(child(0)->GetVisible()); + EXPECT_EQ(gfx::Rect(kUpperLeft, child(1)->GetPreferredSize()), + child(1)->bounds()); + EXPECT_TRUE(child(1)->GetVisible()); + EXPECT_EQ(gfx::Rect(kUpperLeft, child(2)->GetPreferredSize()), + child(2)->bounds()); + EXPECT_TRUE(child(2)->GetVisible()); + + // This should drop out some children. + host_view()->SetSize({25, 25}); + EXPECT_EQ(2, layout_manager()->num_layouts_generated()); + EXPECT_EQ(gfx::Rect(kUpperLeft, child(0)->GetPreferredSize()), + child(0)->bounds()); + EXPECT_TRUE(child(0)->GetVisible()); + EXPECT_FALSE(child(1)->GetVisible()); + EXPECT_FALSE(child(2)->GetVisible()); +} + +TEST_F(LayoutManagerBaseManagerTest, ChildViewIgnoredByLayout) { + AddChildView(kSquarishSize); + AddChildView(kLongSize); + AddChildView(kTallSize); + + EXPECT_FALSE(layout_manager()->IsChildViewIgnoredByLayout(child(0))); + EXPECT_FALSE(layout_manager()->IsChildViewIgnoredByLayout(child(1))); + EXPECT_FALSE(layout_manager()->IsChildViewIgnoredByLayout(child(2))); + + layout_manager()->SetChildViewIgnoredByLayout(child(1), true); + + EXPECT_FALSE(layout_manager()->IsChildViewIgnoredByLayout(child(0))); + EXPECT_TRUE(layout_manager()->IsChildViewIgnoredByLayout(child(1))); + EXPECT_FALSE(layout_manager()->IsChildViewIgnoredByLayout(child(2))); +} + +TEST_F(LayoutManagerBaseManagerTest, + ChildViewIgnoredByLayout_IgnoresChildView) { + AddChildView(kSquarishSize); + AddChildView(kLongSize); + AddChildView(kTallSize); + + layout_manager()->SetChildViewIgnoredByLayout(child(1), true); + + child(1)->SetSize(kLargeSize); + + // Makes enough room for all views, and triggers layout. + host_view()->SetSize({50, 50}); + + EXPECT_EQ(child(0)->GetPreferredSize(), child(0)->size()); + EXPECT_EQ(kLargeSize, child(1)->size()); + EXPECT_EQ(child(2)->GetPreferredSize(), child(2)->size()); +} + +TEST_F(LayoutManagerBaseManagerTest, ViewVisibilitySet) { + AddChildView(kSquarishSize); + AddChildView(kLongSize); + AddChildView(kTallSize); + + child(1)->SetVisible(false); + + // Makes enough room for all views, and triggers layout. + host_view()->SetSize({50, 50}); + + EXPECT_TRUE(child(0)->GetVisible()); + EXPECT_EQ(child(0)->GetPreferredSize(), child(0)->size()); + EXPECT_FALSE(child(1)->GetVisible()); + EXPECT_TRUE(child(2)->GetVisible()); + EXPECT_EQ(child(2)->GetPreferredSize(), child(2)->size()); + + // Turn the second child view back on and verify it's present in the layout + // again. + child(1)->SetVisible(true); + host_view()->Layout(); + + EXPECT_TRUE(child(0)->GetVisible()); + EXPECT_EQ(child(0)->GetPreferredSize(), child(0)->size()); + EXPECT_TRUE(child(1)->GetVisible()); + EXPECT_EQ(child(1)->GetPreferredSize(), child(1)->size()); + EXPECT_TRUE(child(2)->GetVisible()); + EXPECT_EQ(child(2)->GetPreferredSize(), child(2)->size()); +} + +TEST_F(LayoutManagerBaseManagerTest, ViewAdded) { + AddChildView(kLongSize); + AddChildView(kTallSize); + + // Makes enough room for all views, and triggers layout. + host_view()->SetSize({50, 50}); + + EXPECT_TRUE(child(0)->GetVisible()); + EXPECT_EQ(child(0)->GetPreferredSize(), child(0)->size()); + EXPECT_TRUE(child(1)->GetVisible()); + EXPECT_EQ(child(1)->GetPreferredSize(), child(1)->size()); + + // Add a new view and verify it is being laid out. + View* new_view = AddChildView(kSquarishSize); + host_view()->Layout(); + + EXPECT_TRUE(new_view->GetVisible()); + EXPECT_EQ(new_view->GetPreferredSize(), new_view->size()); +} + +TEST_F(LayoutManagerBaseManagerTest, ViewAdded_NotVisible) { + AddChildView(kLongSize); + AddChildView(kTallSize); + + // Makes enough room for all views, and triggers layout. + host_view()->SetSize({50, 50}); + + EXPECT_TRUE(child(0)->GetVisible()); + EXPECT_EQ(child(0)->GetPreferredSize(), child(0)->size()); + EXPECT_TRUE(child(1)->GetVisible()); + EXPECT_EQ(child(1)->GetPreferredSize(), child(1)->size()); + + // Add a new view that is not visible and ensure that the layout manager + // doesn't touch it during layout. + View* new_view = new StaticSizedView(kSquarishSize); + new_view->SetVisible(false); + host_view()->AddChildView(new_view); + host_view()->Layout(); + + EXPECT_FALSE(new_view->GetVisible()); +} + +TEST_F(LayoutManagerBaseManagerTest, ViewRemoved) { + AddChildView(kSquarishSize); + View* const child_view = AddChildView(kLongSize); + AddChildView(kTallSize); + + // Makes enough room for all views, and triggers layout. + host_view()->SetSize({50, 50}); + + EXPECT_TRUE(child(0)->GetVisible()); + EXPECT_EQ(child(0)->GetPreferredSize(), child(0)->size()); + EXPECT_TRUE(child(1)->GetVisible()); + EXPECT_EQ(child(1)->GetPreferredSize(), child(1)->size()); + EXPECT_TRUE(child(2)->GetVisible()); + EXPECT_EQ(child(2)->GetPreferredSize(), child(2)->size()); + + host_view()->RemoveChildView(child_view); + child_view->SetSize(kLargeSize); + host_view()->Layout(); + + EXPECT_TRUE(child(0)->GetVisible()); + EXPECT_EQ(child(0)->GetPreferredSize(), child(0)->size()); + EXPECT_TRUE(child(1)->GetVisible()); + EXPECT_EQ(child(1)->GetPreferredSize(), child(1)->size()); + + EXPECT_TRUE(child_view->GetVisible()); + EXPECT_EQ(kLargeSize, child_view->size()); + + // Required since we removed it from the parent view. + delete child_view; +} + +} // namespace views diff --git a/chromium/ui/views/layout/layout_types.cc b/chromium/ui/views/layout/layout_types.cc new file mode 100644 index 00000000000..67e4a776b04 --- /dev/null +++ b/chromium/ui/views/layout/layout_types.cc @@ -0,0 +1,62 @@ +// 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. + +#include "ui/views/layout/layout_types.h" + +#include <algorithm> + +#include "base/strings/stringprintf.h" +#include "ui/gfx/geometry/size.h" + +namespace views { + +namespace { + +std::string OptionalToString(const base::Optional<int>& opt) { + if (!opt.has_value()) + return "_"; + return base::StringPrintf("%d", opt.value()); +} + +} // namespace + +// SizeBounds ------------------------------------------------------------------ + +SizeBounds::SizeBounds() = default; + +SizeBounds::SizeBounds(const base::Optional<int>& width, + const base::Optional<int>& height) + : width_(width), height_(height) {} + +SizeBounds::SizeBounds(const SizeBounds& other) + : width_(other.width()), height_(other.height()) {} + +SizeBounds::SizeBounds(const gfx::Size& other) + : width_(other.width()), height_(other.height()) {} + +void SizeBounds::Enlarge(int width, int height) { + if (width_) + width_ = std::max(0, *width_ + width); + if (height_) + height_ = std::max(0, *height_ + height); +} + +bool SizeBounds::operator==(const SizeBounds& other) const { + return width_ == other.width_ && height_ == other.height_; +} + +bool SizeBounds::operator!=(const SizeBounds& other) const { + return !(*this == other); +} + +bool SizeBounds::operator<(const SizeBounds& other) const { + return std::tie(height_, width_) < std::tie(other.height_, other.width_); +} + +std::string SizeBounds::ToString() const { + return base::StringPrintf("%s x %s", OptionalToString(width()).c_str(), + OptionalToString(height()).c_str()); +} + +} // namespace views diff --git a/chromium/ui/views/layout/layout_types.h b/chromium/ui/views/layout/layout_types.h new file mode 100644 index 00000000000..24b3383f530 --- /dev/null +++ b/chromium/ui/views/layout/layout_types.h @@ -0,0 +1,58 @@ +// 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. + +#ifndef UI_VIEWS_LAYOUT_LAYOUT_TYPES_H_ +#define UI_VIEWS_LAYOUT_LAYOUT_TYPES_H_ + +#include <string> + +#include "base/optional.h" +#include "ui/views/views_export.h" + +namespace gfx { +class Size; +} // namespace gfx + +namespace views { + +// Whether a layout is oriented horizontally or vertically. +enum class LayoutOrientation { + kHorizontal, + kVertical, +}; + +// Stores an optional width and height upper bound. Used when calculating the +// preferred size of a layout pursuant to a maximum available size. +class VIEWS_EXPORT SizeBounds { + public: + SizeBounds(); + SizeBounds(const base::Optional<int>& width, + const base::Optional<int>& height); + explicit SizeBounds(const gfx::Size& size); + SizeBounds(const SizeBounds& other); + + const base::Optional<int>& width() const { return width_; } + void set_width(const base::Optional<int>& width) { width_ = width; } + + const base::Optional<int>& height() const { return height_; } + void set_height(const base::Optional<int>& height) { height_ = height; } + + // Enlarges (or shrinks, if negative) each upper bound that is present by the + // specified amounts. + void Enlarge(int width, int height); + + bool operator==(const SizeBounds& other) const; + bool operator!=(const SizeBounds& other) const; + bool operator<(const SizeBounds& other) const; + + std::string ToString() const; + + private: + base::Optional<int> width_; + base::Optional<int> height_; +}; + +} // namespace views + +#endif // UI_VIEWS_LAYOUT_LAYOUT_TYPES_H_ diff --git a/chromium/ui/views/linux_ui/status_icon_linux.cc b/chromium/ui/views/linux_ui/status_icon_linux.cc index c3d1ce17864..e861e15761a 100644 --- a/chromium/ui/views/linux_ui/status_icon_linux.cc +++ b/chromium/ui/views/linux_ui/status_icon_linux.cc @@ -12,7 +12,13 @@ StatusIconLinux::StatusIconLinux() = default; StatusIconLinux::~StatusIconLinux() = default; -void StatusIconLinux::RefreshPlatformContextMenu() { +void StatusIconLinux::RefreshPlatformContextMenu() {} + +void StatusIconLinux::OnSetDelegate() {} + +void StatusIconLinux::SetDelegate(Delegate* delegate) { + delegate_ = delegate; + OnSetDelegate(); } } // namespace views diff --git a/chromium/ui/views/linux_ui/status_icon_linux.h b/chromium/ui/views/linux_ui/status_icon_linux.h index a6132695cbd..6bfa71ac022 100644 --- a/chromium/ui/views/linux_ui/status_icon_linux.h +++ b/chromium/ui/views/linux_ui/status_icon_linux.h @@ -29,6 +29,13 @@ class VIEWS_EXPORT StatusIconLinux { virtual void OnClick() = 0; virtual bool HasClickAction() = 0; + virtual const gfx::ImageSkia& GetImage() const = 0; + virtual const base::string16& GetToolTip() const = 0; + virtual ui::MenuModel* GetMenuModel() const = 0; + + // This should be called at most once by the implementation. + virtual void OnImplInitializationFailed() = 0; + protected: virtual ~Delegate(); }; @@ -36,7 +43,7 @@ class VIEWS_EXPORT StatusIconLinux { StatusIconLinux(); virtual ~StatusIconLinux(); - virtual void SetImage(const gfx::ImageSkia& image) = 0; + virtual void SetIcon(const gfx::ImageSkia& image) = 0; virtual void SetToolTip(const base::string16& tool_tip) = 0; // Invoked after a call to SetContextMenu() to let the platform-specific @@ -49,10 +56,13 @@ class VIEWS_EXPORT StatusIconLinux { // need to manually refresh it when the menu model changes. virtual void RefreshPlatformContextMenu(); + virtual void OnSetDelegate(); + + void SetDelegate(Delegate* delegate); + Delegate* delegate() { return delegate_; } - void set_delegate(Delegate* delegate) { delegate_ = delegate; } - private: + protected: Delegate* delegate_ = nullptr; }; diff --git a/chromium/ui/views/metadata/metadata_header_macros.h b/chromium/ui/views/metadata/metadata_header_macros.h index 532c01ad42c..c10bc2c2d83 100644 --- a/chromium/ui/views/metadata/metadata_header_macros.h +++ b/chromium/ui/views/metadata/metadata_header_macros.h @@ -13,4 +13,10 @@ METADATA_ACCESSORS_INTERNAL(class_name) \ METADATA_CLASS_INTERNAL(class_name) +// A version of METADATA_HEADER for View, the root of the metadata hierarchy. +// Here METADATA_ACCESSORS_INTERNAL_BASE is called. +#define METADATA_HEADER_BASE(class_name) \ + METADATA_ACCESSORS_INTERNAL_BASE(class_name) \ + METADATA_CLASS_INTERNAL(class_name) + #endif // UI_VIEWS_METADATA_METADATA_HEADER_MACROS_H_ diff --git a/chromium/ui/views/metadata/metadata_impl_macros.h b/chromium/ui/views/metadata/metadata_impl_macros.h index 215750e3e5a..2eedd00f50e 100644 --- a/chromium/ui/views/metadata/metadata_impl_macros.h +++ b/chromium/ui/views/metadata/metadata_impl_macros.h @@ -11,6 +11,7 @@ // Generate the implementation of the metadata accessors and internal class with // additional macros for defining the class' properties. + #define BEGIN_METADATA(class_name) \ views::metadata::ClassMetaData* class_name::METADATA_CLASS_NAME_INTERNAL( \ class_name)::meta_data_ = nullptr; \ @@ -27,6 +28,11 @@ return MetaData(); \ } \ \ + const char* class_name::GetClassName() const { \ + return class_name::kViewClassName; \ + } \ + const char class_name::kViewClassName[] = #class_name; \ + \ void METADATA_FUNCTION_PREFIX_INTERNAL(class_name)::BuildMetaData() { \ SetTypeName(std::string(#class_name)); diff --git a/chromium/ui/views/metadata/metadata_macros_internal.h b/chromium/ui/views/metadata/metadata_macros_internal.h index d29da827175..6f283f956d7 100644 --- a/chromium/ui/views/metadata/metadata_macros_internal.h +++ b/chromium/ui/views/metadata/metadata_macros_internal.h @@ -18,6 +18,16 @@ // Metadata Accessors --------------------------------------------------------- #define METADATA_ACCESSORS_INTERNAL(class_name) \ + static const char kViewClassName[]; \ + const char* GetClassName() const override; \ + static views::metadata::ClassMetaData* MetaData(); \ + views::metadata::ClassMetaData* GetClassMetaData() override; + +// A version of METADATA_ACCESSORS_INTERNAL for View, the root of the metadata +// hierarchy; here GetClassName() is not declared as an override. +#define METADATA_ACCESSORS_INTERNAL_BASE(class_name) \ + static const char kViewClassName[]; \ + virtual const char* GetClassName() const; \ static views::metadata::ClassMetaData* MetaData(); \ views::metadata::ClassMetaData* GetClassMetaData() override; diff --git a/chromium/ui/views/metadata/metadata_types.cc b/chromium/ui/views/metadata/metadata_types.cc index 7a900603427..34cbd933a7a 100644 --- a/chromium/ui/views/metadata/metadata_types.cc +++ b/chromium/ui/views/metadata/metadata_types.cc @@ -40,10 +40,22 @@ ClassMetaData::ClassMemberIterator::ClassMemberIterator( } ClassMetaData::ClassMemberIterator::~ClassMemberIterator() = default; +// If starting_container's members vector is empty, set current_collection_ +// to its parent until parent class has members. Base parent class View +// will always have members, even if all other parent classes do not. ClassMetaData::ClassMemberIterator::ClassMemberIterator( ClassMetaData* starting_container) { current_collection_ = starting_container; - current_vector_index_ = (current_collection_ ? 0 : SIZE_MAX); + if (!current_collection_) { + current_vector_index_ = SIZE_MAX; + } else if (current_collection_->members().size() == 0) { + do { + current_collection_ = current_collection_->parent_class_meta_data(); + } while (current_collection_ && current_collection_->members().empty()); + current_vector_index_ = (current_collection_ ? 0 : SIZE_MAX); + } else { + current_vector_index_ = 0; + } } bool ClassMetaData::ClassMemberIterator::operator==( @@ -65,6 +77,15 @@ operator++(int) { return tmp; } +bool ClassMetaData::ClassMemberIterator::IsLastMember() const { + return current_vector_index_ == current_collection_->members().size() - 1; +} + +std::string ClassMetaData::ClassMemberIterator::GetCurrentCollectionName() + const { + return current_collection_->type_name(); +} + void ClassMetaData::ClassMemberIterator::IncrementHelper() { DCHECK_LT(current_vector_index_, SIZE_MAX); ++current_vector_index_; diff --git a/chromium/ui/views/metadata/metadata_types.h b/chromium/ui/views/metadata/metadata_types.h index bdf4fa08c8e..90cf1324808 100644 --- a/chromium/ui/views/metadata/metadata_types.h +++ b/chromium/ui/views/metadata/metadata_types.h @@ -90,6 +90,12 @@ class VIEWS_EXPORT ClassMetaData { return current_collection_->members()[current_vector_index_]; } + // Returns true if iterator currently on last member for that current + // collection. + bool IsLastMember() const; + + std::string GetCurrentCollectionName() const; + private: friend class ClassMetaData; explicit ClassMemberIterator(ClassMetaData* starting_container); diff --git a/chromium/ui/views/metadata/metadata_unittest.cc b/chromium/ui/views/metadata/metadata_unittest.cc index 73529c1fe9c..8fad27f3adf 100644 --- a/chromium/ui/views/metadata/metadata_unittest.cc +++ b/chromium/ui/views/metadata/metadata_unittest.cc @@ -109,7 +109,6 @@ TEST_F(MetadataTest, TestFloatMetadataPropertyAccess) { GetMemberMetaData(&test_obj, "FloatProperty"); ASSERT_TRUE(member_data); - base::string16 member_value = member_data->GetValueAsString(&test_obj); CHECK_EQ(member_value, base::NumberToString16(start_value)); } diff --git a/chromium/ui/views/mouse_watcher.cc b/chromium/ui/views/mouse_watcher.cc index 9cc403da342..c13daf49a08 100644 --- a/chromium/ui/views/mouse_watcher.cc +++ b/chromium/ui/views/mouse_watcher.cc @@ -27,7 +27,7 @@ constexpr int kNotifyListenerTimeMs = 300; class MouseWatcher::Observer : public ui::EventObserver { public: Observer(MouseWatcher* mouse_watcher, gfx::NativeWindow window) - : mouse_watcher_(mouse_watcher), notify_listener_factory_(this) { + : mouse_watcher_(mouse_watcher) { event_monitor_ = EventMonitor::CreateApplicationMonitor( this, window, {ui::ET_MOUSE_PRESSED, ui::ET_MOUSE_MOVED, ui::ET_MOUSE_EXITED, @@ -92,7 +92,7 @@ class MouseWatcher::Observer : public ui::EventObserver { std::unique_ptr<views::EventMonitor> event_monitor_; // A factory that is used to construct a delayed callback to the listener. - base::WeakPtrFactory<Observer> notify_listener_factory_; + base::WeakPtrFactory<Observer> notify_listener_factory_{this}; DISALLOW_COPY_AND_ASSIGN(Observer); }; diff --git a/chromium/ui/views/resources/default_100_percent/common/blue_button.png b/chromium/ui/views/resources/default_100_percent/common/blue_button.png Binary files differdeleted file mode 100644 index ada819b1f6d..00000000000 --- a/chromium/ui/views/resources/default_100_percent/common/blue_button.png +++ /dev/null diff --git a/chromium/ui/views/resources/default_100_percent/common/blue_button_focused.png b/chromium/ui/views/resources/default_100_percent/common/blue_button_focused.png Binary files differdeleted file mode 100644 index efbf1518fd9..00000000000 --- a/chromium/ui/views/resources/default_100_percent/common/blue_button_focused.png +++ /dev/null diff --git a/chromium/ui/views/resources/default_100_percent/common/blue_button_focused_hover.png b/chromium/ui/views/resources/default_100_percent/common/blue_button_focused_hover.png Binary files differdeleted file mode 100644 index e3ead548f45..00000000000 --- a/chromium/ui/views/resources/default_100_percent/common/blue_button_focused_hover.png +++ /dev/null diff --git a/chromium/ui/views/resources/default_100_percent/common/blue_button_focused_pressed.png b/chromium/ui/views/resources/default_100_percent/common/blue_button_focused_pressed.png Binary files differdeleted file mode 100644 index 83a08848f19..00000000000 --- a/chromium/ui/views/resources/default_100_percent/common/blue_button_focused_pressed.png +++ /dev/null diff --git a/chromium/ui/views/resources/default_100_percent/common/blue_button_hover.png b/chromium/ui/views/resources/default_100_percent/common/blue_button_hover.png Binary files differdeleted file mode 100644 index 99a23d24e2b..00000000000 --- a/chromium/ui/views/resources/default_100_percent/common/blue_button_hover.png +++ /dev/null diff --git a/chromium/ui/views/resources/default_100_percent/common/blue_button_inactive.png b/chromium/ui/views/resources/default_100_percent/common/blue_button_inactive.png Binary files differdeleted file mode 100644 index 4f4e1ae9df0..00000000000 --- a/chromium/ui/views/resources/default_100_percent/common/blue_button_inactive.png +++ /dev/null diff --git a/chromium/ui/views/resources/default_100_percent/common/blue_button_pressed.png b/chromium/ui/views/resources/default_100_percent/common/blue_button_pressed.png Binary files differdeleted file mode 100644 index 0ab01706bdf..00000000000 --- a/chromium/ui/views/resources/default_100_percent/common/blue_button_pressed.png +++ /dev/null diff --git a/chromium/ui/views/resources/default_100_percent/common/button.png b/chromium/ui/views/resources/default_100_percent/common/button.png Binary files differdeleted file mode 100644 index 698c17bb600..00000000000 --- a/chromium/ui/views/resources/default_100_percent/common/button.png +++ /dev/null diff --git a/chromium/ui/views/resources/default_100_percent/common/button_focused.png b/chromium/ui/views/resources/default_100_percent/common/button_focused.png Binary files differdeleted file mode 100644 index e77fe42efb7..00000000000 --- a/chromium/ui/views/resources/default_100_percent/common/button_focused.png +++ /dev/null diff --git a/chromium/ui/views/resources/default_100_percent/common/button_focused_hover.png b/chromium/ui/views/resources/default_100_percent/common/button_focused_hover.png Binary files differdeleted file mode 100644 index ec427d09307..00000000000 --- a/chromium/ui/views/resources/default_100_percent/common/button_focused_hover.png +++ /dev/null diff --git a/chromium/ui/views/resources/default_100_percent/common/button_focused_pressed.png b/chromium/ui/views/resources/default_100_percent/common/button_focused_pressed.png Binary files differdeleted file mode 100644 index fcbd008f985..00000000000 --- a/chromium/ui/views/resources/default_100_percent/common/button_focused_pressed.png +++ /dev/null diff --git a/chromium/ui/views/resources/default_100_percent/common/button_hover.png b/chromium/ui/views/resources/default_100_percent/common/button_hover.png Binary files differdeleted file mode 100644 index 91905b3b622..00000000000 --- a/chromium/ui/views/resources/default_100_percent/common/button_hover.png +++ /dev/null diff --git a/chromium/ui/views/resources/default_100_percent/common/button_inactive.png b/chromium/ui/views/resources/default_100_percent/common/button_inactive.png Binary files differdeleted file mode 100644 index 0748403983f..00000000000 --- a/chromium/ui/views/resources/default_100_percent/common/button_inactive.png +++ /dev/null diff --git a/chromium/ui/views/resources/default_100_percent/common/button_pressed.png b/chromium/ui/views/resources/default_100_percent/common/button_pressed.png Binary files differdeleted file mode 100644 index 6610da3a9e2..00000000000 --- a/chromium/ui/views/resources/default_100_percent/common/button_pressed.png +++ /dev/null diff --git a/chromium/ui/views/resources/default_100_percent/common/checkbox.png b/chromium/ui/views/resources/default_100_percent/common/checkbox.png Binary files differdeleted file mode 100644 index b61b33b6fae..00000000000 --- a/chromium/ui/views/resources/default_100_percent/common/checkbox.png +++ /dev/null diff --git a/chromium/ui/views/resources/default_100_percent/common/checkbox_checked.png b/chromium/ui/views/resources/default_100_percent/common/checkbox_checked.png Binary files differdeleted file mode 100644 index 332e980e414..00000000000 --- a/chromium/ui/views/resources/default_100_percent/common/checkbox_checked.png +++ /dev/null diff --git a/chromium/ui/views/resources/default_100_percent/common/checkbox_checked_hover.png b/chromium/ui/views/resources/default_100_percent/common/checkbox_checked_hover.png Binary files differdeleted file mode 100644 index eac00a2d07d..00000000000 --- a/chromium/ui/views/resources/default_100_percent/common/checkbox_checked_hover.png +++ /dev/null diff --git a/chromium/ui/views/resources/default_100_percent/common/checkbox_checked_inactive.png b/chromium/ui/views/resources/default_100_percent/common/checkbox_checked_inactive.png Binary files differdeleted file mode 100644 index 1512c305aa1..00000000000 --- a/chromium/ui/views/resources/default_100_percent/common/checkbox_checked_inactive.png +++ /dev/null diff --git a/chromium/ui/views/resources/default_100_percent/common/checkbox_checked_pressed.png b/chromium/ui/views/resources/default_100_percent/common/checkbox_checked_pressed.png Binary files differdeleted file mode 100644 index 36338ba6c12..00000000000 --- a/chromium/ui/views/resources/default_100_percent/common/checkbox_checked_pressed.png +++ /dev/null diff --git a/chromium/ui/views/resources/default_100_percent/common/checkbox_focused.png b/chromium/ui/views/resources/default_100_percent/common/checkbox_focused.png Binary files differdeleted file mode 100644 index 90d87598ec3..00000000000 --- a/chromium/ui/views/resources/default_100_percent/common/checkbox_focused.png +++ /dev/null diff --git a/chromium/ui/views/resources/default_100_percent/common/checkbox_focused_checked.png b/chromium/ui/views/resources/default_100_percent/common/checkbox_focused_checked.png Binary files differdeleted file mode 100644 index 0ba79b5b396..00000000000 --- a/chromium/ui/views/resources/default_100_percent/common/checkbox_focused_checked.png +++ /dev/null diff --git a/chromium/ui/views/resources/default_100_percent/common/checkbox_focused_checked_hover.png b/chromium/ui/views/resources/default_100_percent/common/checkbox_focused_checked_hover.png Binary files differdeleted file mode 100644 index 97d13b7c5c3..00000000000 --- a/chromium/ui/views/resources/default_100_percent/common/checkbox_focused_checked_hover.png +++ /dev/null diff --git a/chromium/ui/views/resources/default_100_percent/common/checkbox_focused_checked_pressed.png b/chromium/ui/views/resources/default_100_percent/common/checkbox_focused_checked_pressed.png Binary files differdeleted file mode 100644 index a1e335c58f2..00000000000 --- a/chromium/ui/views/resources/default_100_percent/common/checkbox_focused_checked_pressed.png +++ /dev/null diff --git a/chromium/ui/views/resources/default_100_percent/common/checkbox_focused_hover.png b/chromium/ui/views/resources/default_100_percent/common/checkbox_focused_hover.png Binary files differdeleted file mode 100644 index dc935b18c58..00000000000 --- a/chromium/ui/views/resources/default_100_percent/common/checkbox_focused_hover.png +++ /dev/null diff --git a/chromium/ui/views/resources/default_100_percent/common/checkbox_focused_pressed.png b/chromium/ui/views/resources/default_100_percent/common/checkbox_focused_pressed.png Binary files differdeleted file mode 100644 index 83cd62fe605..00000000000 --- a/chromium/ui/views/resources/default_100_percent/common/checkbox_focused_pressed.png +++ /dev/null diff --git a/chromium/ui/views/resources/default_100_percent/common/checkbox_hover.png b/chromium/ui/views/resources/default_100_percent/common/checkbox_hover.png Binary files differdeleted file mode 100644 index 9a5db8485d7..00000000000 --- a/chromium/ui/views/resources/default_100_percent/common/checkbox_hover.png +++ /dev/null diff --git a/chromium/ui/views/resources/default_100_percent/common/checkbox_inactive.png b/chromium/ui/views/resources/default_100_percent/common/checkbox_inactive.png Binary files differdeleted file mode 100644 index 4d964ee523c..00000000000 --- a/chromium/ui/views/resources/default_100_percent/common/checkbox_inactive.png +++ /dev/null diff --git a/chromium/ui/views/resources/default_100_percent/common/checkbox_pressed.png b/chromium/ui/views/resources/default_100_percent/common/checkbox_pressed.png Binary files differdeleted file mode 100644 index 3a1cba338f3..00000000000 --- a/chromium/ui/views/resources/default_100_percent/common/checkbox_pressed.png +++ /dev/null diff --git a/chromium/ui/views/resources/default_100_percent/common/folder_open.png b/chromium/ui/views/resources/default_100_percent/common/folder_open.png Binary files differdeleted file mode 100644 index a4a9922b765..00000000000 --- a/chromium/ui/views/resources/default_100_percent/common/folder_open.png +++ /dev/null diff --git a/chromium/ui/views/resources/default_100_percent/common/folder_open_rtl.png b/chromium/ui/views/resources/default_100_percent/common/folder_open_rtl.png Binary files differdeleted file mode 100644 index 40b4c665d84..00000000000 --- a/chromium/ui/views/resources/default_100_percent/common/folder_open_rtl.png +++ /dev/null diff --git a/chromium/ui/views/resources/default_200_percent/common/blue_button.png b/chromium/ui/views/resources/default_200_percent/common/blue_button.png Binary files differdeleted file mode 100644 index 9798989d328..00000000000 --- a/chromium/ui/views/resources/default_200_percent/common/blue_button.png +++ /dev/null diff --git a/chromium/ui/views/resources/default_200_percent/common/blue_button_focused.png b/chromium/ui/views/resources/default_200_percent/common/blue_button_focused.png Binary files differdeleted file mode 100644 index e0a65b76d4b..00000000000 --- a/chromium/ui/views/resources/default_200_percent/common/blue_button_focused.png +++ /dev/null diff --git a/chromium/ui/views/resources/default_200_percent/common/blue_button_focused_hover.png b/chromium/ui/views/resources/default_200_percent/common/blue_button_focused_hover.png Binary files differdeleted file mode 100644 index 5516ba92265..00000000000 --- a/chromium/ui/views/resources/default_200_percent/common/blue_button_focused_hover.png +++ /dev/null diff --git a/chromium/ui/views/resources/default_200_percent/common/blue_button_focused_pressed.png b/chromium/ui/views/resources/default_200_percent/common/blue_button_focused_pressed.png Binary files differdeleted file mode 100644 index 7e1ad4c35a6..00000000000 --- a/chromium/ui/views/resources/default_200_percent/common/blue_button_focused_pressed.png +++ /dev/null diff --git a/chromium/ui/views/resources/default_200_percent/common/blue_button_hover.png b/chromium/ui/views/resources/default_200_percent/common/blue_button_hover.png Binary files differdeleted file mode 100644 index f725d4ec79f..00000000000 --- a/chromium/ui/views/resources/default_200_percent/common/blue_button_hover.png +++ /dev/null diff --git a/chromium/ui/views/resources/default_200_percent/common/blue_button_inactive.png b/chromium/ui/views/resources/default_200_percent/common/blue_button_inactive.png Binary files differdeleted file mode 100644 index 63065b8f9a7..00000000000 --- a/chromium/ui/views/resources/default_200_percent/common/blue_button_inactive.png +++ /dev/null diff --git a/chromium/ui/views/resources/default_200_percent/common/blue_button_pressed.png b/chromium/ui/views/resources/default_200_percent/common/blue_button_pressed.png Binary files differdeleted file mode 100644 index 8d54141b31e..00000000000 --- a/chromium/ui/views/resources/default_200_percent/common/blue_button_pressed.png +++ /dev/null diff --git a/chromium/ui/views/resources/default_200_percent/common/button.png b/chromium/ui/views/resources/default_200_percent/common/button.png Binary files differdeleted file mode 100644 index 9845946c0f1..00000000000 --- a/chromium/ui/views/resources/default_200_percent/common/button.png +++ /dev/null diff --git a/chromium/ui/views/resources/default_200_percent/common/button_focused.png b/chromium/ui/views/resources/default_200_percent/common/button_focused.png Binary files differdeleted file mode 100644 index 72089d7edcd..00000000000 --- a/chromium/ui/views/resources/default_200_percent/common/button_focused.png +++ /dev/null diff --git a/chromium/ui/views/resources/default_200_percent/common/button_focused_hover.png b/chromium/ui/views/resources/default_200_percent/common/button_focused_hover.png Binary files differdeleted file mode 100644 index 7b4f421074c..00000000000 --- a/chromium/ui/views/resources/default_200_percent/common/button_focused_hover.png +++ /dev/null diff --git a/chromium/ui/views/resources/default_200_percent/common/button_focused_pressed.png b/chromium/ui/views/resources/default_200_percent/common/button_focused_pressed.png Binary files differdeleted file mode 100644 index c5e258b132b..00000000000 --- a/chromium/ui/views/resources/default_200_percent/common/button_focused_pressed.png +++ /dev/null diff --git a/chromium/ui/views/resources/default_200_percent/common/button_hover.png b/chromium/ui/views/resources/default_200_percent/common/button_hover.png Binary files differdeleted file mode 100644 index 81aa8ff7e67..00000000000 --- a/chromium/ui/views/resources/default_200_percent/common/button_hover.png +++ /dev/null diff --git a/chromium/ui/views/resources/default_200_percent/common/button_inactive.png b/chromium/ui/views/resources/default_200_percent/common/button_inactive.png Binary files differdeleted file mode 100644 index 34cb7827922..00000000000 --- a/chromium/ui/views/resources/default_200_percent/common/button_inactive.png +++ /dev/null diff --git a/chromium/ui/views/resources/default_200_percent/common/button_pressed.png b/chromium/ui/views/resources/default_200_percent/common/button_pressed.png Binary files differdeleted file mode 100644 index 3353d8c1807..00000000000 --- a/chromium/ui/views/resources/default_200_percent/common/button_pressed.png +++ /dev/null diff --git a/chromium/ui/views/resources/default_200_percent/common/checkbox.png b/chromium/ui/views/resources/default_200_percent/common/checkbox.png Binary files differdeleted file mode 100644 index 2192a7f7dcf..00000000000 --- a/chromium/ui/views/resources/default_200_percent/common/checkbox.png +++ /dev/null diff --git a/chromium/ui/views/resources/default_200_percent/common/checkbox_checked.png b/chromium/ui/views/resources/default_200_percent/common/checkbox_checked.png Binary files differdeleted file mode 100644 index 1d66001c440..00000000000 --- a/chromium/ui/views/resources/default_200_percent/common/checkbox_checked.png +++ /dev/null diff --git a/chromium/ui/views/resources/default_200_percent/common/checkbox_checked_hover.png b/chromium/ui/views/resources/default_200_percent/common/checkbox_checked_hover.png Binary files differdeleted file mode 100644 index c88df15b35d..00000000000 --- a/chromium/ui/views/resources/default_200_percent/common/checkbox_checked_hover.png +++ /dev/null diff --git a/chromium/ui/views/resources/default_200_percent/common/checkbox_checked_inactive.png b/chromium/ui/views/resources/default_200_percent/common/checkbox_checked_inactive.png Binary files differdeleted file mode 100644 index 85c314fd934..00000000000 --- a/chromium/ui/views/resources/default_200_percent/common/checkbox_checked_inactive.png +++ /dev/null diff --git a/chromium/ui/views/resources/default_200_percent/common/checkbox_checked_pressed.png b/chromium/ui/views/resources/default_200_percent/common/checkbox_checked_pressed.png Binary files differdeleted file mode 100644 index 00a7bf4cb31..00000000000 --- a/chromium/ui/views/resources/default_200_percent/common/checkbox_checked_pressed.png +++ /dev/null diff --git a/chromium/ui/views/resources/default_200_percent/common/checkbox_focused.png b/chromium/ui/views/resources/default_200_percent/common/checkbox_focused.png Binary files differdeleted file mode 100644 index c4d04aaf531..00000000000 --- a/chromium/ui/views/resources/default_200_percent/common/checkbox_focused.png +++ /dev/null diff --git a/chromium/ui/views/resources/default_200_percent/common/checkbox_focused_checked.png b/chromium/ui/views/resources/default_200_percent/common/checkbox_focused_checked.png Binary files differdeleted file mode 100644 index 91152f2adb0..00000000000 --- a/chromium/ui/views/resources/default_200_percent/common/checkbox_focused_checked.png +++ /dev/null diff --git a/chromium/ui/views/resources/default_200_percent/common/checkbox_focused_checked_hover.png b/chromium/ui/views/resources/default_200_percent/common/checkbox_focused_checked_hover.png Binary files differdeleted file mode 100644 index 0b5130506e9..00000000000 --- a/chromium/ui/views/resources/default_200_percent/common/checkbox_focused_checked_hover.png +++ /dev/null diff --git a/chromium/ui/views/resources/default_200_percent/common/checkbox_focused_checked_pressed.png b/chromium/ui/views/resources/default_200_percent/common/checkbox_focused_checked_pressed.png Binary files differdeleted file mode 100644 index 2a529005b72..00000000000 --- a/chromium/ui/views/resources/default_200_percent/common/checkbox_focused_checked_pressed.png +++ /dev/null diff --git a/chromium/ui/views/resources/default_200_percent/common/checkbox_focused_hover.png b/chromium/ui/views/resources/default_200_percent/common/checkbox_focused_hover.png Binary files differdeleted file mode 100644 index 91ec9f8aea8..00000000000 --- a/chromium/ui/views/resources/default_200_percent/common/checkbox_focused_hover.png +++ /dev/null diff --git a/chromium/ui/views/resources/default_200_percent/common/checkbox_focused_pressed.png b/chromium/ui/views/resources/default_200_percent/common/checkbox_focused_pressed.png Binary files differdeleted file mode 100644 index f22c98e948b..00000000000 --- a/chromium/ui/views/resources/default_200_percent/common/checkbox_focused_pressed.png +++ /dev/null diff --git a/chromium/ui/views/resources/default_200_percent/common/checkbox_hover.png b/chromium/ui/views/resources/default_200_percent/common/checkbox_hover.png Binary files differdeleted file mode 100644 index 610d864aa0b..00000000000 --- a/chromium/ui/views/resources/default_200_percent/common/checkbox_hover.png +++ /dev/null diff --git a/chromium/ui/views/resources/default_200_percent/common/checkbox_inactive.png b/chromium/ui/views/resources/default_200_percent/common/checkbox_inactive.png Binary files differdeleted file mode 100644 index 4b6933c7a9f..00000000000 --- a/chromium/ui/views/resources/default_200_percent/common/checkbox_inactive.png +++ /dev/null diff --git a/chromium/ui/views/resources/default_200_percent/common/checkbox_pressed.png b/chromium/ui/views/resources/default_200_percent/common/checkbox_pressed.png Binary files differdeleted file mode 100644 index 0e23c5a77a7..00000000000 --- a/chromium/ui/views/resources/default_200_percent/common/checkbox_pressed.png +++ /dev/null diff --git a/chromium/ui/views/resources/default_200_percent/common/folder_open.png b/chromium/ui/views/resources/default_200_percent/common/folder_open.png Binary files differdeleted file mode 100644 index 76938cab788..00000000000 --- a/chromium/ui/views/resources/default_200_percent/common/folder_open.png +++ /dev/null diff --git a/chromium/ui/views/resources/default_200_percent/common/folder_open_rtl.png b/chromium/ui/views/resources/default_200_percent/common/folder_open_rtl.png Binary files differdeleted file mode 100644 index 2d285a35a60..00000000000 --- a/chromium/ui/views/resources/default_200_percent/common/folder_open_rtl.png +++ /dev/null diff --git a/chromium/ui/views/resources/views_resources.grd b/chromium/ui/views/resources/views_resources.grd index dbd023deaf3..f1429ef2c27 100644 --- a/chromium/ui/views/resources/views_resources.grd +++ b/chromium/ui/views/resources/views_resources.grd @@ -15,27 +15,6 @@ <structure type="chrome_scaled_image" name="IDR_APP_TOP_CENTER" file="app_top_center.png" /> <structure type="chrome_scaled_image" name="IDR_APP_TOP_LEFT" file="app_top_left.png" /> <structure type="chrome_scaled_image" name="IDR_APP_TOP_RIGHT" file="app_top_right.png" /> - <structure type="chrome_scaled_image" name="IDR_BUTTON_DISABLED" file="common/button_inactive.png" /> - <structure type="chrome_scaled_image" name="IDR_BUTTON_FOCUSED_HOVER" file="common/button_focused_hover.png" /> - <structure type="chrome_scaled_image" name="IDR_BUTTON_FOCUSED_NORMAL" file="common/button_focused.png" /> - <structure type="chrome_scaled_image" name="IDR_BUTTON_FOCUSED_PRESSED" file="common/button_focused_pressed.png" /> - <structure type="chrome_scaled_image" name="IDR_BUTTON_HOVER" file="common/button_hover.png" /> - <structure type="chrome_scaled_image" name="IDR_BUTTON_NORMAL" file="common/button.png" /> - <structure type="chrome_scaled_image" name="IDR_BUTTON_PRESSED" file="common/button_pressed.png" /> - <structure type="chrome_scaled_image" name="IDR_CHECKBOX" file="common/checkbox.png" /> - <structure type="chrome_scaled_image" name="IDR_CHECKBOX_CHECKED" file="common/checkbox_checked.png" /> - <structure type="chrome_scaled_image" name="IDR_CHECKBOX_CHECKED_DISABLED" file="common/checkbox_checked_inactive.png" /> - <structure type="chrome_scaled_image" name="IDR_CHECKBOX_CHECKED_HOVER" file="common/checkbox_checked_hover.png" /> - <structure type="chrome_scaled_image" name="IDR_CHECKBOX_CHECKED_PRESSED" file="common/checkbox_checked_pressed.png" /> - <structure type="chrome_scaled_image" name="IDR_CHECKBOX_DISABLED" file="common/checkbox_inactive.png" /> - <structure type="chrome_scaled_image" name="IDR_CHECKBOX_FOCUSED" file="common/checkbox_focused.png" /> - <structure type="chrome_scaled_image" name="IDR_CHECKBOX_FOCUSED_CHECKED" file="common/checkbox_focused_checked.png" /> - <structure type="chrome_scaled_image" name="IDR_CHECKBOX_FOCUSED_CHECKED_HOVER" file="common/checkbox_focused_checked_hover.png" /> - <structure type="chrome_scaled_image" name="IDR_CHECKBOX_FOCUSED_CHECKED_PRESSED" file="common/checkbox_focused_checked_pressed.png" /> - <structure type="chrome_scaled_image" name="IDR_CHECKBOX_FOCUSED_HOVER" file="common/checkbox_focused_hover.png" /> - <structure type="chrome_scaled_image" name="IDR_CHECKBOX_FOCUSED_PRESSED" file="common/checkbox_focused_pressed.png" /> - <structure type="chrome_scaled_image" name="IDR_CHECKBOX_HOVER" file="common/checkbox_hover.png" /> - <structure type="chrome_scaled_image" name="IDR_CHECKBOX_PRESSED" file="common/checkbox_pressed.png" /> <structure type="chrome_scaled_image" name="IDR_CLOSE" file="close.png" /> <structure type="chrome_scaled_image" name="IDR_CLOSE_H" file="close_hover.png" /> <structure type="chrome_scaled_image" name="IDR_CLOSE_P" file="close_pressed.png" /> @@ -44,8 +23,6 @@ <structure type="chrome_scaled_image" name="IDR_CONTENT_BOTTOM_RIGHT_CORNER" file="content_bottom_right_corner.png" /> <structure type="chrome_scaled_image" name="IDR_CONTENT_LEFT_SIDE" file="content_left_side.png" /> <structure type="chrome_scaled_image" name="IDR_CONTENT_RIGHT_SIDE" file="content_right_side.png" /> - <structure type="chrome_scaled_image" name="IDR_FOLDER_OPEN" file="common/folder_open.png" /> - <structure type="chrome_scaled_image" name="IDR_FOLDER_OPEN_RTL" file="common/folder_open_rtl.png" /> <structure type="chrome_scaled_image" name="IDR_FRAME" file="frame_default.png" /> <structure type="chrome_scaled_image" name="IDR_FRAME_INACTIVE" file="frame_default_inactive.png" /> <structure type="chrome_scaled_image" name="IDR_MAXIMIZE" file="maximize.png" /> diff --git a/chromium/ui/views/touchui/touch_selection_controller_impl.cc b/chromium/ui/views/touchui/touch_selection_controller_impl.cc index 43d84d109d8..4162361509c 100644 --- a/chromium/ui/views/touchui/touch_selection_controller_impl.cc +++ b/chromium/ui/views/touchui/touch_selection_controller_impl.cc @@ -219,8 +219,7 @@ class TouchSelectionControllerImpl::EditingHandleView : controller_(controller), image_(GetCenterHandleImage()), is_cursor_handle_(is_cursor_handle), - draw_invisible_(false), - weak_ptr_factory_(this) { + draw_invisible_(false) { widget_.reset(CreateTouchSelectionPopupWidget(parent, this)); targeter_ = new aura::WindowTargeter(); @@ -394,7 +393,7 @@ class TouchSelectionControllerImpl::EditingHandleView // handle. bool draw_invisible_; - base::WeakPtrFactory<EditingHandleView> weak_ptr_factory_; + base::WeakPtrFactory<EditingHandleView> weak_ptr_factory_{this}; DISALLOW_COPY_AND_ASSIGN(EditingHandleView); }; diff --git a/chromium/ui/views/touchui/touch_selection_menu_views.cc b/chromium/ui/views/touchui/touch_selection_menu_views.cc index aea8ce84282..1081c830172 100644 --- a/chromium/ui/views/touchui/touch_selection_menu_views.cc +++ b/chromium/ui/views/touchui/touch_selection_menu_views.cc @@ -54,8 +54,9 @@ TouchSelectionMenuViews::TouchSelectionMenuViews( set_adjust_if_offscreen(true); EnableCanvasFlippingForRTLUI(true); - SetLayoutManager(std::make_unique<BoxLayout>( - BoxLayout::kHorizontal, gfx::Insets(), kSpacingBetweenButtons)); + SetLayoutManager( + std::make_unique<BoxLayout>(BoxLayout::Orientation::kHorizontal, + gfx::Insets(), kSpacingBetweenButtons)); } void TouchSelectionMenuViews::ShowMenu(const gfx::Rect& anchor_rect, @@ -183,4 +184,8 @@ void TouchSelectionMenuViews::ButtonPressed(Button* sender, client_->RunContextMenu(); } +BEGIN_METADATA(TouchSelectionMenuViews) +METADATA_PARENT_CLASS(BubbleDialogDelegateView) +END_METADATA() + } // namespace views diff --git a/chromium/ui/views/touchui/touch_selection_menu_views.h b/chromium/ui/views/touchui/touch_selection_menu_views.h index a9882f269a0..b396471e366 100644 --- a/chromium/ui/views/touchui/touch_selection_menu_views.h +++ b/chromium/ui/views/touchui/touch_selection_menu_views.h @@ -22,6 +22,8 @@ class LabelButton; class VIEWS_EXPORT TouchSelectionMenuViews : public BubbleDialogDelegateView, public ButtonListener { public: + METADATA_HEADER(TouchSelectionMenuViews); + TouchSelectionMenuViews(TouchSelectionMenuRunnerViews* owner, ui::TouchSelectionMenuClient* client, aura::Window* context); diff --git a/chromium/ui/views/view.cc b/chromium/ui/views/view.cc index 4e71a3d75c2..ff14cec5521 100644 --- a/chromium/ui/views/view.cc +++ b/chromium/ui/views/view.cc @@ -25,7 +25,6 @@ #include "ui/base/ime/input_method.h" #include "ui/compositor/clip_recorder.h" #include "ui/compositor/compositor.h" -#include "ui/compositor/dip_util.h" #include "ui/compositor/layer.h" #include "ui/compositor/layer_animator.h" #include "ui/compositor/paint_context.h" @@ -115,9 +114,6 @@ class ScopedChildrenLock { } // namespace internal -// static -const char View::kViewClassName[] = "View"; - //////////////////////////////////////////////////////////////////////////////// // View, public: @@ -546,8 +542,7 @@ void View::DestroyLayer() { void View::AddLayerBeneathView(ui::Layer* new_layer) { DCHECK(new_layer); - DCHECK(!base::ContainsValue(layers_beneath_, new_layer)) - << "Layer already added."; + DCHECK(!base::Contains(layers_beneath_, new_layer)) << "Layer already added."; new_layer->AddObserver(this); new_layer->SetVisible(GetVisible()); @@ -557,7 +552,9 @@ void View::AddLayerBeneathView(ui::Layer* new_layer) { // correctly. If not, this will happen on layer creation. if (layer()) { ui::Layer* parent_layer = layer()->parent(); - if (parent_layer) + // Note that |new_layer| may have already been added to the parent, for + // example when the layer of a LayerOwner is recreated. + if (parent_layer && parent_layer != new_layer->parent()) parent_layer->Add(new_layer); new_layer->SetBounds(gfx::Rect(new_layer->size()) + layer()->bounds().OffsetFromOrigin()); @@ -571,18 +568,37 @@ void View::AddLayerBeneathView(ui::Layer* new_layer) { } void View::RemoveLayerBeneathView(ui::Layer* old_layer) { + RemoveLayerBeneathViewKeepInLayerTree(old_layer); + + // Note that |old_layer| may have already been removed from its parent. + ui::Layer* parent_layer = layer()->parent(); + if (parent_layer && parent_layer == old_layer->parent()) + parent_layer->Remove(old_layer); + + CreateOrDestroyLayer(); +} + +void View::RemoveLayerBeneathViewKeepInLayerTree(ui::Layer* old_layer) { auto layer_pos = std::find(layers_beneath_.begin(), layers_beneath_.end(), old_layer); DCHECK(layer_pos != layers_beneath_.end()) << "Attempted to remove a layer that was never added."; layers_beneath_.erase(layer_pos); old_layer->RemoveObserver(this); +} - ui::Layer* parent_layer = layer()->parent(); - if (parent_layer) - parent_layer->Remove(old_layer); +std::vector<ui::Layer*> View::GetLayersInOrder() { + // If not painting to a layer, there are no layers immediately related to this + // view. + if (!layer()) + return {}; - CreateOrDestroyLayer(); + std::vector<ui::Layer*> result; + for (ui::Layer* layer_beneath : layers_beneath_) + result.push_back(layer_beneath); + result.push_back(layer()); + + return result; } void View::LayerDestroyed(ui::Layer* layer) { @@ -684,10 +700,6 @@ void View::SetLayoutManager(std::nullptr_t) { // Attributes ------------------------------------------------------------------ -const char* View::GetClassName() const { - return kViewClassName; -} - const View* View::GetAncestorWithClassName(const std::string& name) const { for (const View* view = this; view; view = view->parent_) { if (!strcmp(view->GetClassName(), name.c_str())) @@ -980,8 +992,9 @@ void View::Paint(const PaintInfo& parent_paint_info) { paint_info.paint_recording_scale_y(), &paint_cache_); gfx::Canvas* canvas = recorder.canvas(); - gfx::ScopedRTLFlipCanvas scoped_canvas(canvas, width(), - flip_canvas_on_paint_for_rtl_ui_); + gfx::ScopedCanvas scoped_canvas(canvas); + if (flip_canvas_on_paint_for_rtl_ui_) + scoped_canvas.FlipIfRTL(width()); // Delegate painting the contents of the View to the virtual OnPaint method. OnPaint(canvas); @@ -998,6 +1011,17 @@ void View::SetBackground(std::unique_ptr<Background> b) { void View::SetBorder(std::unique_ptr<Border> b) { border_ = std::move(b); + + // Conceptually, this should be PreferredSizeChanged(), but for some view + // hierarchies that triggers synchronous add/remove operations that are unsafe + // in some contexts where SetBorder is called. + // + // InvalidateLayout() still triggers a re-layout of the view, which should + // include re-querying its preferred size so in practice this is both safe and + // has the intended effect. + InvalidateLayout(); + + SchedulePaint(); } const ui::ThemeProvider* View::GetThemeProvider() const { @@ -1264,7 +1288,7 @@ void View::AddAccelerator(const ui::Accelerator& accelerator) { if (!accelerators_) accelerators_ = std::make_unique<std::vector<ui::Accelerator>>(); - if (!base::ContainsValue(*accelerators_, accelerator)) + if (!base::Contains(*accelerators_, accelerator)) accelerators_->push_back(accelerator); RegisterPendingAccelerators(); @@ -1675,14 +1699,11 @@ void View::UpdateParentLayer() { return; ui::Layer* parent_layer = nullptr; - gfx::Vector2d offset(GetMirroredX(), y()); - if (parent_) { - offset += - parent_->CalculateOffsetToAncestorWithLayer(&parent_layer).offset(); - } + if (parent_) + parent_->CalculateOffsetToAncestorWithLayer(&parent_layer); - ReparentLayer(offset, parent_layer); + ReparentLayer(parent_layer); } void View::MoveLayerToParent(ui::Layer* parent_layer, @@ -1963,6 +1984,7 @@ void View::HandlePropertyChangeEffects(PropertyEffects effects) { InvalidateLayout(); if (effects & kPropertyEffectsPaint) SchedulePaint(); + OnHandlePropertyChangeEffects(effects); } PropertyChangedSubscription View::AddPropertyChangedCallback( @@ -2168,6 +2190,11 @@ void View::AddChildViewAtImpl(View* view, int index) { view->PropagateThemeChanged(); } + // Need to notify the layout manager because one of the callbacks below might + // want to know the view's new preferred size, minimum size, etc. + if (layout_manager_) + layout_manager_->ViewAdded(this, view); + ViewHierarchyChangedDetails details(true, this, view, parent); for (View* v = this; v; v = v->parent_) @@ -2184,9 +2211,6 @@ void View::AddChildViewAtImpl(View* view, int index) { view->SchedulePaint(); } - if (layout_manager_) - layout_manager_->ViewAdded(this, view); - for (ViewObserver& observer : observers_) observer.OnChildViewAdded(this, view); } @@ -2230,6 +2254,11 @@ void View::DoRemoveChildView(View* view, if (widget) widget->LayerTreeChanged(); + // Need to notify the layout manager because one of the callbacks below might + // want to know the view's new preferred size, minimum size, etc. + if (layout_manager_) + layout_manager_->ViewRemoved(this, view); + view->PropagateRemoveNotifications(this, new_parent, is_removed_from_widget); view->parent_ = nullptr; @@ -2244,9 +2273,6 @@ void View::DoRemoveChildView(View* view, if (update_tool_tip) UpdateTooltip(); - if (layout_manager_) - layout_manager_->ViewRemoved(this, view); - for (ViewObserver& observer : observers_) observer.OnChildViewRemoved(this, view); } @@ -2353,10 +2379,6 @@ void View::SnapLayerToPixelBoundary(const LayerOffsetData& offset_data) { for (ui::Layer* layer_beneath : layers_beneath_) layer_beneath->SetSubpixelPositionOffset( offset_data.GetSubpixelOffset()); - } else { - ui::SnapLayerToPhysicalPixelBoundary(layer()->parent(), layer()); - for (ui::Layer* layer_beneath : layers_beneath_) - ui::SnapLayerToPhysicalPixelBoundary(layer()->parent(), layer_beneath); } } else { // Reset the offset. @@ -2432,12 +2454,17 @@ void View::SetLayoutManagerImpl(std::unique_ptr<LayoutManager> layout_manager) { void View::SetLayerBounds(const gfx::Size& size, const LayerOffsetData& offset_data) { const gfx::Rect bounds = gfx::Rect(size) + offset_data.offset(); + const bool bounds_changed = (bounds != layer()->GetTargetBounds()); layer()->SetBounds(bounds); for (ui::Layer* layer_beneath : layers_beneath_) { layer_beneath->SetBounds(gfx::Rect(layer_beneath->size()) + bounds.OffsetFromOrigin()); } SnapLayerToPixelBoundary(offset_data); + if (bounds_changed) { + for (ViewObserver& observer : observers_) + observer.OnLayerTargetBoundsChanged(this); + } } // Transformations ------------------------------------------------------------- @@ -2572,11 +2599,7 @@ void View::OrphanLayers() { child->OrphanLayers(); } -void View::ReparentLayer(const gfx::Vector2d& offset, ui::Layer* parent_layer) { - layer()->SetBounds(GetLocalBounds() + offset); - for (ui::Layer* layer_beneath : layers_beneath_) - layer_beneath->SetBounds(gfx::Rect(layer_beneath->size()) + offset); - +void View::ReparentLayer(ui::Layer* parent_layer) { DCHECK_NE(layer(), parent_layer); if (parent_layer) { // Adding the main layer can trigger a call to |SnapLayerToPixelBoundary()|. @@ -2586,6 +2609,13 @@ void View::ReparentLayer(const gfx::Vector2d& offset, ui::Layer* parent_layer) { parent_layer->Add(layer_beneath); parent_layer->Add(layer()); } + // Update the layer bounds; this needs to be called after this layer is added + // to the new parent layer since snapping to pixel boundary will be affected + // by the layer hierarchy. + LayerOffsetData offset = + parent_ ? parent_->CalculateOffsetToAncestorWithLayer(nullptr) + : LayerOffsetData(layer()->device_scale_factor()); + SetLayerBounds(size(), offset + GetMirroredBounds().OffsetFromOrigin()); layer()->SchedulePaint(GetLocalBounds()); MoveLayerToParent(layer(), LayerOffsetData(layer()->device_scale_factor())); } @@ -2830,14 +2860,15 @@ bool View::DoDrag(const ui::LocatedEvent& event, if (widget->dragged_view()) return false; - OSExchangeData data; - WriteDragData(press_pt, &data); + std::unique_ptr<OSExchangeData> data(std::make_unique<OSExchangeData>()); + WriteDragData(press_pt, data.get()); // Message the RootView to do the drag and drop. That way if we're removed // the RootView can detect it and avoid calling us back. gfx::Point widget_location(event.location()); ConvertPointToWidget(this, &widget_location); - widget->RunShellDrag(this, data, widget_location, drag_operations, source); + widget->RunShellDrag(this, std::move(data), widget_location, drag_operations, + source); // WARNING: we may have been deleted. return true; } diff --git a/chromium/ui/views/view.h b/chromium/ui/views/view.h index 3d90ae1c174..75eecfb67d0 100644 --- a/chromium/ui/views/view.h +++ b/chromium/ui/views/view.h @@ -44,6 +44,7 @@ #include "ui/gfx/geometry/vector2d.h" #include "ui/gfx/native_widget_types.h" #include "ui/views/metadata/metadata_header_macros.h" +#include "ui/views/metadata/metadata_impl_macros.h" #include "ui/views/paint_info.h" #include "ui/views/view_targeter.h" #include "ui/views/views_export.h" @@ -278,7 +279,7 @@ class VIEWS_EXPORT View : public ui::LayerDelegate, public: using Views = std::vector<View*>; - METADATA_HEADER(View); + METADATA_HEADER_BASE(View); enum class FocusBehavior { // Use when the View is never focusable. Default. @@ -630,6 +631,21 @@ class VIEWS_EXPORT View : public ui::LayerDelegate, virtual void AddLayerBeneathView(ui::Layer* new_layer); virtual void RemoveLayerBeneathView(ui::Layer* old_layer); + // This is like RemoveLayerBeneathView() but doesn't remove |old_layer| from + // its parent. This is useful for when a layer beneth this view is owned by a + // ui::LayerOwner which just recreated it (by calling RecreateLayer()). In + // this case, this function can be called to remove it from |layers_beneath_|, + // and to stop observing it, but it remains in the layer tree since the + // expectation of ui::LayerOwner::RecreateLayer() is that the old layer + // remains under the same parent, and stacked above the newly cloned layer. + void RemoveLayerBeneathViewKeepInLayerTree(ui::Layer* old_layer); + + // Gets the layers associated with this view that should be immediate children + // of the parent layer. They are returned in bottom-to-top order. This + // includes |this->layer()| and any layers added with |AddLayerBeneathView()|. + // Returns an empty vector if this view doesn't paint to a layer. + std::vector<ui::Layer*> GetLayersInOrder(); + // ui::LayerObserver: void LayerDestroyed(ui::Layer* layer) override; @@ -723,15 +739,6 @@ class VIEWS_EXPORT View : public ui::LayerDelegate, // Attributes ---------------------------------------------------------------- - // The view class name. - static const char kViewClassName[]; - - // Return the receiving view's class name. A view class is a string which - // uniquely identifies the view class. It is intended to be used as a way to - // find out during run time if a view can be safely cast to a specific view - // subclass. The default implementation returns kViewClassName. - virtual const char* GetClassName() const; - // Returns the first ancestor, starting at this, whose class name is |name|. // Returns null if no ancestor has the class name |name|. const View* GetAncestorWithClassName(const std::string& name) const; @@ -1580,6 +1587,11 @@ class VIEWS_EXPORT View : public ui::LayerDelegate, void OnPropertyChanged(PropertyKey property, PropertyEffects property_effects); + // Empty function called in HandlePropertyChangeEffects to be overridden in + // subclasses if they have custom functions for property changes. + virtual void OnHandlePropertyChangeEffects(PropertyEffects property_effects) { + } + private: friend class internal::PreEventDispatchHandler; friend class internal::PostEventDispatchHandler; @@ -1746,9 +1758,8 @@ class VIEWS_EXPORT View : public ui::LayerDelegate, bool UpdateParentLayers(); // Parents this view's layer to |parent_layer|, and sets its bounds and other - // properties in accordance to |offset|, the view's offset from the - // |parent_layer|. - void ReparentLayer(const gfx::Vector2d& offset, ui::Layer* parent_layer); + // properties in accordance to the layer hierarchy. + void ReparentLayer(ui::Layer* parent_layer); // Called to update the layer visibility. The layer will be visible if the // View itself, and all its parent Views are visible. This also updates diff --git a/chromium/ui/views/view_class_properties.cc b/chromium/ui/views/view_class_properties.cc index 3a840009300..f226564cded 100644 --- a/chromium/ui/views/view_class_properties.cc +++ b/chromium/ui/views/view_class_properties.cc @@ -7,6 +7,7 @@ #include "ui/base/hit_test.h" #include "ui/gfx/geometry/insets.h" #include "ui/views/bubble/bubble_dialog_delegate_view.h" +#include "ui/views/layout/flex_layout_types.h" #if !defined(USE_AURA) // aura_constants.cc also declared the bool and int[32_t] @@ -21,6 +22,7 @@ DEFINE_EXPORTED_UI_CLASS_PROPERTY_TYPE(VIEWS_EXPORT, views::BubbleDialogDelegateView*) DEFINE_EXPORTED_UI_CLASS_PROPERTY_TYPE(VIEWS_EXPORT, SkPath*) +DEFINE_EXPORTED_UI_CLASS_PROPERTY_TYPE(VIEWS_EXPORT, views::FlexSpecification*) namespace views { @@ -31,5 +33,6 @@ DEFINE_UI_CLASS_PROPERTY_KEY(views::BubbleDialogDelegateView*, kAnchoredDialogKey, nullptr) DEFINE_OWNED_UI_CLASS_PROPERTY_KEY(SkPath, kHighlightPathKey, nullptr) +DEFINE_OWNED_UI_CLASS_PROPERTY_KEY(FlexSpecification, kFlexBehaviorKey, nullptr) } // namespace views diff --git a/chromium/ui/views/view_class_properties.h b/chromium/ui/views/view_class_properties.h index e6f67826fb7..a92096e9b85 100644 --- a/chromium/ui/views/view_class_properties.h +++ b/chromium/ui/views/view_class_properties.h @@ -17,6 +17,7 @@ class Insets; namespace views { class BubbleDialogDelegateView; +class FlexSpecification; // The hit test component (e.g. HTCLIENT) for a View in a window frame. Defaults // to HTNOWHERE. @@ -49,6 +50,11 @@ VIEWS_EXPORT extern const ui::ClassProperty<BubbleDialogDelegateView*>* const // the view in different ways. VIEWS_EXPORT extern const ui::ClassProperty<SkPath*>* const kHighlightPathKey; +// A property to store how a view should flex when placed in a layout. +// Currently only supported by FlexLayout. +VIEWS_EXPORT extern const ui::ClassProperty<FlexSpecification*>* const + kFlexBehaviorKey; + } // namespace views // Declaring the template specialization here to make sure that the @@ -60,4 +66,5 @@ DECLARE_EXPORTED_UI_CLASS_PROPERTY_TYPE(VIEWS_EXPORT, gfx::Insets*) DECLARE_EXPORTED_UI_CLASS_PROPERTY_TYPE(VIEWS_EXPORT, views::BubbleDialogDelegateView*) DECLARE_EXPORTED_UI_CLASS_PROPERTY_TYPE(VIEWS_EXPORT, SkPath*) +DECLARE_EXPORTED_UI_CLASS_PROPERTY_TYPE(VIEWS_EXPORT, views::FlexSpecification*) #endif // UI_VIEWS_VIEW_CLASS_PROPERTIES_H_ diff --git a/chromium/ui/views/view_observer.h b/chromium/ui/views/view_observer.h index a33f177eb8f..f8ea6c4be4c 100644 --- a/chromium/ui/views/view_observer.h +++ b/chromium/ui/views/view_observer.h @@ -34,6 +34,9 @@ class VIEWS_EXPORT ViewObserver { // Called when the bounds of |observed_view| change. virtual void OnViewBoundsChanged(View* observed_view) {} + // Called when the bounds of |observed_view|'s layer change. + virtual void OnLayerTargetBoundsChanged(View* observed_view) {} + // Called when View::ViewHierarchyChanged() is called. virtual void OnViewHierarchyChanged( View* observed_view, diff --git a/chromium/ui/views/view_unittest.cc b/chromium/ui/views/view_unittest.cc index dbca39a1ba7..93549b36eda 100644 --- a/chromium/ui/views/view_unittest.cc +++ b/chromium/ui/views/view_unittest.cc @@ -1998,21 +1998,21 @@ TEST_F(ViewTest, TextfieldCutCopyPaste) { normal->SelectAll(false); normal->ExecuteCommand(IDS_APP_CUT, 0); base::string16 result; - clipboard->ReadText(ui::CLIPBOARD_TYPE_COPY_PASTE, &result); + clipboard->ReadText(ui::ClipboardType::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); result.clear(); - clipboard->ReadText(ui::CLIPBOARD_TYPE_COPY_PASTE, &result); + clipboard->ReadText(ui::ClipboardType::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); result.clear(); - clipboard->ReadText(ui::CLIPBOARD_TYPE_COPY_PASTE, &result); + clipboard->ReadText(ui::ClipboardType::kCopyPaste, &result); // Cut should have failed, so the clipboard content should not have changed. EXPECT_EQ(kNormalText, result); @@ -2024,19 +2024,19 @@ TEST_F(ViewTest, TextfieldCutCopyPaste) { read_only->SelectAll(false); read_only->ExecuteCommand(IDS_APP_COPY, 0); result.clear(); - clipboard->ReadText(ui::CLIPBOARD_TYPE_COPY_PASTE, &result); + clipboard->ReadText(ui::ClipboardType::kCopyPaste, &result); EXPECT_EQ(kReadOnlyText, result); normal->SelectAll(false); normal->ExecuteCommand(IDS_APP_COPY, 0); result.clear(); - clipboard->ReadText(ui::CLIPBOARD_TYPE_COPY_PASTE, &result); + clipboard->ReadText(ui::ClipboardType::kCopyPaste, &result); EXPECT_EQ(kNormalText, result); password->SelectAll(false); password->ExecuteCommand(IDS_APP_COPY, 0); result.clear(); - clipboard->ReadText(ui::CLIPBOARD_TYPE_COPY_PASTE, &result); + clipboard->ReadText(ui::ClipboardType::kCopyPaste, &result); // Text cannot be copied from an obscured field; the clipboard won't change. EXPECT_EQ(kNormalText, result); @@ -3648,8 +3648,8 @@ TEST_F(ViewTest, GetViewByID) { View::Views views; v1.GetViewsInGroup(kGroup, &views); EXPECT_EQ(2U, views.size()); - EXPECT_TRUE(base::ContainsValue(views, &v3)); - EXPECT_TRUE(base::ContainsValue(views, &v4)); + EXPECT_TRUE(base::Contains(views, &v3)); + EXPECT_TRUE(base::Contains(views, &v4)); } TEST_F(ViewTest, AddExistingChild) { @@ -3778,6 +3778,31 @@ void TestLayerAnimator::SetBounds(const gfx::Rect& bounds) { last_bounds_ = bounds; } +class TestingLayerViewObserver : public ViewObserver { + public: + explicit TestingLayerViewObserver(View* view) : view_(view) { + view_->AddObserver(this); + } + ~TestingLayerViewObserver() override { view_->RemoveObserver(this); } + + gfx::Rect GetLastLayerBoundsAndReset() { + gfx::Rect value = last_layer_bounds_; + last_layer_bounds_ = gfx::Rect(); + return value; + } + + private: + // ViewObserver: + void OnLayerTargetBoundsChanged(View* view) override { + last_layer_bounds_ = view->layer()->bounds(); + } + + gfx::Rect last_layer_bounds_; + View* view_; + + DISALLOW_COPY_AND_ASSIGN(TestingLayerViewObserver); +}; + } // namespace class ViewLayerTest : public ViewsTestBase { @@ -3905,6 +3930,7 @@ TEST_F(ViewLayerTest, LayerToggling) { // Create v2 as a child of v1 and do basic assertion testing. View* v2 = new View; + TestingLayerViewObserver v2_observer(v2); v1->AddChildView(v2); EXPECT_TRUE(v2->layer() == nullptr); v2->SetBoundsRect(gfx::Rect(10, 20, 30, 40)); @@ -3912,6 +3938,7 @@ TEST_F(ViewLayerTest, LayerToggling) { ASSERT_TRUE(v2->layer() != nullptr); EXPECT_EQ(v1->layer(), v2->layer()->parent()); EXPECT_EQ(gfx::Rect(10, 20, 30, 40), v2->layer()->bounds()); + EXPECT_EQ(v2->layer()->bounds(), v2_observer.GetLastLayerBoundsAndReset()); // Turn off v1s layer. v2 should still have a layer but its parent should have // changed. @@ -3924,6 +3951,7 @@ TEST_F(ViewLayerTest, LayerToggling) { // The bounds of the layer should have changed to be relative to the root view // now. EXPECT_EQ(gfx::Rect(30, 50, 30, 40), v2->layer()->bounds()); + EXPECT_EQ(v2->layer()->bounds(), v2_observer.GetLastLayerBoundsAndReset()); // Make v1 have a layer again and verify v2s layer is wired up correctly. gfx::Transform transform; @@ -3938,6 +3966,7 @@ TEST_F(ViewLayerTest, LayerToggling) { ASSERT_EQ(1u, v1->layer()->children().size()); EXPECT_EQ(v1->layer()->children()[0], v2->layer()); EXPECT_EQ(gfx::Rect(10, 20, 30, 40), v2->layer()->bounds()); + EXPECT_EQ(v2->layer()->bounds(), v2_observer.GetLastLayerBoundsAndReset()); } // Verifies turning on a layer wires up children correctly. @@ -3950,15 +3979,20 @@ TEST_F(ViewLayerTest, NestedLayerToggling) { v1->SetBoundsRect(gfx::Rect(20, 30, 140, 150)); View* v2 = v1->AddChildView(std::make_unique<View>()); + v2->SetBoundsRect(gfx::Rect(10, 10, 100, 100)); View* v3 = v2->AddChildView(std::make_unique<View>()); + TestingLayerViewObserver v3_observer(v3); + v3->SetBoundsRect(gfx::Rect(0, 0, 100, 100)); v3->SetPaintToLayer(); ASSERT_TRUE(v3->layer() != nullptr); + EXPECT_EQ(v3->layer()->bounds(), v3_observer.GetLastLayerBoundsAndReset()); // At this point we have v1-v2-v3. v3 has a layer, v1 and v2 don't. v1->SetPaintToLayer(); EXPECT_EQ(v1->layer(), v3->layer()->parent()); + EXPECT_EQ(v3->layer()->bounds(), v3_observer.GetLastLayerBoundsAndReset()); } TEST_F(ViewLayerTest, LayerAnimator) { @@ -3989,25 +4023,31 @@ TEST_F(ViewLayerTest, BoundsChangeWithLayer) { v1->SetBoundsRect(gfx::Rect(20, 30, 140, 150)); View* v2 = v1->AddChildView(std::make_unique<View>()); + TestingLayerViewObserver v2_observer(v2); v2->SetBoundsRect(gfx::Rect(10, 11, 40, 50)); v2->SetPaintToLayer(); ASSERT_TRUE(v2->layer() != nullptr); EXPECT_EQ(gfx::Rect(30, 41, 40, 50), v2->layer()->bounds()); + EXPECT_EQ(v2->layer()->bounds(), v2_observer.GetLastLayerBoundsAndReset()); v1->SetPosition(gfx::Point(25, 36)); EXPECT_EQ(gfx::Rect(35, 47, 40, 50), v2->layer()->bounds()); + EXPECT_EQ(v2->layer()->bounds(), v2_observer.GetLastLayerBoundsAndReset()); v2->SetPosition(gfx::Point(11, 12)); EXPECT_EQ(gfx::Rect(36, 48, 40, 50), v2->layer()->bounds()); + EXPECT_EQ(v2->layer()->bounds(), v2_observer.GetLastLayerBoundsAndReset()); // Bounds of the layer should change even if the view is not invisible. v1->SetVisible(false); v1->SetPosition(gfx::Point(20, 30)); EXPECT_EQ(gfx::Rect(31, 42, 40, 50), v2->layer()->bounds()); + EXPECT_EQ(v2->layer()->bounds(), v2_observer.GetLastLayerBoundsAndReset()); v2->SetVisible(false); v2->SetBoundsRect(gfx::Rect(10, 11, 20, 30)); EXPECT_EQ(gfx::Rect(30, 41, 20, 30), v2->layer()->bounds()); + EXPECT_EQ(v2->layer()->bounds(), v2_observer.GetLastLayerBoundsAndReset()); } // Make sure layers are positioned correctly in RTL. @@ -4523,31 +4563,31 @@ TEST_F(ViewLayerTest, SnapLayerToPixel) { v1->SetBoundsRect(gfx::Rect(1, 1, 10, 10)); v11->SetPaintToLayer(); - EXPECT_EQ("0.40 0.40", ToString(v11->layer()->subpixel_position_offset())); + EXPECT_EQ("0.40 0.40", ToString(v11->layer()->GetSubpixelOffset())); // Creating a layer in parent should update the child view's layer offset. v1->SetPaintToLayer(); - EXPECT_EQ("-0.20 -0.20", ToString(v1->layer()->subpixel_position_offset())); - EXPECT_EQ("-0.20 -0.20", ToString(v11->layer()->subpixel_position_offset())); + EXPECT_EQ("-0.20 -0.20", ToString(v1->layer()->GetSubpixelOffset())); + EXPECT_EQ("-0.20 -0.20", ToString(v11->layer()->GetSubpixelOffset())); // DSF change should get propagated and update offsets. GetRootLayer()->GetCompositor()->SetScaleAndSize( 1.5f, size, allocator.GetCurrentLocalSurfaceIdAllocation()); - EXPECT_EQ("0.33 0.33", ToString(v1->layer()->subpixel_position_offset())); - EXPECT_EQ("0.33 0.33", ToString(v11->layer()->subpixel_position_offset())); + EXPECT_EQ("0.33 0.33", ToString(v1->layer()->GetSubpixelOffset())); + EXPECT_EQ("0.33 0.33", ToString(v11->layer()->GetSubpixelOffset())); // Deleting parent's layer should update the child view's layer's offset. v1->DestroyLayer(); - EXPECT_EQ("0.00 0.00", ToString(v11->layer()->subpixel_position_offset())); + EXPECT_EQ("0.00 0.00", ToString(v11->layer()->GetSubpixelOffset())); // Setting parent view should update the child view's layer's offset. v1->SetBoundsRect(gfx::Rect(2, 2, 10, 10)); - EXPECT_EQ("0.33 0.33", ToString(v11->layer()->subpixel_position_offset())); + EXPECT_EQ("0.33 0.33", ToString(v11->layer()->GetSubpixelOffset())); // Setting integral DSF should reset the offset. GetRootLayer()->GetCompositor()->SetScaleAndSize( 2.0f, size, allocator.GetCurrentLocalSurfaceIdAllocation()); - EXPECT_EQ("0.00 0.00", ToString(v11->layer()->subpixel_position_offset())); + EXPECT_EQ("0.00 0.00", ToString(v11->layer()->GetSubpixelOffset())); } TEST_F(ViewLayerTest, LayerBeneathTriggersPaintToLayer) { @@ -4601,9 +4641,8 @@ TEST_F(ViewLayerTest, LayerBeneathAtFractionalScale) { view->AddLayerBeneathView(&layer); view->SetBoundsRect(gfx::Rect(1, 1, 10, 10)); - EXPECT_NE(gfx::Vector2dF(), view->layer()->subpixel_position_offset()); - EXPECT_EQ(view->layer()->subpixel_position_offset(), - layer.subpixel_position_offset()); + EXPECT_NE(gfx::Vector2dF(), view->layer()->GetSubpixelOffset()); + EXPECT_EQ(view->layer()->GetSubpixelOffset(), layer.GetSubpixelOffset()); view->RemoveLayerBeneathView(&layer); } @@ -4873,19 +4912,19 @@ TEST_F(ViewLayerPixelCanvasTest, SnapLayerToPixel) { v1->SetBoundsRect(gfx::Rect(9, 9, 100, 100)); PaintRecordingSizeTest(v3, gfx::Size(21, 8)); // Enclosing Rect = (21, 8) - EXPECT_EQ("-0.63 -0.25", ToString(v3->layer()->subpixel_position_offset())); + EXPECT_EQ("-0.63 -0.25", ToString(v3->layer()->GetSubpixelOffset())); // Creating a layer in parent should update the child view's layer offset. v1->SetPaintToLayer(); - EXPECT_EQ("-0.25 -0.25", ToString(v1->layer()->subpixel_position_offset())); - EXPECT_EQ("-0.37 -0.00", ToString(v3->layer()->subpixel_position_offset())); + EXPECT_EQ("-0.25 -0.25", ToString(v1->layer()->GetSubpixelOffset())); + EXPECT_EQ("-0.37 -0.00", ToString(v3->layer()->GetSubpixelOffset())); // DSF change should get propagated and update offsets. GetRootLayer()->GetCompositor()->SetScaleAndSize( 1.5f, size, allocator.GetCurrentLocalSurfaceIdAllocation()); - EXPECT_EQ("0.33 0.33", ToString(v1->layer()->subpixel_position_offset())); - EXPECT_EQ("0.33 0.67", ToString(v3->layer()->subpixel_position_offset())); + EXPECT_EQ("0.33 0.33", ToString(v1->layer()->GetSubpixelOffset())); + EXPECT_EQ("0.33 0.67", ToString(v3->layer()->GetSubpixelOffset())); v1->DestroyLayer(); PaintRecordingSizeTest(v3, gfx::Size(20, 7)); // Enclosing Rect = (20, 8) @@ -4894,23 +4933,23 @@ TEST_F(ViewLayerPixelCanvasTest, SnapLayerToPixel) { GetRootLayer()->GetCompositor()->SetScaleAndSize( 1.33f, size, allocator.GetCurrentLocalSurfaceIdAllocation()); - EXPECT_EQ("0.02 0.02", ToString(v1->layer()->subpixel_position_offset())); - EXPECT_EQ("0.05 -0.45", ToString(v3->layer()->subpixel_position_offset())); + EXPECT_EQ("0.02 0.02", ToString(v1->layer()->GetSubpixelOffset())); + EXPECT_EQ("0.05 -0.45", ToString(v3->layer()->GetSubpixelOffset())); v1->DestroyLayer(); PaintRecordingSizeTest(v3, gfx::Size(17, 7)); // Enclosing Rect = (18, 7) // Deleting parent's layer should update the child view's layer's offset. - EXPECT_EQ("0.08 -0.43", ToString(v3->layer()->subpixel_position_offset())); + EXPECT_EQ("0.08 -0.43", ToString(v3->layer()->GetSubpixelOffset())); // Setting parent view should update the child view's layer's offset. v1->SetBoundsRect(gfx::Rect(3, 3, 10, 10)); - EXPECT_EQ("0.06 -0.44", ToString(v3->layer()->subpixel_position_offset())); + EXPECT_EQ("0.06 -0.44", ToString(v3->layer()->GetSubpixelOffset())); // Setting integral DSF should reset the offset. GetRootLayer()->GetCompositor()->SetScaleAndSize( 2.0f, size, allocator.GetCurrentLocalSurfaceIdAllocation()); - EXPECT_EQ("0.00 0.00", ToString(v3->layer()->subpixel_position_offset())); + EXPECT_EQ("0.00 0.00", ToString(v3->layer()->GetSubpixelOffset())); } TEST_F(ViewLayerPixelCanvasTest, LayerBeneathOnPixelCanvas) { @@ -4929,9 +4968,8 @@ TEST_F(ViewLayerPixelCanvasTest, LayerBeneathOnPixelCanvas) { view->AddLayerBeneathView(&layer); view->SetBoundsRect(gfx::Rect(1, 1, 10, 10)); - EXPECT_NE(gfx::Vector2dF(), view->layer()->subpixel_position_offset()); - EXPECT_EQ(view->layer()->subpixel_position_offset(), - layer.subpixel_position_offset()); + EXPECT_NE(gfx::Vector2dF(), view->layer()->GetSubpixelOffset()); + EXPECT_EQ(view->layer()->GetSubpixelOffset(), layer.GetSubpixelOffset()); view->RemoveLayerBeneathView(&layer); } diff --git a/chromium/ui/views/views_features.cc b/chromium/ui/views/views_features.cc index 500931f755d..442002849ee 100644 --- a/chromium/ui/views/views_features.cc +++ b/chromium/ui/views/views_features.cc @@ -11,6 +11,12 @@ namespace features { // Please keep alphabetized. +#if defined(OS_WIN) +// Uses aura tooltips instead of the native comctl32 tooltips on Windows. +const base::Feature kEnableAuraTooltipsOnWindows{ + "EnableAuraTooltipsOnWindows", base::FEATURE_ENABLED_BY_DEFAULT}; +#endif // OS_WIN + // Increases corner radius on Dialogs for the material design refresh. // TODO(sajadm): Remove this feature flag when platform inconsistencies // have been fixed as recorded on: https://crbug.com/932970 diff --git a/chromium/ui/views/views_features.h b/chromium/ui/views/views_features.h index 0df9cff6a38..629df274b11 100644 --- a/chromium/ui/views/views_features.h +++ b/chromium/ui/views/views_features.h @@ -6,12 +6,17 @@ #define UI_VIEWS_VIEWS_FEATURES_H_ #include "base/feature_list.h" +#include "build/build_config.h" #include "ui/views/views_export.h" namespace views { namespace features { // Please keep alphabetized. +#if defined(OS_WIN) +VIEWS_EXPORT extern const base::Feature kEnableAuraTooltipsOnWindows; +#endif // OS_WIN + VIEWS_EXPORT extern const base::Feature kEnableMDRoundedCornersOnDialogs; } // namespace features 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 ecd4604482b..121481d6e9a 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 @@ -24,12 +24,12 @@ #include "ui/base/dragdrop/os_exchange_data_provider_aurax11.h" #include "ui/base/layout.h" #include "ui/base/x/selection_utils.h" -#include "ui/base/x/x11_window_event_manager.h" #include "ui/display/screen.h" #include "ui/events/event.h" #include "ui/events/event_utils.h" #include "ui/events/platform/platform_event_source.h" #include "ui/events/platform_event.h" +#include "ui/events/x/x11_window_event_manager.h" #include "ui/gfx/image/image_skia.h" #include "ui/gfx/x/x11.h" #include "ui/gfx/x/x11_atom_cache.h" @@ -679,11 +679,11 @@ void DesktopDragDropClientAuraX11::OnXdndDrop( aura::client::DragDropDelegate* delegate = aura::client::GetDragDropDelegate(target_window_); if (delegate) { - ui::OSExchangeData data( + auto data(std::make_unique<ui::OSExchangeData>( std::make_unique<ui::OSExchangeDataProviderAuraX11>( - xwindow_, target_current_context_->fetched_targets())); + xwindow_, target_current_context_->fetched_targets()))); - ui::DropTargetEvent event(data, + ui::DropTargetEvent event(*data.get(), gfx::PointF(target_window_location_), gfx::PointF(target_window_root_location_), target_current_context_->GetDragOperation()); @@ -698,7 +698,7 @@ void DesktopDragDropClientAuraX11::OnXdndDrop( UMA_HISTOGRAM_COUNTS_1M("Event.DragDrop.ExternalOriginDrop", 1); } - drag_operation = delegate->OnPerformDrop(event); + drag_operation = delegate->OnPerformDrop(event, std::move(data)); } target_window_->RemoveObserver(this); @@ -729,7 +729,7 @@ void DesktopDragDropClientAuraX11::OnSelectionNotify( } int DesktopDragDropClientAuraX11::StartDragAndDrop( - const ui::OSExchangeData& data, + std::unique_ptr<ui::OSExchangeData> data, aura::Window* root_window, aura::Window* source_window, const gfx::Point& screen_location, @@ -748,7 +748,7 @@ int DesktopDragDropClientAuraX11::StartDragAndDrop( drag_operation_ = operation; negotiated_operation_ = ui::DragDropTypes::DRAG_NONE; - const ui::OSExchangeData::Provider* provider = &data.provider(); + const ui::OSExchangeData::Provider* provider = &data->provider(); source_provider_ = static_cast<const ui::OSExchangeDataProviderAuraX11*>( provider); 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 de855088d25..fea36f36f46 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 @@ -82,7 +82,7 @@ class VIEWS_EXPORT DesktopDragDropClientAuraX11 void OnSelectionNotify(const XSelectionEvent& xselection); // Overridden from aura::client::DragDropClient: - int StartDragAndDrop(const ui::OSExchangeData& data, + int StartDragAndDrop(std::unique_ptr<ui::OSExchangeData> data, aura::Window* root_window, aura::Window* source_window, const gfx::Point& screen_location, 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 ef13a2b4382..df0755982e0 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 @@ -361,20 +361,17 @@ class DesktopDragDropClientAuraX11Test : public ViewsTestBase { ~DesktopDragDropClientAuraX11Test() override = default; int StartDragAndDrop() { - ui::OSExchangeData data; - data.SetString(base::ASCIIToUTF16("Test")); + auto data(std::make_unique<ui::OSExchangeData>()); + data->SetString(base::ASCIIToUTF16("Test")); SkBitmap drag_bitmap; drag_bitmap.allocN32Pixels(10, 10); drag_bitmap.eraseARGB(0xFF, 0, 0, 0); gfx::ImageSkia drag_image(gfx::ImageSkia::CreateFrom1xBitmap(drag_bitmap)); - data.provider().SetDragImage(drag_image, gfx::Vector2d()); + data->provider().SetDragImage(drag_image, gfx::Vector2d()); return client_->StartDragAndDrop( - data, - widget_->GetNativeWindow()->GetRootWindow(), - widget_->GetNativeWindow(), - gfx::Point(), - ui::DragDropTypes::DRAG_COPY, + std::move(data), widget_->GetNativeWindow()->GetRootWindow(), + widget_->GetNativeWindow(), gfx::Point(), ui::DragDropTypes::DRAG_COPY, ui::DragDropTypes::DRAG_EVENT_SOURCE_MOUSE); } @@ -806,7 +803,8 @@ class TestDragDropDelegate : public aura::client::DragDropDelegate { ++num_exits_; } - int OnPerformDrop(const ui::DropTargetEvent& event) override { + int OnPerformDrop(const ui::DropTargetEvent& event, + std::unique_ptr<OSExchangeData> data) override { ++num_drops_; last_event_mouse_position_ = event.location(); last_event_flags_ = event.flags(); @@ -836,15 +834,12 @@ class DesktopDragDropClientAuraX11ChromeSourceTargetTest ~DesktopDragDropClientAuraX11ChromeSourceTargetTest() override = default; int StartDragAndDrop() { - ui::OSExchangeData data; - data.SetString(base::ASCIIToUTF16("Test")); + auto data(std::make_unique<ui::OSExchangeData>()); + data->SetString(base::ASCIIToUTF16("Test")); return client_->StartDragAndDrop( - data, - widget_->GetNativeWindow()->GetRootWindow(), - widget_->GetNativeWindow(), - gfx::Point(), - ui::DragDropTypes::DRAG_COPY, + std::move(data), widget_->GetNativeWindow()->GetRootWindow(), + widget_->GetNativeWindow(), gfx::Point(), ui::DragDropTypes::DRAG_COPY, ui::DragDropTypes::DRAG_EVENT_SOURCE_MOUSE); } 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 dfe5f9a9f70..7f08e6a6141 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 @@ -53,7 +53,7 @@ DesktopDragDropClientOzone::~DesktopDragDropClientOzone() { } int DesktopDragDropClientOzone::StartDragAndDrop( - const ui::OSExchangeData& data, + std::unique_ptr<ui::OSExchangeData> data, aura::Window* root_window, aura::Window* source_window, const gfx::Point& root_location, @@ -81,7 +81,7 @@ int DesktopDragDropClientOzone::StartDragAndDrop( cursor_manager_->GetInitializedCursor(ui::CursorType::kGrabbing)); drag_handler_->StartDrag( - data, operation, cursor_client->GetCursor(), + *data.get(), operation, cursor_client->GetCursor(), base::BindOnce(&DesktopDragDropClientOzone::OnDragSessionClosed, base::Unretained(this))); in_move_loop_ = true; @@ -234,7 +234,8 @@ void DesktopDragDropClientOzone::PerformDrop() { std::unique_ptr<ui::DropTargetEvent> event = CreateDropTargetEvent(last_drag_point_); if (drag_drop_delegate_ && event) - drag_operation_ = drag_drop_delegate_->OnPerformDrop(*event); + drag_operation_ = drag_drop_delegate_->OnPerformDrop( + *event, std::move(os_exchange_data_)); DragDropSessionCompleted(); } diff --git a/chromium/ui/views/widget/desktop_aura/desktop_drag_drop_client_ozone.h b/chromium/ui/views/widget/desktop_aura/desktop_drag_drop_client_ozone.h index e31f41ba49e..05b2fd24af6 100644 --- a/chromium/ui/views/widget/desktop_aura/desktop_drag_drop_client_ozone.h +++ b/chromium/ui/views/widget/desktop_aura/desktop_drag_drop_client_ozone.h @@ -40,7 +40,7 @@ class VIEWS_EXPORT DesktopDragDropClientOzone ~DesktopDragDropClientOzone() override; // Overridden from aura::client::DragDropClient: - int StartDragAndDrop(const ui::OSExchangeData& data, + int StartDragAndDrop(std::unique_ptr<ui::OSExchangeData> data, aura::Window* root_window, aura::Window* source_window, const gfx::Point& root_location, diff --git a/chromium/ui/views/widget/desktop_aura/desktop_drag_drop_client_ozone_unittest.cc b/chromium/ui/views/widget/desktop_aura/desktop_drag_drop_client_ozone_unittest.cc index bcc003f2a2b..336258bb0ae 100644 --- a/chromium/ui/views/widget/desktop_aura/desktop_drag_drop_client_ozone_unittest.cc +++ b/chromium/ui/views/widget/desktop_aura/desktop_drag_drop_client_ozone_unittest.cc @@ -45,14 +45,13 @@ class FakePlatformWindow : public ui::PlatformWindow, public ui::WmDragHandler { void Minimize() override {} void Restore() override {} ui::PlatformWindowState GetPlatformWindowState() const override { - return ui::PlatformWindowState::PLATFORM_WINDOW_STATE_NORMAL; + return ui::PlatformWindowState::kNormal; } + void Activate() override {} + void Deactivate() override {} void SetCursor(ui::PlatformCursor cursor) override {} void MoveCursorTo(const gfx::Point& location) override {} void ConfineCursorToBounds(const gfx::Rect& bounds) override {} - ui::PlatformImeController* GetPlatformImeController() override { - return nullptr; - } void SetRestoredBoundsInPixels(const gfx::Rect& bounds) override {} gfx::Rect GetRestoredBoundsInPixels() const override { return gfx::Rect(); } diff --git a/chromium/ui/views/widget/desktop_aura/desktop_drag_drop_client_win.cc b/chromium/ui/views/widget/desktop_aura/desktop_drag_drop_client_win.cc index 2c829dca0ee..b2c6b75837d 100644 --- a/chromium/ui/views/widget/desktop_aura/desktop_drag_drop_client_win.cc +++ b/chromium/ui/views/widget/desktop_aura/desktop_drag_drop_client_win.cc @@ -30,7 +30,7 @@ DesktopDragDropClientWin::~DesktopDragDropClientWin() { } int DesktopDragDropClientWin::StartDragAndDrop( - const ui::OSExchangeData& data, + std::unique_ptr<ui::OSExchangeData> data, aura::Window* root_window, aura::Window* source_window, const gfx::Point& screen_location, @@ -43,8 +43,8 @@ int DesktopDragDropClientWin::StartDragAndDrop( drag_source_ = ui::DragSourceWin::Create(); Microsoft::WRL::ComPtr<ui::DragSourceWin> drag_source_copy = drag_source_; - drag_source_copy->set_data(&data); - ui::OSExchangeDataProviderWin::GetDataObjectImpl(data) + drag_source_copy->set_data(data.get()); + ui::OSExchangeDataProviderWin::GetDataObjectImpl(*data.get()) ->set_in_drag_loop(true); DWORD effect; @@ -53,7 +53,8 @@ int DesktopDragDropClientWin::StartDragAndDrop( ui::DragDropTypes::DRAG_EVENT_SOURCE_COUNT); HRESULT result = DoDragDrop( - ui::OSExchangeDataProviderWin::GetIDataObject(data), drag_source_.Get(), + ui::OSExchangeDataProviderWin::GetIDataObject(*data.get()), + drag_source_.Get(), ui::DragDropTypes::DragOperationToDropEffect(operation), &effect); drag_source_copy->set_data(nullptr); diff --git a/chromium/ui/views/widget/desktop_aura/desktop_drag_drop_client_win.h b/chromium/ui/views/widget/desktop_aura/desktop_drag_drop_client_win.h index 25845e08f22..70e5950f3d6 100644 --- a/chromium/ui/views/widget/desktop_aura/desktop_drag_drop_client_win.h +++ b/chromium/ui/views/widget/desktop_aura/desktop_drag_drop_client_win.h @@ -34,7 +34,7 @@ class VIEWS_EXPORT DesktopDragDropClientWin ~DesktopDragDropClientWin() override; // Overridden from aura::client::DragDropClient: - int StartDragAndDrop(const ui::OSExchangeData& data, + int StartDragAndDrop(std::unique_ptr<ui::OSExchangeData> data, aura::Window* root_window, aura::Window* source_window, const gfx::Point& screen_location, diff --git a/chromium/ui/views/widget/desktop_aura/desktop_drop_target_win.cc b/chromium/ui/views/widget/desktop_aura/desktop_drop_target_win.cc index 898eacdecac..052d82cbea1 100644 --- a/chromium/ui/views/widget/desktop_aura/desktop_drop_target_win.cc +++ b/chromium/ui/views/widget/desktop_aura/desktop_drop_target_win.cc @@ -98,7 +98,7 @@ DWORD DesktopDropTargetWin::OnDrop(IDataObject* data_object, DragDropDelegate* delegate; Translate(data_object, key_state, position, effect, &data, &event, &delegate); if (delegate) { - drag_operation = delegate->OnPerformDrop(*event); + drag_operation = delegate->OnPerformDrop(*event, std::move(data)); DragDropClient* client = aura::client::GetDragDropClient(root_window_); if (client && !client->IsDragDropInProgress() && drag_operation != ui::DragDropTypes::DRAG_NONE) { diff --git a/chromium/ui/views/widget/desktop_aura/desktop_focus_rules.cc b/chromium/ui/views/widget/desktop_aura/desktop_focus_rules.cc index 6e823cdd63a..ec25062629a 100644 --- a/chromium/ui/views/widget/desktop_aura/desktop_focus_rules.cc +++ b/chromium/ui/views/widget/desktop_aura/desktop_focus_rules.cc @@ -17,6 +17,10 @@ DesktopFocusRules::DesktopFocusRules(aura::Window* content_window) DesktopFocusRules::~DesktopFocusRules() = default; bool DesktopFocusRules::CanActivateWindow(const aura::Window* window) const { + // The RootWindow is not activatable, only |content_window_| and children of + // the RootWindow are considered activatable. + if (window && window->IsRootWindow()) + return false; if (window && IsToplevelWindow(window) && content_window_->GetRootWindow()->Contains(window) && wm::WindowStateIs(window->GetRootWindow(), ui::SHOW_STATE_MINIMIZED)) { diff --git a/chromium/ui/views/widget/desktop_aura/desktop_focus_rules_unittest.cc b/chromium/ui/views/widget/desktop_aura/desktop_focus_rules_unittest.cc index 8717379bfa3..597e57744f0 100644 --- a/chromium/ui/views/widget/desktop_aura/desktop_focus_rules_unittest.cc +++ b/chromium/ui/views/widget/desktop_aura/desktop_focus_rules_unittest.cc @@ -40,4 +40,14 @@ TEST_F(DesktopFocusRulesTest, DontFocusWindowsInOtherHierarchies) { w2->CloseNow(); } +// Verifies root windows are not activatable. +TEST_F(DesktopFocusRulesTest, CanActivateWindowForRootWindow) { + Widget* w1 = CreateTopLevelNativeWidget(); + aura::Window* content_window = w1->GetNativeWindow(); + aura::Window* root_window = content_window->GetRootWindow(); + EXPECT_TRUE(wm::CanActivateWindow(content_window)); + EXPECT_FALSE(wm::CanActivateWindow(root_window)); + w1->CloseNow(); +} + } // namespace views diff --git a/chromium/ui/views/widget/desktop_aura/desktop_native_widget_aura.cc b/chromium/ui/views/widget/desktop_aura/desktop_native_widget_aura.cc index 85569403055..e7d20b83506 100644 --- a/chromium/ui/views/widget/desktop_aura/desktop_native_widget_aura.cc +++ b/chromium/ui/views/widget/desktop_aura/desktop_native_widget_aura.cc @@ -86,7 +86,7 @@ class DesktopNativeWidgetTopLevelHandler : public aura::WindowObserver { const gfx::Rect& bounds, bool full_screen, bool is_menu, - bool root_is_always_on_top) { + ui::ZOrderLevel root_z_order) { // This instance will get deleted when the widget is destroyed. DesktopNativeWidgetTopLevelHandler* top_level_handler = new DesktopNativeWidgetTopLevelHandler; @@ -103,7 +103,7 @@ class DesktopNativeWidgetTopLevelHandler : public aura::WindowObserver { init_params.activatable = full_screen ? Widget::InitParams::ACTIVATABLE_YES : Widget::InitParams::ACTIVATABLE_NO; - init_params.keep_on_top = root_is_always_on_top; + init_params.z_order = root_z_order; // This widget instance will get deleted when the window is // destroyed. @@ -193,14 +193,14 @@ class DesktopNativeWidgetAuraWindowParentingClient bool is_menu = window->type() == aura::client::WINDOW_TYPE_MENU; if (is_fullscreen || is_menu) { - bool root_is_always_on_top = false; + ui::ZOrderLevel root_z_order = ui::ZOrderLevel::kNormal; internal::NativeWidgetPrivate* native_widget = DesktopNativeWidgetAura::ForWindow(root_window_); if (native_widget) - root_is_always_on_top = native_widget->IsAlwaysOnTop(); + root_z_order = native_widget->GetZOrderLevel(); return DesktopNativeWidgetTopLevelHandler::CreateParentWindow( - window, bounds, is_fullscreen, is_menu, root_is_always_on_top); + window, bounds, is_fullscreen, is_menu, root_z_order); } return root_window_; } @@ -249,8 +249,7 @@ DesktopNativeWidgetAura::DesktopNativeWidgetAura( last_drop_operation_(ui::DragDropTypes::DRAG_NONE), restore_focus_on_activate_(false), cursor_(gfx::kNullCursor), - widget_type_(Widget::InitParams::TYPE_WINDOW), - close_widget_factory_(this) { + widget_type_(Widget::InitParams::TYPE_WINDOW) { aura::client::SetFocusChangeObserver(content_window_, this); wm::SetActivationChangeObserver(content_window_, this); } @@ -814,13 +813,15 @@ bool DesktopNativeWidgetAura::IsActive() const { return content_window_ && desktop_window_tree_host_->IsActive(); } -void DesktopNativeWidgetAura::SetAlwaysOnTop(bool always_on_top) { +void DesktopNativeWidgetAura::SetZOrderLevel(ui::ZOrderLevel order) { if (content_window_) - desktop_window_tree_host_->SetAlwaysOnTop(always_on_top); + desktop_window_tree_host_->SetZOrderLevel(order); } -bool DesktopNativeWidgetAura::IsAlwaysOnTop() const { - return content_window_ && desktop_window_tree_host_->IsAlwaysOnTop(); +ui::ZOrderLevel DesktopNativeWidgetAura::GetZOrderLevel() const { + if (content_window_) + return desktop_window_tree_host_->GetZOrderLevel(); + return ui::ZOrderLevel::kNormal; } void DesktopNativeWidgetAura::SetVisibleOnAllWorkspaces(bool always_visible) { @@ -888,11 +889,12 @@ void DesktopNativeWidgetAura::FlashFrame(bool flash_frame) { void DesktopNativeWidgetAura::RunShellDrag( View* view, - const ui::OSExchangeData& data, + std::unique_ptr<ui::OSExchangeData> data, const gfx::Point& location, int operation, ui::DragDropTypes::DragEventSource source) { - views::RunShellDrag(content_window_, data, location, operation, source); + views::RunShellDrag(content_window_, std::move(data), location, operation, + source); } void DesktopNativeWidgetAura::SchedulePaintInRect(const gfx::Rect& rect) { @@ -1005,10 +1007,6 @@ void DesktopNativeWidgetAura::OnSizeConstraintsChanged() { desktop_window_tree_host_->SizeConstraintsChanged(); } -void DesktopNativeWidgetAura::OnCanActivateChanged() { - desktop_window_tree_host_->OnCanActivateChanged(); -} - std::string DesktopNativeWidgetAura::GetName() const { return name_; } @@ -1196,7 +1194,9 @@ void DesktopNativeWidgetAura::OnDragExited() { drop_helper_->OnDragExit(); } -int DesktopNativeWidgetAura::OnPerformDrop(const ui::DropTargetEvent& event) { +int DesktopNativeWidgetAura::OnPerformDrop( + const ui::DropTargetEvent& event, + std::unique_ptr<ui::OSExchangeData> data) { DCHECK(drop_helper_.get() != nullptr); if (ShouldActivate()) Activate(); diff --git a/chromium/ui/views/widget/desktop_aura/desktop_native_widget_aura.h b/chromium/ui/views/widget/desktop_aura/desktop_native_widget_aura.h index 5d45002399b..8b038de0c5b 100644 --- a/chromium/ui/views/widget/desktop_aura/desktop_native_widget_aura.h +++ b/chromium/ui/views/widget/desktop_aura/desktop_native_widget_aura.h @@ -151,8 +151,8 @@ class VIEWS_EXPORT DesktopNativeWidgetAura void Activate() override; void Deactivate() override; bool IsActive() const override; - void SetAlwaysOnTop(bool always_on_top) override; - bool IsAlwaysOnTop() const override; + void SetZOrderLevel(ui::ZOrderLevel order) override; + ui::ZOrderLevel GetZOrderLevel() const override; void SetVisibleOnAllWorkspaces(bool always_visible) override; bool IsVisibleOnAllWorkspaces() const override; void Maximize() override; @@ -168,7 +168,7 @@ class VIEWS_EXPORT DesktopNativeWidgetAura void SetAspectRatio(const gfx::SizeF& aspect_ratio) override; void FlashFrame(bool flash_frame) override; void RunShellDrag(View* view, - const ui::OSExchangeData& data, + std::unique_ptr<ui::OSExchangeData> data, const gfx::Point& location, int operation, ui::DragDropTypes::DragEventSource source) override; @@ -191,7 +191,6 @@ class VIEWS_EXPORT DesktopNativeWidgetAura bool IsTranslucentWindowOpacitySupported() const override; ui::GestureRecognizer* GetGestureRecognizer() override; void OnSizeConstraintsChanged() override; - void OnCanActivateChanged() override; std::string GetName() const override; // Overridden from aura::WindowDelegate: @@ -238,7 +237,8 @@ class VIEWS_EXPORT DesktopNativeWidgetAura void OnDragEntered(const ui::DropTargetEvent& event) override; int OnDragUpdated(const ui::DropTargetEvent& event) override; void OnDragExited() override; - int OnPerformDrop(const ui::DropTargetEvent& event) override; + int OnPerformDrop(const ui::DropTargetEvent& event, + std::unique_ptr<ui::OSExchangeData> data) override; // Overridden from aura::WindowTreeHostObserver: void OnHostCloseRequested(aura::WindowTreeHost* host) override; @@ -323,7 +323,7 @@ class VIEWS_EXPORT DesktopNativeWidgetAura // The following factory is used for calls to close the NativeWidgetAura // instance. - base::WeakPtrFactory<DesktopNativeWidgetAura> close_widget_factory_; + base::WeakPtrFactory<DesktopNativeWidgetAura> close_widget_factory_{this}; DISALLOW_COPY_AND_ASSIGN(DesktopNativeWidgetAura); }; 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 5fc1220919c..69c9e7324c2 100644 --- a/chromium/ui/views/widget/desktop_aura/desktop_screen_x11.cc +++ b/chromium/ui/views/widget/desktop_aura/desktop_screen_x11.cc @@ -23,8 +23,7 @@ #include "ui/events/platform/platform_event_source.h" #include "ui/events/platform/x11/x11_event_source.h" #include "ui/gfx/font_render_params.h" -#include "ui/gfx/geometry/point_conversions.h" -#include "ui/gfx/geometry/size_conversions.h" +#include "ui/gfx/geometry/dip_util.h" #include "ui/gfx/native_widget_types.h" #include "ui/gfx/switches.h" #include "ui/gfx/x/x11.h" @@ -50,14 +49,6 @@ float GetDeviceScaleFactor() { return device_scale_factor; } -gfx::Point PixelToDIPPoint(const gfx::Point& pixel_point) { - return gfx::ScaleToFlooredPoint(pixel_point, 1.0f / GetDeviceScaleFactor()); -} - -gfx::Point DIPToPixelPoint(const gfx::Point& dip_point) { - return gfx::ScaleToFlooredPoint(dip_point, GetDeviceScaleFactor()); -} - } // namespace namespace views { @@ -68,8 +59,7 @@ namespace views { DesktopScreenX11::DesktopScreenX11() : xdisplay_(gfx::GetXDisplay()), x_root_window_(DefaultRootWindow(xdisplay_)), - xrandr_version_(ui::GetXrandrVersion(xdisplay_)), - weak_factory_(this) { + xrandr_version_(ui::GetXrandrVersion(xdisplay_)) { if (views::LinuxUI::instance()) views::LinuxUI::instance()->AddDeviceScaleFactorObserver(this); float scale = GetDeviceScaleFactor(); @@ -111,7 +101,7 @@ gfx::Point DesktopScreenX11::GetCursorScreenPoint() { auto point = ui::X11EventSource::GetInstance() ->GetRootCursorLocationFromCurrentEvent(); if (point) - return PixelToDIPPoint(point.value()); + return gfx::ConvertPointToDIP(GetDeviceScaleFactor(), point.value()); } ::Window root, child; @@ -120,7 +110,8 @@ gfx::Point DesktopScreenX11::GetCursorScreenPoint() { XQueryPointer(xdisplay_, x_root_window_, &root, &child, &root_x, &root_y, &win_x, &win_y, &mask); - return PixelToDIPPoint(gfx::Point(root_x, root_y)); + return gfx::ConvertPointToDIP(GetDeviceScaleFactor(), + gfx::Point(root_x, root_y)); } bool DesktopScreenX11::IsWindowUnderCursor(gfx::NativeWindow window) { @@ -131,7 +122,7 @@ gfx::NativeWindow DesktopScreenX11::GetWindowAtScreenPoint( const gfx::Point& point) { X11TopmostWindowFinder finder; return finder.FindLocalProcessWindowAt( - DIPToPixelPoint(point), std::set<aura::Window*>()); + gfx::ConvertPointToPixel(GetDeviceScaleFactor(), point), {}); } int DesktopScreenX11::GetNumDisplays() const { @@ -161,11 +152,10 @@ display::Display DesktopScreenX11::GetDisplayNearestWindow( DesktopWindowTreeHostX11* rwh = DesktopWindowTreeHostX11::GetHostForXID( host->GetAcceleratedWidget()); if (rwh) { - const float scale = 1.0f / GetDeviceScaleFactor(); const gfx::Rect pixel_rect = rwh->GetX11RootWindowBounds(); - return GetDisplayMatching( - gfx::Rect(gfx::ScaleToFlooredPoint(pixel_rect.origin(), scale), - gfx::ScaleToCeiledSize(pixel_rect.size(), scale))); + const gfx::Rect dip_rect = + gfx::ConvertRectToDIP(GetDeviceScaleFactor(), pixel_rect); + return GetDisplayMatching(dip_rect); } } @@ -176,25 +166,13 @@ display::Display DesktopScreenX11::GetDisplayNearestPoint( const gfx::Point& point) const { if (displays_.size() <= 1) return GetPrimaryDisplay(); - for (const auto& display : displays_) { - if (display.bounds().Contains(point)) - return display; - } return *FindDisplayNearestPoint(displays_, point); } display::Display DesktopScreenX11::GetDisplayMatching( const gfx::Rect& match_rect) const { - int max_area = 0; - const display::Display* matching = nullptr; - for (const auto& display : displays_) { - gfx::Rect intersect = gfx::IntersectRects(display.bounds(), match_rect); - int area = intersect.width() * intersect.height(); - if (area > max_area) { - max_area = area; - matching = &display; - } - } + const display::Display* matching = + display::FindDisplayWithBiggestIntersection(displays_, match_rect); // Fallback to the primary display if there is no matching display. return matching ? *matching : GetPrimaryDisplay(); } @@ -254,8 +232,7 @@ DesktopScreenX11::DesktopScreenX11( : xdisplay_(gfx::GetXDisplay()), x_root_window_(DefaultRootWindow(xdisplay_)), xrandr_version_(ui::GetXrandrVersion(xdisplay_)), - displays_(test_displays), - weak_factory_(this) { + displays_(test_displays) { if (views::LinuxUI::instance()) views::LinuxUI::instance()->AddDeviceScaleFactorObserver(this); } 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 877d4ae3cb1..beb698efa63 100644 --- a/chromium/ui/views/widget/desktop_aura/desktop_screen_x11.h +++ b/chromium/ui/views/widget/desktop_aura/desktop_screen_x11.h @@ -102,7 +102,7 @@ class VIEWS_EXPORT DesktopScreenX11 : public display::Screen, display::DisplayChangeNotifier change_notifier_; - base::WeakPtrFactory<DesktopScreenX11> weak_factory_; + base::WeakPtrFactory<DesktopScreenX11> weak_factory_{this}; DISALLOW_COPY_AND_ASSIGN(DesktopScreenX11); }; diff --git a/chromium/ui/views/widget/desktop_aura/desktop_screen_x11_unittest.cc b/chromium/ui/views/widget/desktop_aura/desktop_screen_x11_unittest.cc index abd96f43f46..c40d24755cc 100644 --- a/chromium/ui/views/widget/desktop_aura/desktop_screen_x11_unittest.cc +++ b/chromium/ui/views/widget/desktop_aura/desktop_screen_x11_unittest.cc @@ -7,6 +7,7 @@ #include <stdint.h> #include <memory> +#include <vector> #include "base/macros.h" #include "testing/gtest/include/gtest/gtest.h" @@ -14,6 +15,8 @@ #include "ui/aura/window.h" #include "ui/aura/window_event_dispatcher.h" #include "ui/base/hit_test.h" +#include "ui/base/x/x11_util.h" +#include "ui/display/display.h" #include "ui/display/display_observer.h" #include "ui/events/test/event_generator.h" #include "ui/gfx/font_render_params.h" diff --git a/chromium/ui/views/widget/desktop_aura/desktop_window_tree_host.h b/chromium/ui/views/widget/desktop_aura/desktop_window_tree_host.h index eff14bc37bc..c4fa6859b4a 100644 --- a/chromium/ui/views/widget/desktop_aura/desktop_window_tree_host.h +++ b/chromium/ui/views/widget/desktop_aura/desktop_window_tree_host.h @@ -130,8 +130,8 @@ class VIEWS_EXPORT DesktopWindowTreeHost { virtual bool HasCapture() const = 0; - virtual void SetAlwaysOnTop(bool always_on_top) = 0; - virtual bool IsAlwaysOnTop() const = 0; + virtual void SetZOrderLevel(ui::ZOrderLevel order) = 0; + virtual ui::ZOrderLevel GetZOrderLevel() const = 0; virtual void SetVisibleOnAllWorkspaces(bool always_visible) = 0; virtual bool IsVisibleOnAllWorkspaces() const = 0; @@ -197,9 +197,6 @@ class VIEWS_EXPORT DesktopWindowTreeHost { // Sets the bounds in screen coordinate DIPs (WindowTreeHost generally // operates in pixels). This function is implemented in terms of Screen. virtual void SetBoundsInDIP(const gfx::Rect& bounds); - - // See description in Widget::OnCanActivateChanged(). - virtual void OnCanActivateChanged() {} }; } // namespace views diff --git a/chromium/ui/views/widget/desktop_aura/desktop_window_tree_host_platform.cc b/chromium/ui/views/widget/desktop_aura/desktop_window_tree_host_platform.cc index ac7c92db83a..c8021f125d6 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 @@ -50,6 +50,8 @@ ui::PlatformWindowInitProperties ConvertWidgetInitParamsToInitProperties( } properties.bounds = params.bounds; + properties.activatable = + params.activatable == Widget::InitParams::ACTIVATABLE_YES; if (params.parent && params.parent->GetHost()) properties.parent_widget = params.parent->GetHost()->GetAcceleratedWidget(); @@ -306,13 +308,11 @@ void DesktopWindowTreeHostPlatform::SetShape( } void DesktopWindowTreeHostPlatform::Activate() { - // TODO: needs PlatformWindow support. - NOTIMPLEMENTED_LOG_ONCE(); + platform_window()->Activate(); } void DesktopWindowTreeHostPlatform::Deactivate() { - // TODO: needs PlatformWindow support. - NOTIMPLEMENTED_LOG_ONCE(); + platform_window()->Deactivate(); } bool DesktopWindowTreeHostPlatform::IsActive() const { @@ -333,26 +333,26 @@ void DesktopWindowTreeHostPlatform::Restore() { bool DesktopWindowTreeHostPlatform::IsMaximized() const { return platform_window()->GetPlatformWindowState() == - ui::PlatformWindowState::PLATFORM_WINDOW_STATE_MAXIMIZED; + ui::PlatformWindowState::kMaximized; } bool DesktopWindowTreeHostPlatform::IsMinimized() const { return platform_window()->GetPlatformWindowState() == - ui::PlatformWindowState::PLATFORM_WINDOW_STATE_MINIMIZED; + ui::PlatformWindowState::kMinimized; } bool DesktopWindowTreeHostPlatform::HasCapture() const { return platform_window()->HasCapture(); } -void DesktopWindowTreeHostPlatform::SetAlwaysOnTop(bool always_on_top) { +void DesktopWindowTreeHostPlatform::SetZOrderLevel(ui::ZOrderLevel order) { // TODO: needs PlatformWindow support. NOTIMPLEMENTED_LOG_ONCE(); } -bool DesktopWindowTreeHostPlatform::IsAlwaysOnTop() const { +ui::ZOrderLevel DesktopWindowTreeHostPlatform::GetZOrderLevel() const { // TODO: needs PlatformWindow support. - return false; + return ui::ZOrderLevel::kNormal; } void DesktopWindowTreeHostPlatform::SetVisibleOnAllWorkspaces( @@ -419,7 +419,7 @@ void DesktopWindowTreeHostPlatform::SetFullscreen(bool fullscreen) { bool DesktopWindowTreeHostPlatform::IsFullscreen() const { return platform_window()->GetPlatformWindowState() == - ui::PlatformWindowState::PLATFORM_WINDOW_STATE_FULLSCREEN; + ui::PlatformWindowState::kFullScreen; } void DesktopWindowTreeHostPlatform::SetOpacity(float opacity) { @@ -519,8 +519,7 @@ void DesktopWindowTreeHostPlatform::OnWindowStateChanged( // Propagate minimization/restore to compositor to avoid drawing 'blank' // frames that could be treated as previews, which show content even if a // window is minimized. - bool visible = - new_state != ui::PlatformWindowState::PLATFORM_WINDOW_STATE_MINIMIZED; + bool visible = new_state != ui::PlatformWindowState::kMinimized; if (visible != compositor()->IsVisible()) { compositor()->SetVisible(visible); native_widget_delegate_->OnNativeWidgetVisibilityChanged(visible); 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 02f30ef40ca..8760639632c 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 @@ -59,8 +59,8 @@ class VIEWS_EXPORT DesktopWindowTreeHostPlatform bool IsMaximized() const override; bool IsMinimized() const override; bool HasCapture() const override; - void SetAlwaysOnTop(bool always_on_top) override; - bool IsAlwaysOnTop() const override; + void SetZOrderLevel(ui::ZOrderLevel order) override; + ui::ZOrderLevel GetZOrderLevel() const override; void SetVisibleOnAllWorkspaces(bool always_visible) override; bool IsVisibleOnAllWorkspaces() const override; bool SetWindowTitle(const base::string16& title) override; 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 c18d5fb9ea5..0eedc266531 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 @@ -8,6 +8,7 @@ #include "base/containers/flat_set.h" #include "base/memory/ptr_util.h" #include "base/metrics/histogram_macros.h" +#include "base/win/win_util.h" #include "third_party/skia/include/core/SkPath.h" #include "third_party/skia/include/core/SkRegion.h" #include "ui/aura/client/aura_constants.h" @@ -29,7 +30,9 @@ #include "ui/gfx/geometry/vector2d.h" #include "ui/gfx/native_widget_types.h" #include "ui/gfx/path_win.h" +#include "ui/views/corewm/tooltip_aura.h" #include "ui/views/corewm/tooltip_win.h" +#include "ui/views/views_features.h" #include "ui/views/views_switches.h" #include "ui/views/widget/desktop_aura/desktop_drag_drop_client_win.h" #include "ui/views/widget/desktop_aura/desktop_native_cursor_manager.h" @@ -130,6 +133,7 @@ void DesktopWindowTreeHostWin::Init(const Widget::InitParams& params) { remove_standard_frame_ = params.remove_standard_frame; has_non_client_view_ = Widget::RequiresNonClientView(params.type); + z_order_ = params.EffectiveZOrderLevel(); // We don't have an HWND yet, so scale relative to the nearest screen. gfx::Rect pixel_bounds = @@ -165,6 +169,9 @@ void DesktopWindowTreeHostWin::OnActiveWindowChanged(bool active) {} void DesktopWindowTreeHostWin::OnWidgetInitDone() {} std::unique_ptr<corewm::Tooltip> DesktopWindowTreeHostWin::CreateTooltip() { + if (base::FeatureList::IsEnabled(features::kEnableAuraTooltipsOnWindows)) + return std::make_unique<corewm::TooltipAura>(); + DCHECK(!tooltip_); tooltip_ = new corewm::TooltipWin(GetAcceleratedWidget()); return base::WrapUnique(tooltip_); @@ -361,12 +368,25 @@ bool DesktopWindowTreeHostWin::HasCapture() const { return message_handler_->HasCapture(); } -void DesktopWindowTreeHostWin::SetAlwaysOnTop(bool always_on_top) { - message_handler_->SetAlwaysOnTop(always_on_top); +void DesktopWindowTreeHostWin::SetZOrderLevel(ui::ZOrderLevel order) { + z_order_ = order; + // Emulate the multiple window levels provided by other platforms by + // collapsing the z-order enum into kNormal = normal, everything else = always + // on top. + message_handler_->SetAlwaysOnTop(order != ui::ZOrderLevel::kNormal); } -bool DesktopWindowTreeHostWin::IsAlwaysOnTop() const { - return message_handler_->IsAlwaysOnTop(); +ui::ZOrderLevel DesktopWindowTreeHostWin::GetZOrderLevel() const { + bool window_always_on_top = message_handler_->IsAlwaysOnTop(); + bool level_always_on_top = z_order_ != ui::ZOrderLevel::kNormal; + + if (window_always_on_top == level_always_on_top) + return z_order_; + + // If something external has forced a window to be always-on-top, map it to + // kFloatingWindow as a reasonable equivalent. + return window_always_on_top ? ui::ZOrderLevel::kFloatingWindow + : ui::ZOrderLevel::kNormal; } void DesktopWindowTreeHostWin::SetVisibleOnAllWorkspaces(bool always_visible) { @@ -900,6 +920,16 @@ bool DesktopWindowTreeHostWin::HandleMouseEvent(ui::MouseEvent* event) { } void DesktopWindowTreeHostWin::HandleKeyEvent(ui::KeyEvent* event) { + // Bypass normal handling of alt-space, which would otherwise consume the + // corresponding WM_SYSCHAR. This allows HandleIMEMessage() to show the + // system menu in this case. If we instead showed the system menu here, the + // WM_SYSCHAR would trigger a beep when processed by the native event handler. + if ((event->type() == ui::ET_KEY_PRESSED) && + (event->key_code() == ui::VKEY_SPACE) && + (event->flags() & ui::EF_ALT_DOWN) && GetWidget()->non_client_view()) { + return; + } + SendEventToSink(event); } @@ -935,6 +965,15 @@ bool DesktopWindowTreeHostWin::HandleIMEMessage(UINT message, WPARAM w_param, LPARAM l_param, LRESULT* result) { + // Show the system menu at an appropriate location on alt-space. + if ((message == WM_SYSCHAR) && (w_param == VK_SPACE) && + GetWidget()->non_client_view()) { + const auto* frame = GetWidget()->non_client_view()->frame_view(); + ShowSystemMenuAtScreenPixelLocation( + GetHWND(), frame->GetSystemMenuScreenPixelLocation()); + return true; + } + MSG msg = {}; msg.hwnd = GetHWND(); msg.message = message; 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 8ac960b083c..335db0db49b 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 @@ -95,8 +95,8 @@ class VIEWS_EXPORT DesktopWindowTreeHostWin bool IsMaximized() const override; bool IsMinimized() const override; bool HasCapture() const override; - void SetAlwaysOnTop(bool always_on_top) override; - bool IsAlwaysOnTop() const override; + void SetZOrderLevel(ui::ZOrderLevel order) override; + ui::ZOrderLevel GetZOrderLevel() const override; void SetVisibleOnAllWorkspaces(bool always_visible) override; bool IsVisibleOnAllWorkspaces() const override; bool SetWindowTitle(const base::string16& title) override; @@ -307,6 +307,10 @@ class VIEWS_EXPORT DesktopWindowTreeHostWin // become activated. bool wants_mouse_events_when_inactive_ = false; + // The z-order level of the window; the window exhibits "always on top" + // behavior if > 0. + ui::ZOrderLevel z_order_ = ui::ZOrderLevel::kNormal; + DISALLOW_COPY_AND_ASSIGN(DesktopWindowTreeHostWin); }; 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 2c502b3a533..7c4f1e88e42 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 @@ -4,8 +4,13 @@ #include "ui/views/widget/desktop_aura/desktop_window_tree_host_x11.h" +#include <algorithm> +#include <list> #include <memory> +#include <set> +#include <string> #include <utility> +#include <vector> #include "base/bind.h" #include "base/command_line.h" @@ -19,13 +24,13 @@ #include "base/threading/thread_task_runner_handle.h" #include "base/trace_event/trace_event.h" #include "third_party/skia/include/core/SkPath.h" -#include "ui/accessibility/platform/atk_util_auralinux.h" #include "ui/aura/client/aura_constants.h" #include "ui/aura/client/cursor_client.h" #include "ui/aura/client/focus_client.h" #include "ui/aura/null_window_targeter.h" #include "ui/aura/window.h" #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/hit_test.h" @@ -34,16 +39,15 @@ #include "ui/base/x/x11_pointer_grab.h" #include "ui/base/x/x11_util.h" #include "ui/base/x/x11_util_internal.h" -#include "ui/base/x/x11_window_event_manager.h" #include "ui/display/screen.h" -#include "ui/events/devices/x11/device_data_manager_x11.h" #include "ui/events/devices/x11/device_list_cache_x11.h" -#include "ui/events/devices/x11/touch_factory_x11.h" +#include "ui/events/event.h" #include "ui/events/event_utils.h" #include "ui/events/keyboard_hook.h" #include "ui/events/keycodes/dom/dom_code.h" #include "ui/events/platform/platform_event_source.h" -#include "ui/gfx/geometry/insets.h" +#include "ui/events/x/events_x_utils.h" +#include "ui/events/x/x11_window_event_manager.h" #include "ui/gfx/geometry/size_conversions.h" #include "ui/gfx/image/image_skia.h" #include "ui/gfx/image/image_skia_rep.h" @@ -65,6 +69,10 @@ #include "ui/wm/core/compound_event_filter.h" #include "ui/wm/core/window_util.h" +#if BUILDFLAG(USE_ATK) +#include "ui/accessibility/platform/atk_util_auralinux.h" +#endif + DEFINE_UI_CLASS_PROPERTY_TYPE(views::DesktopWindowTreeHostX11*) namespace views { @@ -80,12 +88,60 @@ DEFINE_UI_CLASS_PROPERTY_KEY(DesktopWindowTreeHostX11*, namespace { -// Special value of the _NET_WM_DESKTOP property which indicates that the window -// should appear on all desktops. -const int kAllDesktops = 0xFFFFFFFF; +ui::XWindow::Configuration ConvertInitParamsToX11WindowConfig( + const Widget::InitParams& params) { + using WindowType = ui::XWindow::WindowType; + using WindowOpacity = ui::XWindow::WindowOpacity; + ui::XWindow::Configuration config; -const char kX11WindowRolePopup[] = "popup"; -const char kX11WindowRoleBubble[] = "bubble"; + switch (params.type) { + case Widget::InitParams::TYPE_WINDOW: + config.type = WindowType::kWindow; + break; + case Widget::InitParams::TYPE_MENU: + config.type = WindowType::kMenu; + break; + case Widget::InitParams::TYPE_TOOLTIP: + config.type = WindowType::kTooltip; + break; + case Widget::InitParams::TYPE_DRAG: + config.type = WindowType::kDrag; + break; + case Widget::InitParams::TYPE_BUBBLE: + config.type = WindowType::kBubble; + break; + default: + config.type = WindowType::kPopup; + break; + } + + switch (params.opacity) { + case Widget::InitParams::WindowOpacity::INFER_OPACITY: + config.opacity = WindowOpacity::kInferOpacity; + break; + case Widget::InitParams::WindowOpacity::OPAQUE_WINDOW: + config.opacity = WindowOpacity::kOpaqueWindow; + break; + case Widget::InitParams::WindowOpacity::TRANSLUCENT_WINDOW: + config.opacity = WindowOpacity::kTranslucentWindow; + break; + } + + config.activatable = + params.activatable == Widget::InitParams::ACTIVATABLE_YES; + config.force_show_in_taskbar = params.force_show_in_taskbar; + config.keep_on_top = + params.EffectiveZOrderLevel() != ui::ZOrderLevel::kNormal; + config.visible_on_all_workspaces = params.visible_on_all_workspaces; + config.remove_standard_frame = params.remove_standard_frame; + + config.workspace = params.workspace; + config.wm_class_name = params.wm_class_name; + config.wm_class_class = params.wm_class_class; + config.wm_role_name = params.wm_role_name; + + return config; +} // Returns the whole path from |window| to the root. std::vector<::Window> GetParentsList(XDisplay* xdisplay, ::Window window) { @@ -106,34 +162,6 @@ std::vector<::Window> GetParentsList(XDisplay* xdisplay, ::Window window) { return result; } -int XI2ModeToXMode(int xi2_mode) { - switch (xi2_mode) { - case XINotifyNormal: - return NotifyNormal; - case XINotifyGrab: - case XINotifyPassiveGrab: - return NotifyGrab; - case XINotifyUngrab: - case XINotifyPassiveUngrab: - return NotifyUngrab; - case XINotifyWhileGrabbed: - return NotifyWhileGrabbed; - default: - NOTREACHED(); - return NotifyNormal; - } -} - -int IgnoreX11Errors(XDisplay* display, XErrorEvent* error) { - return 0; -} - -bool SyncSetCounter(XDisplay* display, XID counter, int64_t value) { - XSyncValue sync_value; - XSyncIntsToValue(&sync_value, value & 0xFFFFFFFF, value >> 32); - return XSyncSetCounter(display, counter, sync_value) == x11::True; -} - class SwapWithNewSizeObserverHelper : public ui::CompositorObserver { public: using HelperCallback = base::RepeatingCallback<void(const gfx::Size&)>; @@ -166,18 +194,25 @@ class SwapWithNewSizeObserverHelper : public ui::CompositorObserver { DISALLOW_COPY_AND_ASSIGN(SwapWithNewSizeObserverHelper); }; +bool ShouldDiscardKeyEvent(XEvent* xev) { +#if BUILDFLAG(USE_ATK) + return ui::AtkUtilAuraLinux::HandleKeyEvent(xev) == + ui::DiscardAtkKeyEvent::Discard; +#else + return false; +#endif +} + } // namespace //////////////////////////////////////////////////////////////////////////////// // DesktopWindowTreeHostX11, public: - DesktopWindowTreeHostX11::DesktopWindowTreeHostX11( internal::NativeWidgetDelegate* native_widget_delegate, DesktopNativeWidgetAura* desktop_native_widget_aura) - : xdisplay_(gfx::GetXDisplay()), - x_root_window_(DefaultRootWindow(xdisplay_)), - native_widget_delegate_(native_widget_delegate), - desktop_native_widget_aura_(desktop_native_widget_aura) {} + : native_widget_delegate_(native_widget_delegate), + desktop_native_widget_aura_(desktop_native_widget_aura), + x11_window_(std::make_unique<ui::XWindow>(this)) {} DesktopWindowTreeHostX11::~DesktopWindowTreeHostX11() { window()->ClearProperty(kHostForRootWindow); @@ -211,160 +246,15 @@ std::vector<aura::Window*> DesktopWindowTreeHostX11::GetAllOpenWindows() { } gfx::Rect DesktopWindowTreeHostX11::GetX11RootWindowBounds() const { - return bounds_in_pixels_; + return x11_window_->bounds(); } gfx::Rect DesktopWindowTreeHostX11::GetX11RootWindowOuterBounds() const { - gfx::Rect outer_bounds(bounds_in_pixels_); - outer_bounds.Inset(-native_window_frame_borders_in_pixels_); - return outer_bounds; + return x11_window_->GetOutterBounds(); } ::Region DesktopWindowTreeHostX11::GetWindowShape() const { - return window_shape_.get(); -} - -void DesktopWindowTreeHostX11::BeforeActivationStateChanged() { - was_active_ = IsActive(); - had_pointer_ = has_pointer_; - had_pointer_grab_ = has_pointer_grab_; - had_window_focus_ = has_window_focus_; -} - -void DesktopWindowTreeHostX11::AfterActivationStateChanged() { - if (had_pointer_grab_ && !has_pointer_grab_) - dispatcher()->OnHostLostMouseGrab(); - - bool had_pointer_capture = had_pointer_ || had_pointer_grab_; - bool has_pointer_capture = has_pointer_ || has_pointer_grab_; - if (had_pointer_capture && !has_pointer_capture) - OnHostLostWindowCapture(); - - if (!was_active_ && IsActive()) { - FlashFrame(false); - // TODO(thomasanderson): Remove this window shuffling and use XWindowCache - // instead. - open_windows().remove(xwindow_); - open_windows().insert(open_windows().begin(), xwindow_); - } - - if (was_active_ != IsActive()) { - desktop_native_widget_aura_->HandleActivationChanged(IsActive()); - native_widget_delegate_->AsWidget()->GetRootView()->SchedulePaint(); - } -} - -void DesktopWindowTreeHostX11::OnCrossingEvent(bool enter, - bool focus_in_window_or_ancestor, - int mode, - int detail) { - // NotifyInferior on a crossing event means the pointer moved into or out of a - // child window, but the pointer is still within |xwindow_|. - if (detail == NotifyInferior) - return; - - BeforeActivationStateChanged(); - - if (mode == NotifyGrab) - has_pointer_grab_ = enter; - else if (mode == NotifyUngrab) - has_pointer_grab_ = false; - - has_pointer_ = enter; - if (focus_in_window_or_ancestor && !has_window_focus_) { - // If we reach this point, we know the focus is in an ancestor or the - // pointer root. The definition of |has_pointer_focus_| is (An ancestor - // window or the PointerRoot is focused) && |has_pointer_|. Therefore, we - // can just use |has_pointer_| in the assignment. The transitions for when - // the focus changes are handled in OnFocusEvent(). - has_pointer_focus_ = has_pointer_; - } - - AfterActivationStateChanged(); -} - -void DesktopWindowTreeHostX11::OnFocusEvent(bool focus_in, - int mode, - int detail) { - // NotifyInferior on a focus event means the focus moved into or out of a - // child window, but the focus is still within |xwindow_|. - if (detail == NotifyInferior) - return; - - bool notify_grab = mode == NotifyGrab || mode == NotifyUngrab; - - BeforeActivationStateChanged(); - - // For every focus change, the X server sends normal focus events which are - // useful for tracking |has_window_focus_|, but supplements these events with - // NotifyPointer events which are only useful for tracking pointer focus. - - // For |has_pointer_focus_| and |has_window_focus_|, we continue tracking - // state during a grab, but ignore grab/ungrab events themselves. - if (!notify_grab && detail != NotifyPointer) - has_window_focus_ = focus_in; - - if (!notify_grab && has_pointer_) { - switch (detail) { - case NotifyAncestor: - case NotifyVirtual: - // If we reach this point, we know |has_pointer_| was true before and - // after this event. Since the definition of |has_pointer_focus_| is - // (An ancestor window or the PointerRoot is focused) && |has_pointer_|, - // we only need to worry about transitions on the first conjunct. - // Therefore, |has_pointer_focus_| will become true when: - // 1. Focus moves from |xwindow_| to an ancestor - // (FocusOut with NotifyAncestor) - // 2. Focus moves from a decendant of |xwindow_| to an ancestor - // (FocusOut with NotifyVirtual) - // |has_pointer_focus_| will become false when: - // 1. Focus moves from an ancestor to |xwindow_| - // (FocusIn with NotifyAncestor) - // 2. Focus moves from an ancestor to a child of |xwindow_| - // (FocusIn with NotifyVirtual) - has_pointer_focus_ = !focus_in; - break; - case NotifyPointer: - // The remaining cases for |has_pointer_focus_| becoming true are: - // 3. Focus moves from |xwindow_| to the PointerRoot - // 4. Focus moves from a decendant of |xwindow_| to the PointerRoot - // 5. Focus moves from None to the PointerRoot - // 6. Focus moves from Other to the PointerRoot - // 7. Focus moves from None to an ancestor of |xwindow_| - // 8. Focus moves from Other to an ancestor fo |xwindow_| - // In each case, we will get a FocusIn with a detail of NotifyPointer. - // The remaining cases for |has_pointer_focus_| becoming false are: - // 3. Focus moves from the PointerRoot to |xwindow_| - // 4. Focus moves from the PointerRoot to a decendant of |xwindow| - // 5. Focus moves from the PointerRoot to None - // 6. Focus moves from an ancestor of |xwindow_| to None - // 7. Focus moves from the PointerRoot to Other - // 8. Focus moves from an ancestor of |xwindow_| to Other - // In each case, we will get a FocusOut with a detail of NotifyPointer. - has_pointer_focus_ = focus_in; - break; - case NotifyNonlinear: - case NotifyNonlinearVirtual: - // We get Nonlinear(Virtual) events when - // 1. Focus moves from Other to |xwindow_| - // (FocusIn with NotifyNonlinear) - // 2. Focus moves from Other to a decendant of |xwindow_| - // (FocusIn with NotifyNonlinearVirtual) - // 3. Focus moves from |xwindow_| to Other - // (FocusOut with NotifyNonlinear) - // 4. Focus moves from a decendant of |xwindow_| to Other - // (FocusOut with NotifyNonlinearVirtual) - // |has_pointer_focus_| should be false before and after this event. - has_pointer_focus_ = false; - break; - default: - break; - } - } - - ignore_keyboard_input_ = false; - - AfterActivationStateChanged(); + return x11_window_->shape(); } void DesktopWindowTreeHostX11::AddObserver( @@ -406,23 +296,10 @@ void DesktopWindowTreeHostX11::CleanUpWindowList( // DesktopWindowTreeHostX11, DesktopWindowTreeHost implementation: void DesktopWindowTreeHostX11::Init(const Widget::InitParams& params) { - activatable_ = (params.activatable == Widget::InitParams::ACTIVATABLE_YES); - if (params.type == Widget::InitParams::TYPE_WINDOW) content_window()->SetProperty(aura::client::kAnimationsDisabledKey, true); - // TODO(erg): Check whether we *should* be building a WindowTreeHost here, or - // whether we should be proxying requests to another DRWHL. - - // In some situations, views tries to make a zero sized window, and that - // makes us crash. Make sure we have valid sizes. - Widget::InitParams sanitized_params = params; - if (sanitized_params.bounds.width() == 0) - sanitized_params.bounds.set_width(100); - if (sanitized_params.bounds.height() == 0) - sanitized_params.bounds.set_height(100); - - InitX11Window(sanitized_params); + InitX11Window(params); InitHost(); window()->Show(); } @@ -462,7 +339,7 @@ std::unique_ptr<aura::client::DragDropClient> DesktopWindowTreeHostX11::CreateDragDropClient( DesktopNativeCursorManager* cursor_manager) { drag_drop_client_ = new DesktopDragDropClientAuraX11( - window(), cursor_manager, xdisplay_, xwindow_); + window(), cursor_manager, x11_window_->display(), x11_window_->window()); drag_drop_client_->Init(); return base::WrapUnique(drag_drop_client_); } @@ -471,7 +348,7 @@ void DesktopWindowTreeHostX11::Close() { content_window()->Hide(); // TODO(erg): Might need to do additional hiding tasks here. - delayed_resize_task_.Cancel(); + x11_window_->CancelResize(); if (!close_widget_factory_.HasWeakPtrs()) { // And we delay the close so that if we are called from an ATL callback, @@ -485,7 +362,7 @@ void DesktopWindowTreeHostX11::Close() { } void DesktopWindowTreeHostX11::CloseNow() { - if (xwindow_ == x11::None) + if (x11_window_->window() == x11::None) return; ReleaseCapture(); @@ -515,20 +392,12 @@ void DesktopWindowTreeHostX11::CloseNow() { // causes a crash with in-process renderer. DestroyCompositor(); - open_windows().remove(xwindow_); + open_windows().remove(x11_window_->window()); // Actually free our native resources. - if (ui::PlatformEventSource::GetInstance()) - ui::PlatformEventSource::GetInstance()->RemovePlatformEventDispatcher(this); - XDestroyWindow(xdisplay_, xwindow_); - xwindow_ = x11::None; - - if (update_counter_ != x11::None) { - XSyncDestroyCounter(xdisplay_, update_counter_); - XSyncDestroyCounter(xdisplay_, extended_update_counter_); - update_counter_ = x11::None; - extended_update_counter_ = x11::None; - } + if (auto* source = ui::PlatformEventSource::GetInstance()) + source->RemovePlatformEventDispatcher(this); + x11_window_->Close(); desktop_native_widget_aura_->OnHostClosed(); } @@ -541,7 +410,7 @@ void DesktopWindowTreeHostX11::Show(ui::WindowShowState show_state, if (compositor()) SetVisible(true); - if (!window_mapped_in_client_ || IsMinimized()) + if (!x11_window_->mapped_in_client() || IsMinimized()) MapWindow(show_state); switch (show_state) { @@ -552,7 +421,6 @@ void DesktopWindowTreeHostX11::Show(ui::WindowShowState show_state, // have reset it. restored_bounds_in_pixels_ = ToPixelRect(restore_bounds); } - break; case ui::SHOW_STATE_MINIMIZED: Minimize(); @@ -570,19 +438,17 @@ void DesktopWindowTreeHostX11::Show(ui::WindowShowState show_state, } bool DesktopWindowTreeHostX11::IsVisible() const { - // On Windows, IsVisible() returns true for minimized windows. On X11, a - // minimized window is not mapped, so an explicit IsMinimized() check is - // necessary. - return window_mapped_in_client_ || IsMinimized(); + return x11_window_->IsVisible(); } void DesktopWindowTreeHostX11::SetSize(const gfx::Size& requested_size) { gfx::Size size_in_pixels = ToPixelRect(gfx::Rect(requested_size)).size(); size_in_pixels = AdjustSize(size_in_pixels); - bool size_changed = bounds_in_pixels_.size() != size_in_pixels; - XResizeWindow(xdisplay_, xwindow_, size_in_pixels.width(), - size_in_pixels.height()); - bounds_in_pixels_.set_size(size_in_pixels); + + bool size_changed = x11_window_->bounds().size() != size_in_pixels; + + x11_window_->SetSize(size_in_pixels); + if (size_changed) { OnHostResizedInPixels(size_in_pixels); ResetWindowRegion(); @@ -590,13 +456,16 @@ void DesktopWindowTreeHostX11::SetSize(const gfx::Size& requested_size) { } void DesktopWindowTreeHostX11::StackAbove(aura::Window* window) { + XDisplay* display = x11_window_->display(); + ::Window xwindow = x11_window_->window(); + if (window && window->GetRootWindow()) { ::Window window_below = window->GetHost()->GetAcceleratedWidget(); // Find all parent windows up to the root. std::vector<::Window> window_below_parents = - GetParentsList(xdisplay_, window_below); + GetParentsList(display, window_below); std::vector<::Window> window_above_parents = - GetParentsList(xdisplay_, xwindow_); + GetParentsList(display, xwindow); // Find their common ancestor. auto it_below_window = window_below_parents.rbegin(); @@ -609,19 +478,19 @@ void DesktopWindowTreeHostX11::StackAbove(aura::Window* window) { if (it_below_window != window_below_parents.rend() && it_above_window != window_above_parents.rend()) { - // First stack |xwindow_| below so Z-order of |window| stays the same. + // First stack |xwindow| below so Z-order of |window| stays the same. ::Window windows[] = {*it_below_window, *it_above_window}; - if (XRestackWindows(xdisplay_, windows, 2) == 0) { + if (XRestackWindows(display, windows, 2) == 0) { // Now stack them properly. std::swap(windows[0], windows[1]); - XRestackWindows(xdisplay_, windows, 2); + XRestackWindows(display, windows, 2); } } } } void DesktopWindowTreeHostX11::StackAtTop() { - XRaiseWindow(xdisplay_, xwindow_); + x11_window_->StackAtTop(); } void DesktopWindowTreeHostX11::CenterWindow(const gfx::Size& size) { @@ -671,7 +540,8 @@ void DesktopWindowTreeHostX11::GetWindowPlacement( } gfx::Rect DesktopWindowTreeHostX11::GetWindowBoundsInScreen() const { - return ToDIPRect(bounds_in_pixels_); + gfx::Rect bounds_in_pixels = x11_window_->bounds(); + return ToDIPRect(bounds_in_pixels); } gfx::Rect DesktopWindowTreeHostX11::GetClientAreaBoundsInScreen() const { @@ -698,15 +568,8 @@ gfx::Rect DesktopWindowTreeHostX11::GetRestoredBounds() const { } std::string DesktopWindowTreeHostX11::GetWorkspace() const { - return workspace_ ? base::NumberToString(workspace_.value()) : std::string(); -} - -void DesktopWindowTreeHostX11::UpdateWorkspace() { - int workspace; - if (ui::GetWindowDesktop(xwindow_, &workspace)) - workspace_ = workspace; - else - workspace_ = base::nullopt; + base::Optional<int> workspace = x11_window_->workspace(); + return workspace ? base::NumberToString(workspace.value()) : std::string(); } gfx::Rect DesktopWindowTreeHostX11::GetWorkAreaBoundsInScreen() const { @@ -717,9 +580,7 @@ gfx::Rect DesktopWindowTreeHostX11::GetWorkAreaBoundsInScreen() const { void DesktopWindowTreeHostX11::SetShape( std::unique_ptr<Widget::ShapeRects> native_shape) { - custom_window_shape_ = false; - window_shape_.reset(); - + _XRegion* xregion = nullptr; if (native_shape) { SkRegion native_region; for (const gfx::Rect& rect : *native_shape) @@ -730,171 +591,102 @@ void DesktopWindowTreeHostX11::SetShape( if (native_region.getBoundaryPath(&path_in_dip)) { SkPath path_in_pixels; path_in_dip.transform(transform.matrix(), &path_in_pixels); - window_shape_.reset(gfx::CreateRegionFromSkPath(path_in_pixels)); + xregion = gfx::CreateRegionFromSkPath(path_in_pixels); } else { - window_shape_.reset(XCreateRegion()); + xregion = XCreateRegion(); } } else { - window_shape_.reset(gfx::CreateRegionFromSkRegion(native_region)); + xregion = gfx::CreateRegionFromSkRegion(native_region); } - - custom_window_shape_ = true; } + x11_window_->SetShape(xregion); ResetWindowRegion(); } void DesktopWindowTreeHostX11::Activate() { - if (!IsVisible() || !activatable_) - return; - - BeforeActivationStateChanged(); - - ignore_keyboard_input_ = false; - - // wmii says that it supports _NET_ACTIVE_WINDOW but does not. - // https://code.google.com/p/wmii/issues/detail?id=266 - static bool wm_supports_active_window = - ui::GuessWindowManager() != ui::WM_WMII && - ui::WmSupportsHint(gfx::GetAtom("_NET_ACTIVE_WINDOW")); - - Time timestamp = ui::X11EventSource::GetInstance()->GetTimestamp(); - - // override_redirect windows ignore _NET_ACTIVE_WINDOW. - // https://crbug.com/940924 - if (wm_supports_active_window && !override_redirect_) { - XEvent xclient; - memset(&xclient, 0, sizeof(xclient)); - xclient.type = ClientMessage; - xclient.xclient.window = xwindow_; - xclient.xclient.message_type = gfx::GetAtom("_NET_ACTIVE_WINDOW"); - xclient.xclient.format = 32; - xclient.xclient.data.l[0] = 1; // Specified we are an app. - xclient.xclient.data.l[1] = timestamp; - // TODO(thomasanderson): if another chrome window is active, specify that in - // data.l[2]. The EWMH spec claims this may make the WM more likely to - // service our _NET_ACTIVE_WINDOW request. - xclient.xclient.data.l[2] = x11::None; - xclient.xclient.data.l[3] = 0; - xclient.xclient.data.l[4] = 0; - - XSendEvent(xdisplay_, x_root_window_, x11::False, - SubstructureRedirectMask | SubstructureNotifyMask, &xclient); - } else { - XRaiseWindow(xdisplay_, xwindow_); - // Directly ask the X server to give focus to the window. Note that the call - // would have raised an X error if the window is not mapped. - auto old_error_handler = XSetErrorHandler(IgnoreX11Errors); - XSetInputFocus(xdisplay_, xwindow_, RevertToParent, timestamp); - // At this point, we know we will receive focus, and some - // webdriver tests depend on a window being IsActive() immediately - // after an Activate(), so just set this state now. - has_pointer_focus_ = false; - has_window_focus_ = true; - window_mapped_in_server_ = true; - XSetErrorHandler(old_error_handler); - } - AfterActivationStateChanged(); + x11_window_->Activate(); } void DesktopWindowTreeHostX11::Deactivate() { - BeforeActivationStateChanged(); - - // Ignore future input events. - ignore_keyboard_input_ = true; - ReleaseCapture(); - XLowerWindow(xdisplay_, xwindow_); - - AfterActivationStateChanged(); + x11_window_->Deactivate(); } bool DesktopWindowTreeHostX11::IsActive() const { - // Focus and stacking order are independent in X11. Since we cannot guarantee - // a window is topmost iff it has focus, just use the focus state to determine - // if a window is active. Note that Activate() and Deactivate() change the - // stacking order in addition to changing the focus state. - bool is_active = - (has_window_focus_ || has_pointer_focus_) && !ignore_keyboard_input_; - - // is_active => window_mapped_in_server_ - // !window_mapped_in_server_ => !is_active - DCHECK(!is_active || window_mapped_in_server_); - - // |has_window_focus_| and |has_pointer_focus_| are mutually exclusive. - DCHECK(!has_window_focus_ || !has_pointer_focus_); - - return is_active; + return x11_window_->IsActive(); } void DesktopWindowTreeHostX11::Maximize() { - if (ui::HasWMSpecProperty(window_properties_, - gfx::GetAtom("_NET_WM_STATE_FULLSCREEN"))) { + // TODO(nickdiego): Move into XWindow. For now, it is kept outside + // it due to |AdjustSize|, which depends on display::Display, which is not + // accessible at Ozone layer. + if (x11_window_->IsFullscreen()) { // Unfullscreen the window if it is fullscreen. - SetWMSpecState(false, gfx::GetAtom("_NET_WM_STATE_FULLSCREEN"), x11::None); + x11_window_->SetFullscreen(false); // Resize the window so that it does not have the same size as a monitor. // (Otherwise, some window managers immediately put the window back in // fullscreen mode). - gfx::Rect adjusted_bounds_in_pixels(bounds_in_pixels_.origin(), - AdjustSize(bounds_in_pixels_.size())); - if (adjusted_bounds_in_pixels != bounds_in_pixels_) + gfx::Rect bounds = x11_window_->bounds(); + gfx::Rect adjusted_bounds_in_pixels(bounds.origin(), + AdjustSize(bounds.size())); + if (adjusted_bounds_in_pixels != bounds) SetBoundsInPixels(adjusted_bounds_in_pixels); } - // Some WMs do not respect maximization hints on unmapped windows, so we - // save this one for later too. - should_maximize_after_map_ = !window_mapped_in_client_; - // When we are in the process of requesting to maximize a window, we can // accurately keep track of our restored bounds instead of relying on the // heuristics that are in the PropertyNotify and ConfigureNotify handlers. - restored_bounds_in_pixels_ = bounds_in_pixels_; + restored_bounds_in_pixels_ = x11_window_->bounds(); - SetWMSpecState(true, gfx::GetAtom("_NET_WM_STATE_MAXIMIZED_VERT"), - gfx::GetAtom("_NET_WM_STATE_MAXIMIZED_HORZ")); + x11_window_->Maximize(); if (IsMinimized()) Show(ui::SHOW_STATE_NORMAL, gfx::Rect()); } void DesktopWindowTreeHostX11::Minimize() { ReleaseCapture(); - if (window_mapped_in_client_) - XIconifyWindow(xdisplay_, xwindow_, 0); - else - SetWMSpecState(true, gfx::GetAtom("_NET_WM_STATE_HIDDEN"), x11::None); + x11_window_->Minimize(); } void DesktopWindowTreeHostX11::Restore() { - should_maximize_after_map_ = false; - SetWMSpecState(false, gfx::GetAtom("_NET_WM_STATE_MAXIMIZED_VERT"), - gfx::GetAtom("_NET_WM_STATE_MAXIMIZED_HORZ")); + x11_window_->Unmaximize(); Show(ui::SHOW_STATE_NORMAL, gfx::Rect()); - SetWMSpecState(false, gfx::GetAtom("_NET_WM_STATE_HIDDEN"), x11::None); + x11_window_->Unhide(); } bool DesktopWindowTreeHostX11::IsMaximized() const { - return (ui::HasWMSpecProperty(window_properties_, - gfx::GetAtom("_NET_WM_STATE_MAXIMIZED_VERT")) && - ui::HasWMSpecProperty(window_properties_, - gfx::GetAtom("_NET_WM_STATE_MAXIMIZED_HORZ"))); + return x11_window_->IsMaximized(); } bool DesktopWindowTreeHostX11::IsMinimized() const { - return ui::HasWMSpecProperty(window_properties_, - gfx::GetAtom("_NET_WM_STATE_HIDDEN")); + return x11_window_->IsMinimized(); } bool DesktopWindowTreeHostX11::HasCapture() const { return g_current_capture == this; } -void DesktopWindowTreeHostX11::SetAlwaysOnTop(bool always_on_top) { - is_always_on_top_ = always_on_top; - SetWMSpecState(always_on_top, gfx::GetAtom("_NET_WM_STATE_ABOVE"), x11::None); +void DesktopWindowTreeHostX11::SetZOrderLevel(ui::ZOrderLevel order) { + z_order_ = order; + + // Emulate the multiple window levels provided by other platforms by + // collapsing the z-order enum into kNormal = normal, everything else = always + // on top. + x11_window_->SetAlwaysOnTop(order != ui::ZOrderLevel::kNormal); } -bool DesktopWindowTreeHostX11::IsAlwaysOnTop() const { - return is_always_on_top_; +ui::ZOrderLevel DesktopWindowTreeHostX11::GetZOrderLevel() const { + bool window_always_on_top = x11_window_->is_always_on_top(); + bool level_always_on_top = z_order_ != ui::ZOrderLevel::kNormal; + + if (window_always_on_top == level_always_on_top) + return z_order_; + + // If something external has forced a window to be always-on-top, map it to + // kFloatingWindow as a reasonable equivalent. + return window_always_on_top ? ui::ZOrderLevel::kFloatingWindow + : ui::ZOrderLevel::kNormal; } void DesktopWindowTreeHostX11::SetVisible(bool visible) { @@ -904,62 +696,20 @@ void DesktopWindowTreeHostX11::SetVisible(bool visible) { is_compositor_set_visible_ = visible; if (compositor()) compositor()->SetVisible(visible); + native_widget_delegate_->OnNativeWidgetVisibilityChanged(visible); } void DesktopWindowTreeHostX11::SetVisibleOnAllWorkspaces(bool always_visible) { - SetWMSpecState(always_visible, gfx::GetAtom("_NET_WM_STATE_STICKY"), - x11::None); - - int new_desktop = 0; - if (always_visible) { - new_desktop = kAllDesktops; - } else { - if (!ui::GetCurrentDesktop(&new_desktop)) - return; - } - - workspace_ = kAllDesktops; - XEvent xevent; - memset (&xevent, 0, sizeof (xevent)); - xevent.type = ClientMessage; - xevent.xclient.window = xwindow_; - xevent.xclient.message_type = gfx::GetAtom("_NET_WM_DESKTOP"); - xevent.xclient.format = 32; - xevent.xclient.data.l[0] = new_desktop; - xevent.xclient.data.l[1] = 0; - xevent.xclient.data.l[2] = 0; - xevent.xclient.data.l[3] = 0; - xevent.xclient.data.l[4] = 0; - XSendEvent(xdisplay_, x_root_window_, x11::False, - SubstructureRedirectMask | SubstructureNotifyMask, &xevent); + x11_window_->SetVisibleOnAllWorkspaces(always_visible); } bool DesktopWindowTreeHostX11::IsVisibleOnAllWorkspaces() const { - // We don't need a check for _NET_WM_STATE_STICKY because that would specify - // that the window remain in a fixed position even if the viewport scrolls. - // This is different from the type of workspace that's associated with - // _NET_WM_DESKTOP. - return GetWorkspace() == base::NumberToString(kAllDesktops); + return x11_window_->IsVisibleOnAllWorkspaces(); } bool DesktopWindowTreeHostX11::SetWindowTitle(const base::string16& title) { - if (window_title_ == title) - return false; - window_title_ = title; - std::string utf8str = base::UTF16ToUTF8(title); - XChangeProperty(xdisplay_, xwindow_, gfx::GetAtom("_NET_WM_NAME"), - gfx::GetAtom("UTF8_STRING"), 8, PropModeReplace, - reinterpret_cast<const unsigned char*>(utf8str.c_str()), - utf8str.size()); - XTextProperty xtp; - char* c_utf8_str = const_cast<char*>(utf8str.c_str()); - if (Xutf8TextListToTextProperty(xdisplay_, &c_utf8_str, 1, XUTF8StringStyle, - &xtp) == x11::Success) { - XSetWMName(xdisplay_, xwindow_, &xtp); - XFree(xtp.value); - } - return true; + return x11_window_->SetTitle(title); } void DesktopWindowTreeHostX11::ClearNativeFocus() { @@ -1005,11 +755,11 @@ NonClientFrameView* DesktopWindowTreeHostX11::CreateNonClientFrameView() { } bool DesktopWindowTreeHostX11::ShouldUseNativeFrame() const { - return use_native_frame_; + return x11_window_->use_native_frame(); } bool DesktopWindowTreeHostX11::ShouldWindowContentsBeTransparent() const { - return use_argb_visual_; + return x11_window_->has_alpha(); } void DesktopWindowTreeHostX11::FrameTypeChanged() { @@ -1034,9 +784,10 @@ void DesktopWindowTreeHostX11::FrameTypeChanged() { void DesktopWindowTreeHostX11::SetFullscreen(bool fullscreen) { if (is_fullscreen_ == fullscreen) return; + is_fullscreen_ = fullscreen; if (is_fullscreen_) - delayed_resize_task_.Cancel(); + x11_window_->CancelResize(); // Work around a bug where if we try to unfullscreen, metacity immediately // fullscreens us again. This is a little flickery and not necessary if @@ -1047,8 +798,9 @@ void DesktopWindowTreeHostX11::SetFullscreen(bool fullscreen) { if (unmaximize_and_remaximize) Restore(); - SetWMSpecState(fullscreen, gfx::GetAtom("_NET_WM_STATE_FULLSCREEN"), - x11::None); + + x11_window_->SetFullscreen(fullscreen); + if (unmaximize_and_remaximize) Maximize(); @@ -1057,20 +809,21 @@ void DesktopWindowTreeHostX11::SetFullscreen(bool fullscreen) { // - works around Flash content which expects to have the size updated // synchronously. // See https://crbug.com/361408 + gfx::Rect bounds = x11_window_->bounds(); if (fullscreen) { - restored_bounds_in_pixels_ = bounds_in_pixels_; - const display::Display display = - display::Screen::GetScreen()->GetDisplayNearestWindow(window()); - bounds_in_pixels_ = ToPixelRect(display.bounds()); + display::Screen* screen = display::Screen::GetScreen(); + const display::Display display = screen->GetDisplayNearestWindow(window()); + restored_bounds_in_pixels_ = bounds; + bounds = ToPixelRect(display.bounds()); } else { - bounds_in_pixels_ = restored_bounds_in_pixels_; + bounds = restored_bounds_in_pixels_; } - OnHostMovedInPixels(bounds_in_pixels_.origin()); - OnHostResizedInPixels(bounds_in_pixels_.size()); + x11_window_->set_bounds(bounds); + + OnHostMovedInPixels(bounds.origin()); + OnHostResizedInPixels(bounds.size()); - if (ui::HasWMSpecProperty(window_properties_, - gfx::GetAtom("_NET_WM_STATE_FULLSCREEN")) == - fullscreen) { + if (x11_window_->IsFullscreen() == fullscreen) { Relayout(); ResetWindowRegion(); } @@ -1083,59 +836,16 @@ bool DesktopWindowTreeHostX11::IsFullscreen() const { } void DesktopWindowTreeHostX11::SetOpacity(float opacity) { - // X server opacity is in terms of 32 bit unsigned int space, and counts from - // the opposite direction. - // XChangeProperty() expects "cardinality" to be long. - - // Scale opacity to [0 .. 255] range. - unsigned long opacity_8bit = - static_cast<unsigned long>(opacity * 255.0f) & 0xFF; - // Use opacity value for all channels. - const unsigned long channel_multiplier = 0x1010101; - unsigned long cardinality = opacity_8bit * channel_multiplier; - - if (cardinality == 0xffffffff) { - XDeleteProperty(xdisplay_, xwindow_, - gfx::GetAtom("_NET_WM_WINDOW_OPACITY")); - } else { - XChangeProperty(xdisplay_, xwindow_, gfx::GetAtom("_NET_WM_WINDOW_OPACITY"), - XA_CARDINAL, 32, PropModeReplace, - reinterpret_cast<unsigned char*>(&cardinality), 1); - } + x11_window_->SetOpacity(opacity); } void DesktopWindowTreeHostX11::SetAspectRatio(const gfx::SizeF& aspect_ratio) { - XSizeHints size_hints; - size_hints.flags = 0; - long supplied_return; - - XGetWMNormalHints(xdisplay_, xwindow_, &size_hints, &supplied_return); - size_hints.flags |= PAspect; - size_hints.min_aspect.x = size_hints.max_aspect.x = aspect_ratio.width(); - size_hints.min_aspect.y = size_hints.max_aspect.y = aspect_ratio.height(); - XSetWMNormalHints(xdisplay_, xwindow_, &size_hints); + x11_window_->SetAspectRatio(aspect_ratio); } void DesktopWindowTreeHostX11::SetWindowIcons( const gfx::ImageSkia& window_icon, const gfx::ImageSkia& app_icon) { - // TODO(erg): The way we handle icons across different versions of chrome - // could be substantially improved. The Windows version does its own thing - // and only sometimes comes down this code path. The icon stuff in - // ChromeViewsDelegate is hard coded to use HICONs. Likewise, we're hard - // coded to be given two images instead of an arbitrary collection of images - // so that we can pass to the WM. - // - // All of this could be made much, much better. - std::vector<unsigned long> data; - - if (!window_icon.isNull()) - SerializeImageRepresentation(window_icon.GetRepresentation(1.0f), &data); - - if (!app_icon.isNull()) - SerializeImageRepresentation(app_icon.GetRepresentation(1.0f), &data); - - if (!data.empty()) - ui::SetAtomArrayProperty(xwindow_, "_NET_WM_ICON", "CARDINAL", data); + x11_window_->SetWindowIcons(window_icon, app_icon); } void DesktopWindowTreeHostX11::InitModalType(ui::ModalType modal_type) { @@ -1151,23 +861,7 @@ void DesktopWindowTreeHostX11::InitModalType(ui::ModalType modal_type) { } void DesktopWindowTreeHostX11::FlashFrame(bool flash_frame) { - if (urgency_hint_set_ == flash_frame) - return; - - gfx::XScopedPtr<XWMHints> hints(XGetWMHints(xdisplay_, xwindow_)); - if (!hints) { - // The window hasn't had its hints set yet. - hints.reset(XAllocWMHints()); - } - - if (flash_frame) - hints->flags |= XUrgencyHint; - else - hints->flags &= ~XUrgencyHint; - - XSetWMHints(xdisplay_, xwindow_, hints.get()); - - urgency_hint_set_ = flash_frame; + x11_window_->FlashFrame(flash_frame); } bool DesktopWindowTreeHostX11::IsAnimatingClosed() const { @@ -1176,13 +870,13 @@ bool DesktopWindowTreeHostX11::IsAnimatingClosed() const { bool DesktopWindowTreeHostX11::IsTranslucentWindowOpacitySupported() const { // This function may be called before InitX11Window() (which - // initializes |use_argb_visual_|), so we cannot simply return - // |use_argb_visual_|. + // initializes |visual_has_alpha_|), so we cannot simply return + // |visual_has_alpha_|. return ui::XVisualManager::GetInstance()->ArgbVisualAvailable(); } void DesktopWindowTreeHostX11::SizeConstraintsChanged() { - UpdateMinAndMaxSize(); + x11_window_->UpdateMinAndMaxSize(); } bool DesktopWindowTreeHostX11::ShouldUpdateWindowTransparency() const { @@ -1218,7 +912,7 @@ ui::EventSource* DesktopWindowTreeHostX11::GetEventSource() { } gfx::AcceleratedWidget DesktopWindowTreeHostX11::GetAcceleratedWidget() { - return xwindow_; + return x11_window_->window(); } void DesktopWindowTreeHostX11::ShowImpl() { @@ -1226,69 +920,34 @@ void DesktopWindowTreeHostX11::ShowImpl() { } void DesktopWindowTreeHostX11::HideImpl() { - if (window_mapped_in_client_) { - XWithdrawWindow(xdisplay_, xwindow_, 0); - window_mapped_in_client_ = false; + if (x11_window_->Hide()) SetVisible(false); - } } gfx::Rect DesktopWindowTreeHostX11::GetBoundsInPixels() const { - return bounds_in_pixels_; + return x11_window_->bounds(); } void DesktopWindowTreeHostX11::SetBoundsInPixels( const gfx::Rect& requested_bounds_in_pixel) { + gfx::Rect bounds = x11_window_->bounds(); gfx::Rect bounds_in_pixels(requested_bounds_in_pixel.origin(), AdjustSize(requested_bounds_in_pixel.size())); - bool origin_changed = bounds_in_pixels_.origin() != bounds_in_pixels.origin(); - bool size_changed = bounds_in_pixels_.size() != bounds_in_pixels.size(); - XWindowChanges changes = {0}; - unsigned value_mask = 0; + bool origin_changed = bounds.origin() != bounds_in_pixels.origin(); + bool size_changed = bounds.size() != bounds_in_pixels.size(); if (size_changed) { // Only cancel the delayed resize task if we're already about to call // OnHostResized in this function. - delayed_resize_task_.Cancel(); - - // Update the minimum and maximum sizes in case they have changed. - UpdateMinAndMaxSize(); - - if (bounds_in_pixels.width() < min_size_in_pixels_.width() || - bounds_in_pixels.height() < min_size_in_pixels_.height() || - (!max_size_in_pixels_.IsEmpty() && - (bounds_in_pixels.width() > max_size_in_pixels_.width() || - bounds_in_pixels.height() > max_size_in_pixels_.height()))) { - gfx::Size size_in_pixels = bounds_in_pixels.size(); - if (!max_size_in_pixels_.IsEmpty()) - size_in_pixels.SetToMin(max_size_in_pixels_); - size_in_pixels.SetToMax(min_size_in_pixels_); - bounds_in_pixels.set_size(size_in_pixels); - } - - changes.width = bounds_in_pixels.width(); - changes.height = bounds_in_pixels.height(); - value_mask |= CWHeight | CWWidth; + x11_window_->CancelResize(); } - if (origin_changed) { - changes.x = bounds_in_pixels.x(); - changes.y = bounds_in_pixels.y(); - value_mask |= CWX | CWY; - } - if (value_mask) - XConfigureWindow(xdisplay_, xwindow_, value_mask, &changes); - - // Assume that the resize will go through as requested, which should be the - // case if we're running without a window manager. If there's a window - // manager, it can modify or ignore the request, but (per ICCCM) we'll get a - // (possibly synthetic) ConfigureNotify about the actual size and correct - // |bounds_in_pixels_| later. - bounds_in_pixels_ = bounds_in_pixels; + x11_window_->SetBounds(bounds_in_pixels); if (origin_changed) native_widget_delegate_->AsWidget()->OnNativeWidgetMove(); + if (size_changed) { OnHostResizedInPixels(bounds_in_pixels.size()); ResetWindowRegion(); @@ -1296,7 +955,7 @@ void DesktopWindowTreeHostX11::SetBoundsInPixels( } gfx::Point DesktopWindowTreeHostX11::GetLocationOnScreenInPixels() const { - return bounds_in_pixels_.origin(); + return x11_window_->bounds().origin(); } void DesktopWindowTreeHostX11::SetCapture() { @@ -1318,9 +977,7 @@ void DesktopWindowTreeHostX11::SetCapture() { if (old_capturer) old_capturer->OnHostLostWindowCapture(); - // If the pointer is already in |xwindow_|, we will not get a crossing event - // with a mode of NotifyGrab, so we must record the grab state manually. - has_pointer_grab_ |= !ui::GrabPointer(xwindow_, true, x11::None); + x11_window_->GrabPointer(); } void DesktopWindowTreeHostX11::ReleaseCapture() { @@ -1329,8 +986,7 @@ void DesktopWindowTreeHostX11::ReleaseCapture() { // the topmost window underneath the mouse so the capture release being // asynchronous is likely inconsequential. g_current_capture = nullptr; - ui::UngrabPointer(); - has_pointer_grab_ = false; + x11_window_->ReleasePointerGrab(); OnHostLostWindowCapture(); } @@ -1359,14 +1015,12 @@ bool DesktopWindowTreeHostX11::IsKeyLocked(ui::DomCode dom_code) { } void DesktopWindowTreeHostX11::SetCursorNative(gfx::NativeCursor cursor) { - XDefineCursor(xdisplay_, xwindow_, cursor.platform()); + x11_window_->SetCursor(cursor.platform()); } void DesktopWindowTreeHostX11::MoveCursorToScreenLocationInPixels( const gfx::Point& location_in_pixels) { - XWarpPointer(xdisplay_, x11::None, x_root_window_, 0, 0, 0, 0, - bounds_in_pixels_.x() + location_in_pixels.x(), - bounds_in_pixels_.y() + location_in_pixels.y()); + x11_window_->MoveCursorTo(location_in_pixels); } void DesktopWindowTreeHostX11::OnCursorVisibilityChangedNative(bool show) { @@ -1390,357 +1044,29 @@ void DesktopWindowTreeHostX11::OnDisplayMetricsChanged( // compositor redraw will be scheduled. This is weird, but works. // TODO(thomasanderson): Figure out a more direct way of doing // this. - RestartDelayedResizeTask(); + x11_window_->DispatchResize(); } } -//////////////////////////////////////////////////////////////////////////////// -// DesktopWindowTreeHostX11, private: - -void DesktopWindowTreeHostX11::InitX11Window( - const Widget::InitParams& params) { - unsigned long attribute_mask = CWBackPixel | CWBitGravity; - XSetWindowAttributes swa; - memset(&swa, 0, sizeof(swa)); - swa.background_pixmap = x11::None; - swa.bit_gravity = NorthWestGravity; - - // Set the background color on startup to make the initial flickering - // happening between the XWindow is mapped and the first expose event - // is completely handled less annoying. If possible, we use the content - // window's background color, otherwise we fallback to white. - int background_color; - - const views::LinuxUI* linux_ui = views::LinuxUI::instance(); - if (linux_ui && content_window()) { - ui::NativeTheme::ColorId target_color; - switch (params.type) { - case Widget::InitParams::TYPE_BUBBLE: - target_color = ui::NativeTheme::kColorId_BubbleBackground; - break; - case Widget::InitParams::TYPE_TOOLTIP: - target_color = ui::NativeTheme::kColorId_TooltipBackground; - break; - default: - target_color = ui::NativeTheme::kColorId_WindowBackground; - break; - } - - ui::NativeTheme* theme = linux_ui->GetNativeTheme(content_window()); - background_color = theme->GetSystemColor(target_color); - } else { - background_color = WhitePixel(xdisplay_, DefaultScreen(xdisplay_)); - } - swa.background_pixel = background_color; - - XAtom window_type; - switch (params.type) { - case Widget::InitParams::TYPE_MENU: - swa.override_redirect = x11::True; - window_type = gfx::GetAtom("_NET_WM_WINDOW_TYPE_MENU"); - break; - case Widget::InitParams::TYPE_TOOLTIP: - swa.override_redirect = x11::True; - window_type = gfx::GetAtom("_NET_WM_WINDOW_TYPE_TOOLTIP"); - break; - case Widget::InitParams::TYPE_POPUP: - swa.override_redirect = x11::True; - window_type = gfx::GetAtom("_NET_WM_WINDOW_TYPE_NOTIFICATION"); - break; - case Widget::InitParams::TYPE_DRAG: - swa.override_redirect = x11::True; - window_type = gfx::GetAtom("_NET_WM_WINDOW_TYPE_DND"); - break; - default: - window_type = gfx::GetAtom("_NET_WM_WINDOW_TYPE_NORMAL"); - break; - } - // An in-activatable window should not interact with the system wm. - if (!activatable_) - swa.override_redirect = x11::True; - - override_redirect_ = swa.override_redirect == x11::True; - if (override_redirect_) - attribute_mask |= CWOverrideRedirect; +void DesktopWindowTreeHostX11::OnXWindowCreated() { + if (auto* source = ui::PlatformEventSource::GetInstance()) + source->AddPlatformEventDispatcher(this); - bool enable_transparent_visuals; - switch (params.opacity) { - case Widget::InitParams::OPAQUE_WINDOW: - enable_transparent_visuals = false; - break; - case Widget::InitParams::TRANSLUCENT_WINDOW: - enable_transparent_visuals = true; - break; - case Widget::InitParams::INFER_OPACITY: - default: - enable_transparent_visuals = params.type == Widget::InitParams::TYPE_DRAG; - } - - Visual* visual = CopyFromParent; - int depth = CopyFromParent; - Colormap colormap = CopyFromParent; - ui::XVisualManager::GetInstance()->ChooseVisualForWindow( - enable_transparent_visuals, &visual, &depth, &colormap, - &use_argb_visual_); - - if (colormap != CopyFromParent) { - attribute_mask |= CWColormap; - swa.colormap = colormap; - } - - // x.org will BadMatch if we don't set a border when the depth isn't the - // same as the parent depth. - attribute_mask |= CWBorderPixel; - swa.border_pixel = 0; - - bounds_in_pixels_ = ToPixelRect(params.bounds); - bounds_in_pixels_.set_size(AdjustSize(bounds_in_pixels_.size())); - xwindow_ = XCreateWindow(xdisplay_, x_root_window_, bounds_in_pixels_.x(), - bounds_in_pixels_.y(), bounds_in_pixels_.width(), - bounds_in_pixels_.height(), - 0, // border width - depth, InputOutput, visual, attribute_mask, &swa); - if (ui::PlatformEventSource::GetInstance()) - ui::PlatformEventSource::GetInstance()->AddPlatformEventDispatcher(this); - open_windows().push_front(xwindow_); - - // TODO(erg): Maybe need to set a ViewProp here like in RWHL::RWHL(). - - long event_mask = ButtonPressMask | ButtonReleaseMask | FocusChangeMask | - KeyPressMask | KeyReleaseMask | - EnterWindowMask | LeaveWindowMask | - ExposureMask | VisibilityChangeMask | - StructureNotifyMask | PropertyChangeMask | - PointerMotionMask; - xwindow_events_ = - std::make_unique<ui::XScopedEventSelector>(xwindow_, event_mask); - XFlush(xdisplay_); - - if (ui::IsXInput2Available()) - ui::TouchFactory::GetInstance()->SetupXI2ForXWindow(xwindow_); - - // TODO(erg): We currently only request window deletion events. We also - // should listen for activation events and anything else that GTK+ listens - // for, and do something useful. - // Request the _NET_WM_SYNC_REQUEST protocol which is used for synchronizing - // between chrome and desktop compositor (or WM) during resizing. - // The resizing behavior with _NET_WM_SYNC_REQUEST is: - // 1. Desktop compositor (or WM) sends client message _NET_WM_SYNC_REQUEST - // with a 64 bits counter to notify about an incoming resize. - // 2. Desktop compositor resizes chrome browser window. - // 3. Desktop compositor waits on an alert on value change of XSyncCounter on - // chrome window. - // 4. Chrome handles the ConfigureNotify event, and renders a new frame with - // the new size. - // 5. Chrome increases the XSyncCounter on chrome window - // 6. Desktop compositor gets the alert of counter change, and draws a new - // frame with new content from chrome. - // 7. Desktop compositor responses user mouse move events, and starts a new - // resize process, go to step 1. - XAtom protocols[] = { - gfx::GetAtom("WM_DELETE_WINDOW"), - gfx::GetAtom("_NET_WM_PING"), - gfx::GetAtom("_NET_WM_SYNC_REQUEST"), - }; - XSetWMProtocols(xdisplay_, xwindow_, protocols, base::size(protocols)); - - // We need a WM_CLIENT_MACHINE and WM_LOCALE_NAME value so we integrate with - // the desktop environment. - XSetWMProperties(xdisplay_, xwindow_, nullptr, nullptr, nullptr, 0, nullptr, - nullptr, nullptr); - - // Likewise, the X server needs to know this window's pid so it knows which - // program to kill if the window hangs. - // XChangeProperty() expects "pid" to be long. - static_assert(sizeof(long) >= sizeof(pid_t), - "pid_t should not be larger than long"); - long pid = getpid(); - XChangeProperty(xdisplay_, xwindow_, gfx::GetAtom("_NET_WM_PID"), XA_CARDINAL, - 32, PropModeReplace, reinterpret_cast<unsigned char*>(&pid), - 1); - - XChangeProperty(xdisplay_, xwindow_, gfx::GetAtom("_NET_WM_WINDOW_TYPE"), - XA_ATOM, 32, PropModeReplace, - reinterpret_cast<unsigned char*>(&window_type), 1); - - // The changes to |window_properties_| here will be sent to the X server just - // before the window is mapped. - - // Remove popup windows from taskbar unless overridden. - if ((params.type == Widget::InitParams::TYPE_POPUP || - params.type == Widget::InitParams::TYPE_BUBBLE) && - !params.force_show_in_taskbar) { - window_properties_.insert(gfx::GetAtom("_NET_WM_STATE_SKIP_TASKBAR")); - } - - // If the window should stay on top of other windows, add the - // _NET_WM_STATE_ABOVE property. - is_always_on_top_ = params.keep_on_top; - if (is_always_on_top_) - window_properties_.insert(gfx::GetAtom("_NET_WM_STATE_ABOVE")); - - workspace_ = base::nullopt; - if (params.visible_on_all_workspaces) { - window_properties_.insert(gfx::GetAtom("_NET_WM_STATE_STICKY")); - ui::SetIntProperty(xwindow_, "_NET_WM_DESKTOP", "CARDINAL", kAllDesktops); - } else if (!params.workspace.empty()) { - int workspace; - if (base::StringToInt(params.workspace, &workspace)) - ui::SetIntProperty(xwindow_, "_NET_WM_DESKTOP", "CARDINAL", workspace); - } - - if (!params.wm_class_name.empty() || !params.wm_class_class.empty()) { - ui::SetWindowClassHint( - xdisplay_, xwindow_, params.wm_class_name, params.wm_class_class); - } - - const char* wm_role_name = nullptr; - // If the widget isn't overriding the role, provide a default value for popup - // and bubble types. - if (!params.wm_role_name.empty()) { - wm_role_name = params.wm_role_name.c_str(); - } else { - switch (params.type) { - case Widget::InitParams::TYPE_POPUP: - wm_role_name = kX11WindowRolePopup; - break; - case Widget::InitParams::TYPE_BUBBLE: - wm_role_name = kX11WindowRoleBubble; - break; - default: - break; - } - } - if (wm_role_name) - ui::SetWindowRole(xdisplay_, xwindow_, std::string(wm_role_name)); - - if (params.remove_standard_frame) { - // Setting _GTK_HIDE_TITLEBAR_WHEN_MAXIMIZED tells gnome-shell to not force - // fullscreen on the window when it matches the desktop size. - ui::SetHideTitlebarWhenMaximizedProperty(xwindow_, - ui::HIDE_TITLEBAR_WHEN_MAXIMIZED); - } - - if (views::LinuxUI::instance() && - views::LinuxUI::instance()->PreferDarkTheme()) { - const unsigned char kDarkGtkThemeVariant[] = "dark"; - XChangeProperty(xdisplay_, xwindow_, gfx::GetAtom("_GTK_THEME_VARIANT"), - gfx::GetAtom("UTF8_STRING"), 8, PropModeReplace, - kDarkGtkThemeVariant, base::size(kDarkGtkThemeVariant) - 1); - } - - if (ui::IsSyncExtensionAvailable()) { - XSyncValue value; - XSyncIntToValue(&value, 0); - update_counter_ = XSyncCreateCounter(xdisplay_, value); - extended_update_counter_ = XSyncCreateCounter(xdisplay_, value); - XID counters[2] = { - update_counter_, - extended_update_counter_, - }; - - // Set XSyncCounter as window property _NET_WM_SYNC_REQUEST_COUNTER. the - // compositor will listen on them during resizing. - XChangeProperty( - xdisplay_, xwindow_, gfx::GetAtom("_NET_WM_SYNC_REQUEST_COUNTER"), - XA_CARDINAL, 32, PropModeReplace, - reinterpret_cast<const unsigned char*>(counters), base::size(counters)); - } - - // Always composite Chromium windows if a compositing WM is used. Sometimes, - // WMs will not composite fullscreen windows as an optimization, but this can - // lead to tearing of fullscreen videos. - ui::SetIntProperty(xwindow_, "_NET_WM_BYPASS_COMPOSITOR", "CARDINAL", 2); - - // If we have a parent, record the parent/child relationship. We use this - // data during destruction to make sure that when we try to close a parent - // window, we also destroy all child windows. - if (params.parent && params.parent->GetHost()) { - XID parent_xid = - params.parent->GetHost()->GetAcceleratedWidget(); - window_parent_ = GetHostForXID(parent_xid); - DCHECK(window_parent_); - window_parent_->window_children_.insert(this); - } - - // If we have a delegate which is providing a default window icon, use that - // icon. - gfx::ImageSkia* window_icon = - ViewsDelegate::GetInstance()->GetDefaultWindowIcon(); - if (window_icon) { - SetWindowIcons(gfx::ImageSkia(), *window_icon); - } - // Disable compositing on tooltips as a workaround for - // https://crbug.com/442111. - CreateCompositor(viz::FrameSinkId(), - params.type == Widget::InitParams::TYPE_TOOLTIP); - - if (ui::IsSyncExtensionAvailable()) { - compositor_observer_ = std::make_unique<SwapWithNewSizeObserverHelper>( - compositor(), base::BindRepeating( - &DesktopWindowTreeHostX11::OnCompleteSwapWithNewSize, - base::Unretained(this))); - } - OnAcceleratedWidgetAvailable(); + open_windows().push_front(x11_window_->window()); } -gfx::Size DesktopWindowTreeHostX11::AdjustSize( - const gfx::Size& requested_size_in_pixels) { - std::vector<display::Display> displays = - display::Screen::GetScreen()->GetAllDisplays(); - // Compare against all monitor sizes. The window manager can move the window - // to whichever monitor it wants. - for (const auto& display : displays) { - if (requested_size_in_pixels == display.GetSizeInPixel()) { - return gfx::Size(requested_size_in_pixels.width() - 1, - requested_size_in_pixels.height() - 1); - } - } - - // Do not request a 0x0 window size. It causes an XError. - gfx::Size size_in_pixels = requested_size_in_pixels; - size_in_pixels.SetToMax(gfx::Size(1, 1)); - return size_in_pixels; -} - -void DesktopWindowTreeHostX11::SetWMSpecState(bool enabled, - XAtom state1, - XAtom state2) { - if (window_mapped_in_client_) { - ui::SetWMSpecState(xwindow_, enabled, state1, state2); - } else { - // The updated state will be set when the window is (re)mapped. - base::flat_set<XAtom> new_window_properties = window_properties_; - for (XAtom atom : {state1, state2}) { - if (enabled) - new_window_properties.insert(atom); - else - new_window_properties.erase(atom); - } - UpdateWindowProperties(new_window_properties); - } +void DesktopWindowTreeHostX11::OnXWindowMapped() { + for (DesktopWindowTreeHostObserverX11& observer : observer_list_) + observer.OnWindowMapped(x11_window_->window()); } -void DesktopWindowTreeHostX11::OnWMStateUpdated() { - // The EWMH spec requires window managers to remove the _NET_WM_STATE property - // when a window is unmapped. However, Chromium code wants the state to - // persist across a Hide() and Show(). So if the window is currently - // unmapped, leave the state unchanged so it will be restored when the window - // is remapped. - std::vector<XAtom> atom_list; - if (ui::GetAtomArrayProperty(xwindow_, "_NET_WM_STATE", &atom_list) || - window_mapped_in_client_) { - UpdateWindowProperties( - base::flat_set<XAtom>(std::begin(atom_list), std::end(atom_list))); - } +void DesktopWindowTreeHostX11::OnXWindowUnmapped() { + for (DesktopWindowTreeHostObserverX11& observer : observer_list_) + observer.OnWindowUnmapped(x11_window_->window()); } -void DesktopWindowTreeHostX11::UpdateWindowProperties( - const base::flat_set<XAtom>& new_window_properties) { - bool was_minimized = IsMinimized(); - - window_properties_ = new_window_properties; - +void DesktopWindowTreeHostX11::OnXWindowStateChanged() { + bool was_minimized = x11_window_->was_minimized(); bool is_minimized = IsMinimized(); // Propagate the window minimization information to the content window, so @@ -1772,7 +1098,7 @@ void DesktopWindowTreeHostX11::UpdateWindowProperties( // a best effort attempt to get restored bounds by setting it to our // previously set bounds (and if we get this wrong, we aren't any worse // off since we'd otherwise be returning our maximized bounds). - restored_bounds_in_pixels_ = previous_bounds_in_pixels_; + restored_bounds_in_pixels_ = x11_window_->previous_bounds(); } } else if (!IsMaximized() && !IsFullscreen()) { // If we have restored bounds, but WM_STATE no longer claims to be @@ -1780,14 +1106,6 @@ void DesktopWindowTreeHostX11::UpdateWindowProperties( restored_bounds_in_pixels_ = gfx::Rect(); } - // Ignore requests by the window manager to enter or exit fullscreen (e.g. as - // a result of pressing a window manager accelerator key). Chrome does not - // handle window manager initiated fullscreen. In particular, Chrome needs to - // do preprocessing before the x window's fullscreen state is toggled. - - is_always_on_top_ = ui::HasWMSpecProperty( - window_properties_, gfx::GetAtom("_NET_WM_STATE_ABOVE")); - // Now that we have different window properties, we may need to relayout the // window. (The windows code doesn't need this because their window change is // synchronous.) @@ -1795,75 +1113,212 @@ void DesktopWindowTreeHostX11::UpdateWindowProperties( ResetWindowRegion(); } -void DesktopWindowTreeHostX11::OnFrameExtentsUpdated() { - std::vector<int> insets; - if (ui::GetIntArrayProperty(xwindow_, "_NET_FRAME_EXTENTS", &insets) && - insets.size() == 4) { - // |insets| are returned in the order: [left, right, top, bottom]. - native_window_frame_borders_in_pixels_ = - gfx::Insets(insets[2], insets[0], insets[3], insets[1]); - } else { - native_window_frame_borders_in_pixels_ = gfx::Insets(); - } +void DesktopWindowTreeHostX11::OnXWindowWorkspaceChanged() { + OnHostWorkspaceChanged(); } -void DesktopWindowTreeHostX11::UpdateMinAndMaxSize() { - gfx::Size minimum_in_pixels = - ToPixelRect(gfx::Rect(native_widget_delegate_->GetMinimumSize())).size(); - gfx::Size maximum_in_pixels = - ToPixelRect(gfx::Rect(native_widget_delegate_->GetMaximumSize())).size(); - if (min_size_in_pixels_ == minimum_in_pixels && - max_size_in_pixels_ == maximum_in_pixels) - return; +void DesktopWindowTreeHostX11::OnXWindowDamageEvent( + const gfx::Rect& damage_rect_in_pixels) { + compositor()->ScheduleRedrawRect(damage_rect_in_pixels); +} + +void DesktopWindowTreeHostX11::OnXWindowKeyEvent(ui::KeyEvent* key_event) { + DispatchKeyEvent(key_event); +} - min_size_in_pixels_ = minimum_in_pixels; - max_size_in_pixels_ = maximum_in_pixels; +void DesktopWindowTreeHostX11::OnXWindowMouseEvent(ui::MouseEvent* mouseev) { + DispatchMouseEvent(mouseev); +} - XSizeHints hints; - hints.flags = 0; - long supplied_return; - XGetWMNormalHints(xdisplay_, xwindow_, &hints, &supplied_return); +void DesktopWindowTreeHostX11::OnXWindowTouchEvent( + ui::TouchEvent* touch_event) { + DispatchTouchEvent(touch_event); +} - if (minimum_in_pixels.IsEmpty()) { - hints.flags &= ~PMinSize; - } else { - hints.flags |= PMinSize; - hints.min_width = min_size_in_pixels_.width(); - hints.min_height = min_size_in_pixels_.height(); +void DesktopWindowTreeHostX11::OnXWindowScrollEvent( + ui::ScrollEvent* scroll_event) { + SendEventToSink(scroll_event); +} + +void DesktopWindowTreeHostX11::OnXWindowSelectionEvent(XEvent* xev) { + DCHECK(xev); + DCHECK(drag_drop_client_); + drag_drop_client_->OnSelectionNotify(xev->xselection); +} + +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); } +} - if (maximum_in_pixels.IsEmpty()) { - hints.flags &= ~PMaxSize; - } else { - hints.flags |= PMaxSize; - hints.max_width = max_size_in_pixels_.width(); - hints.max_height = max_size_in_pixels_.height(); +void DesktopWindowTreeHostX11::OnXWindowRawKeyEvent(XEvent* xev) { + switch (xev->type) { + case KeyPress: + if (!ShouldDiscardKeyEvent(xev)) { + ui::KeyEvent keydown_event(xev); + DispatchKeyEvent(&keydown_event); + } + break; + case KeyRelease: + + // There is no way to deactivate a window in X11 so ignore input if + // window is supposed to be 'inactive'. + if (!IsActive() && !HasCapture()) + break; + + if (!ShouldDiscardKeyEvent(xev)) { + ui::KeyEvent keyup_event(xev); + DispatchKeyEvent(&keyup_event); + } + break; + default: + NOTREACHED() << xev->type; + break; } +} - XSetWMNormalHints(xdisplay_, xwindow_, &hints); +void DesktopWindowTreeHostX11::OnXWindowChildCrossingEvent(XEvent* xev) { + DCHECK(xev); + ui::MouseEvent mouse_event(xev); + DispatchMouseEvent(&mouse_event); } -void DesktopWindowTreeHostX11::UpdateWMUserTime( - const ui::PlatformEvent& event) { - if (!IsActive()) - return; +void DesktopWindowTreeHostX11::OnXWindowSizeChanged( + const gfx::Size& size_in_pixels) { + OnHostResizedInPixels(size_in_pixels); + ResetWindowRegion(); +} + +void DesktopWindowTreeHostX11::OnXWindowCloseRequested() { + OnHostCloseRequested(); +} + +void DesktopWindowTreeHostX11::OnXWindowMoved(const gfx::Point& window_origin) { + OnHostMovedInPixels(window_origin); +} + +void DesktopWindowTreeHostX11::OnXWindowLostPointerGrab() { + dispatcher()->OnHostLostMouseGrab(); +} + +void DesktopWindowTreeHostX11::OnXWindowLostCapture() { + OnHostLostWindowCapture(); +} + +void DesktopWindowTreeHostX11::OnXWindowIsActiveChanged(bool active) { + if (active) { + // TODO(thomasanderson): Remove this window shuffling and use XWindowCache + // instead. + ::Window xwindow = x11_window_->window(); + open_windows().remove(xwindow); + open_windows().insert(open_windows().begin(), xwindow); + } + desktop_native_widget_aura_->HandleActivationChanged(active); + native_widget_delegate_->AsWidget()->GetRootView()->SchedulePaint(); +} + +gfx::Size DesktopWindowTreeHostX11::GetMinimumSizeForXWindow() { + return ToPixelRect(gfx::Rect(native_widget_delegate_->GetMinimumSize())) + .size(); +} + +gfx::Size DesktopWindowTreeHostX11::GetMaximumSizeForXWindow() { + return ToPixelRect(gfx::Rect(native_widget_delegate_->GetMaximumSize())) + .size(); +} + +//////////////////////////////////////////////////////////////////////////////// +// DesktopWindowTreeHostX11, private: + +void DesktopWindowTreeHostX11::InitX11Window(const Widget::InitParams& params) { + // Calculate initial bounds + gfx::Rect bounds_in_pixels = ToPixelRect(params.bounds); + gfx::Size adjusted_size = AdjustSize(bounds_in_pixels.size()); + bounds_in_pixels.set_size(adjusted_size); + + // Set the background color on startup to make the initial flickering + // happening between the XWindow is mapped and the first expose event + // is completely handled less annoying. If possible, we use the content + // window's background color, otherwise we fallback to white. + base::Optional<int> background_color; + const views::LinuxUI* linux_ui = views::LinuxUI::instance(); + if (linux_ui && content_window()) { + ui::NativeTheme::ColorId target_color; + switch (params.type) { + case Widget::InitParams::TYPE_BUBBLE: + target_color = ui::NativeTheme::kColorId_BubbleBackground; + break; + case Widget::InitParams::TYPE_TOOLTIP: + target_color = ui::NativeTheme::kColorId_TooltipBackground; + break; + default: + target_color = ui::NativeTheme::kColorId_WindowBackground; + break; + } + ui::NativeTheme* theme = linux_ui->GetNativeTheme(content_window()); + background_color = theme->GetSystemColor(target_color); + } + + // Create window configuration and initialize it + ui::XWindow::Configuration config = + ConvertInitParamsToX11WindowConfig(params); + config.bounds = bounds_in_pixels; + config.background_color = background_color; + config.prefer_dark_theme = linux_ui && linux_ui->PreferDarkTheme(); + config.icon = ViewsDelegate::GetInstance()->GetDefaultWindowIcon(); + x11_window_->Init(config); + + // Disable compositing on tooltips as a workaround for + // https://crbug.com/442111. + CreateCompositor(viz::FrameSinkId(), + params.force_software_compositing || + params.type == Widget::InitParams::TYPE_TOOLTIP); + + if (ui::IsSyncExtensionAvailable()) { + compositor_observer_ = std::make_unique<SwapWithNewSizeObserverHelper>( + compositor(), base::BindRepeating( + &DesktopWindowTreeHostX11::OnCompleteSwapWithNewSize, + base::Unretained(this))); + } + OnAcceleratedWidgetAvailable(); +} - ui::EventType type = ui::EventTypeFromNative(event); - if (type == ui::ET_MOUSE_PRESSED || - type == ui::ET_KEY_PRESSED || - type == ui::ET_TOUCH_PRESSED) { - unsigned long wm_user_time_ms = static_cast<unsigned long>( - (ui::EventTimeFromNative(event) - base::TimeTicks()).InMilliseconds()); - XChangeProperty(xdisplay_, xwindow_, gfx::GetAtom("_NET_WM_USER_TIME"), - XA_CARDINAL, 32, PropModeReplace, - reinterpret_cast<const unsigned char*>(&wm_user_time_ms), - 1); +gfx::Size DesktopWindowTreeHostX11::AdjustSize( + const gfx::Size& requested_size_in_pixels) { + std::vector<display::Display> displays = + display::Screen::GetScreen()->GetAllDisplays(); + // Compare against all monitor sizes. The window manager can move the window + // to whichever monitor it wants. + for (const auto& display : displays) { + if (requested_size_in_pixels == display.GetSizeInPixel()) { + return gfx::Size(requested_size_in_pixels.width() - 1, + requested_size_in_pixels.height() - 1); + } } + + // Do not request a 0x0 window size. It causes an XError. + gfx::Size size_in_pixels = requested_size_in_pixels; + size_in_pixels.SetToMax(gfx::Size(1, 1)); + return size_in_pixels; } void DesktopWindowTreeHostX11::SetUseNativeFrame(bool use_native_frame) { - use_native_frame_ = use_native_frame; - ui::SetUseOSWindowFrame(xwindow_, use_native_frame); + x11_window_->SetUseNativeFrame(use_native_frame); ResetWindowRegion(); } @@ -1934,71 +1389,23 @@ void DesktopWindowTreeHostX11::DispatchKeyEvent(ui::KeyEvent* event) { } void DesktopWindowTreeHostX11::ResetWindowRegion() { - // If a custom window shape was supplied then apply it. - if (custom_window_shape_) { - XShapeCombineRegion(xdisplay_, xwindow_, ShapeBounding, 0, 0, - window_shape_.get(), false); - return; - } - - window_shape_.reset(); - - if (!IsMaximized() && !IsFullscreen()) { + _XRegion* xregion = nullptr; + if (!x11_window_->use_custom_shape() && !IsMaximized() && !IsFullscreen()) { SkPath window_mask; Widget* widget = native_widget_delegate_->AsWidget(); if (widget->non_client_view()) { // Some frame views define a custom (non-rectangular) window mask. If // so, use it to define the window shape. If not, fall through. - widget->non_client_view()->GetWindowMask(bounds_in_pixels_.size(), + widget->non_client_view()->GetWindowMask(x11_window_->bounds().size(), &window_mask); if (window_mask.countPoints() > 0) { - window_shape_.reset(gfx::CreateRegionFromSkPath(window_mask)); - XShapeCombineRegion(xdisplay_, xwindow_, ShapeBounding, 0, 0, - window_shape_.get(), false); - return; + xregion = gfx::CreateRegionFromSkPath(window_mask); } } } - - // If we didn't set the shape for any reason, reset the shaping information. - // How this is done depends on the border style, due to quirks and bugs in - // various window managers. - if (ShouldUseNativeFrame()) { - // If the window has system borders, the mask must be set to null (not a - // rectangle), because several window managers (eg, KDE, XFCE, XMonad) will - // not put borders on a window with a custom shape. - XShapeCombineMask(xdisplay_, xwindow_, ShapeBounding, 0, 0, x11::None, - ShapeSet); - } else { - // Conversely, if the window does not have system borders, the mask must be - // manually set to a rectangle that covers the whole window (not null). This - // is due to a bug in KWin <= 4.11.5 (KDE bug #330573) where setting a null - // shape causes the hint to disable system borders to be ignored (resulting - // in a double border). - XRectangle r = {0, - 0, - static_cast<unsigned short>(bounds_in_pixels_.width()), - static_cast<unsigned short>(bounds_in_pixels_.height())}; - XShapeCombineRectangles( - xdisplay_, xwindow_, ShapeBounding, 0, 0, &r, 1, ShapeSet, YXBanded); - } + x11_window_->UpdateWindowRegion(xregion); } -void DesktopWindowTreeHostX11::SerializeImageRepresentation( - const gfx::ImageSkiaRep& rep, - std::vector<unsigned long>* data) { - int width = rep.GetWidth(); - data->push_back(width); - - int height = rep.GetHeight(); - data->push_back(height); - - const SkBitmap& bitmap = rep.GetBitmap(); - - for (int y = 0; y < height; ++y) - for (int x = 0; x < width; ++x) - data->push_back(bitmap.getColor(x, y)); -} std::list<XID>& DesktopWindowTreeHostX11::open_windows() { if (!open_windows_) @@ -2015,51 +1422,20 @@ void DesktopWindowTreeHostX11::MapWindow(ui::WindowShowState show_state) { NOTIMPLEMENTED_LOG_ONCE(); } - // Before we map the window, set size hints. Otherwise, some window managers - // will ignore toplevel XMoveWindow commands. - XSizeHints size_hints; - size_hints.flags = 0; - long supplied_return; - XGetWMNormalHints(xdisplay_, xwindow_, &size_hints, &supplied_return); - size_hints.flags |= PPosition; - size_hints.x = bounds_in_pixels_.x(); - size_hints.y = bounds_in_pixels_.y(); - XSetWMNormalHints(xdisplay_, xwindow_, &size_hints); - // If SHOW_STATE_INACTIVE, tell the window manager not to focus the window // when mapping. This is done by setting the _NET_WM_USER_TIME to 0. See e.g. // http://standards.freedesktop.org/wm-spec/latest/ar01s05.html - ignore_keyboard_input_ = show_state == ui::SHOW_STATE_INACTIVE; - unsigned long wm_user_time_ms = - ignore_keyboard_input_ - ? 0 - : ui::X11EventSource::GetInstance()->GetTimestamp(); - if (show_state == ui::SHOW_STATE_INACTIVE || wm_user_time_ms != 0) { - XChangeProperty(xdisplay_, xwindow_, gfx::GetAtom("_NET_WM_USER_TIME"), - XA_CARDINAL, 32, PropModeReplace, - reinterpret_cast<const unsigned char*>(&wm_user_time_ms), - 1); - } - - UpdateMinAndMaxSize(); + bool inactive = show_state == ui::SHOW_STATE_INACTIVE; - if (window_properties_.empty()) { - XDeleteProperty(xdisplay_, xwindow_, gfx::GetAtom("_NET_WM_STATE")); - } else { - ui::SetAtomArrayProperty(xwindow_, "_NET_WM_STATE", "ATOM", - std::vector<XAtom>(std::begin(window_properties_), - std::end(window_properties_))); - } - - XMapWindow(xdisplay_, xwindow_); - window_mapped_in_client_ = true; + x11_window_->Map(inactive); } void DesktopWindowTreeHostX11::SetWindowTransparency() { - compositor()->SetBackgroundColor(use_argb_visual_ ? SK_ColorTRANSPARENT - : SK_ColorWHITE); - window()->SetTransparent(use_argb_visual_); - content_window()->SetTransparent(use_argb_visual_); + bool has_alpha = x11_window_->has_alpha(); + compositor()->SetBackgroundColor(has_alpha ? SK_ColorTRANSPARENT + : SK_ColorWHITE); + window()->SetTransparent(has_alpha); + content_window()->SetTransparent(has_alpha); } void DesktopWindowTreeHostX11::Relayout() { @@ -2077,9 +1453,7 @@ void DesktopWindowTreeHostX11::Relayout() { bool DesktopWindowTreeHostX11::CanDispatchEvent( const ui::PlatformEvent& event) { - return event->xany.window == xwindow_ || - (event->type == GenericEvent && - static_cast<XIDeviceEvent*>(event->xcookie.data)->event == xwindow_); + return x11_window_->IsTargetedBy(*event); } uint32_t DesktopWindowTreeHostX11::DispatchEvent( @@ -2089,349 +1463,10 @@ uint32_t DesktopWindowTreeHostX11::DispatchEvent( TRACE_EVENT1("views", "DesktopWindowTreeHostX11::Dispatch", "event->type", event->type); - UpdateWMUserTime(event); - - // May want to factor CheckXEventForConsistency(xev); into a common location - // since it is called here. - switch (xev->type) { - case EnterNotify: - case LeaveNotify: { - OnCrossingEvent(xev->type == EnterNotify, xev->xcrossing.focus, - xev->xcrossing.mode, xev->xcrossing.detail); - - // Ignore EventNotify and LeaveNotify events from children of |xwindow_|. - // NativeViewGLSurfaceGLX adds a child to |xwindow_|. - if (xev->xcrossing.detail != NotifyInferior) { - ui::MouseEvent mouse_event(xev); - DispatchMouseEvent(&mouse_event); - } - break; - } - case Expose: { - gfx::Rect damage_rect_in_pixels(xev->xexpose.x, xev->xexpose.y, - xev->xexpose.width, xev->xexpose.height); - compositor()->ScheduleRedrawRect(damage_rect_in_pixels); - break; - } - case KeyPress: { - if (ui::AtkUtilAuraLinux::HandleKeyEvent(xev) != - ui::DiscardAtkKeyEvent::Discard) { - ui::KeyEvent keydown_event(xev); - DispatchKeyEvent(&keydown_event); - } - break; - } - case KeyRelease: { - // There is no way to deactivate a window in X11 so ignore input if - // window is supposed to be 'inactive'. - if (!IsActive() && !HasCapture()) - break; - - if (ui::AtkUtilAuraLinux::HandleKeyEvent(xev) != - ui::DiscardAtkKeyEvent::Discard) { - ui::KeyEvent key_event(xev); - DispatchKeyEvent(&key_event); - } - break; - } - case ButtonPress: - case ButtonRelease: { - ui::EventType event_type = ui::EventTypeFromNative(xev); - switch (event_type) { - case ui::ET_MOUSEWHEEL: { - ui::MouseWheelEvent mouseev(xev); - DispatchMouseEvent(&mouseev); - break; - } - case ui::ET_MOUSE_PRESSED: - case ui::ET_MOUSE_RELEASED: { - ui::MouseEvent mouseev(xev); - DispatchMouseEvent(&mouseev); - break; - } - case ui::ET_UNKNOWN: - // No event is created for X11-release events for mouse-wheel buttons. - break; - default: - NOTREACHED() << event_type; - } - break; - } - case x11::FocusIn: - case x11::FocusOut: - OnFocusEvent(xev->type == x11::FocusIn, event->xfocus.mode, - event->xfocus.detail); - break; - case ConfigureNotify: { - DCHECK_EQ(xwindow_, xev->xconfigure.window); - DCHECK_EQ(xwindow_, xev->xconfigure.event); - - if (pending_counter_value_) { - DCHECK(!configure_counter_value_); - configure_counter_value_ = pending_counter_value_; - configure_counter_value_is_extended_ = - pending_counter_value_is_extended_; - pending_counter_value_is_extended_ = 0; - pending_counter_value_ = 0; - } - - // It's possible that the X window may be resized by some other means than - // from within aura (e.g. the X window manager can change the size). Make - // sure the root window size is maintained properly. - int translated_x_in_pixels = xev->xconfigure.x; - int translated_y_in_pixels = xev->xconfigure.y; - if (!xev->xconfigure.send_event && !xev->xconfigure.override_redirect) { - Window unused; - XTranslateCoordinates(xdisplay_, xwindow_, x_root_window_, 0, 0, - &translated_x_in_pixels, &translated_y_in_pixels, - &unused); - } - gfx::Rect bounds_in_pixels(translated_x_in_pixels, translated_y_in_pixels, - xev->xconfigure.width, xev->xconfigure.height); - bool size_changed = bounds_in_pixels_.size() != bounds_in_pixels.size(); - bool origin_changed = - bounds_in_pixels_.origin() != bounds_in_pixels.origin(); - previous_bounds_in_pixels_ = bounds_in_pixels_; - bounds_in_pixels_ = bounds_in_pixels; - - if (origin_changed) - OnHostMovedInPixels(bounds_in_pixels_.origin()); - - if (size_changed) - RestartDelayedResizeTask(); - break; - } - case GenericEvent: { - ui::TouchFactory* factory = ui::TouchFactory::GetInstance(); - if (!factory->ShouldProcessXI2Event(xev)) - break; - - XIEnterEvent* enter_event = static_cast<XIEnterEvent*>(xev->xcookie.data); - switch (static_cast<XIEvent*>(xev->xcookie.data)->evtype) { - case XI_Enter: - case XI_Leave: - OnCrossingEvent(enter_event->evtype == XI_Enter, enter_event->focus, - XI2ModeToXMode(enter_event->mode), - enter_event->detail); - return ui::POST_DISPATCH_STOP_PROPAGATION; - case XI_FocusIn: - case XI_FocusOut: - OnFocusEvent(enter_event->evtype == XI_FocusIn, - XI2ModeToXMode(enter_event->mode), enter_event->detail); - return ui::POST_DISPATCH_STOP_PROPAGATION; - default: - break; - } - - ui::EventType type = ui::EventTypeFromNative(xev); - XEvent last_event; - int num_coalesced = 0; - - switch (type) { - case ui::ET_TOUCH_MOVED: - num_coalesced = ui::CoalescePendingMotionEvents(xev, &last_event); - if (num_coalesced > 0) - xev = &last_event; - FALLTHROUGH; - case ui::ET_TOUCH_PRESSED: - case ui::ET_TOUCH_RELEASED: { - ui::TouchEvent touchev(xev); - DispatchTouchEvent(&touchev); - break; - } - case ui::ET_MOUSE_MOVED: - case ui::ET_MOUSE_DRAGGED: - case ui::ET_MOUSE_PRESSED: - case ui::ET_MOUSE_RELEASED: - case ui::ET_MOUSE_ENTERED: - case ui::ET_MOUSE_EXITED: { - if (type == ui::ET_MOUSE_MOVED || type == ui::ET_MOUSE_DRAGGED) { - // If this is a motion event, we want to coalesce all pending motion - // events that are at the top of the queue. - num_coalesced = ui::CoalescePendingMotionEvents(xev, &last_event); - if (num_coalesced > 0) - xev = &last_event; - } - ui::MouseEvent mouseev(xev); - // If after CoalescePendingMotionEvents the type of xev is resolved to - // UNKNOWN, don't dispatch the event. - // TODO(804418): investigate why ColescePendingMotionEvents can - // include mouse wheel events as well. Investigation showed that - // events on Linux are checked with cmt-device path, and can include - // DT_CMT_SCROLL_ data. See more discussion in - // https://crrev.com/c/853953 - if (mouseev.type() != ui::ET_UNKNOWN) - DispatchMouseEvent(&mouseev); - break; - } - case ui::ET_MOUSEWHEEL: { - ui::MouseWheelEvent mouseev(xev); - DispatchMouseEvent(&mouseev); - break; - } - case ui::ET_SCROLL_FLING_START: - case ui::ET_SCROLL_FLING_CANCEL: - case ui::ET_SCROLL: { - ui::ScrollEvent scrollev(xev); - // We need to filter zero scroll offset here. Because - // MouseWheelEventQueue assumes we'll never get a zero scroll offset - // event and we need delta to determine which element to scroll on - // phaseBegan. - if (scrollev.x_offset() != 0.0 || scrollev.y_offset() != 0.0) - SendEventToSink(&scrollev); - break; - } - case ui::ET_KEY_PRESSED: - case ui::ET_KEY_RELEASED: { - ui::KeyEvent key_event(xev); - DispatchKeyEvent(&key_event); - break; - } - case ui::ET_UNKNOWN: - break; - default: - NOTREACHED(); - } - - // If we coalesced an event we need to free its cookie. - if (num_coalesced > 0) - XFreeEventData(xev->xgeneric.display, &last_event.xcookie); - break; - } - case MapNotify: { - window_mapped_in_server_ = true; - - for (DesktopWindowTreeHostObserverX11& observer : observer_list_) - observer.OnWindowMapped(xwindow_); - - // Some WMs only respect maximize hints after the window has been mapped. - // Check whether we need to re-do a maximization. - if (should_maximize_after_map_) { - Maximize(); - should_maximize_after_map_ = false; - } - - break; - } - case UnmapNotify: { - window_mapped_in_server_ = false; - has_pointer_ = false; - has_pointer_grab_ = false; - has_pointer_focus_ = false; - has_window_focus_ = false; - for (DesktopWindowTreeHostObserverX11& observer : observer_list_) - observer.OnWindowUnmapped(xwindow_); - break; - } - case ClientMessage: { - Atom message_type = xev->xclient.message_type; - if (message_type == gfx::GetAtom("WM_PROTOCOLS")) { - Atom protocol = static_cast<Atom>(xev->xclient.data.l[0]); - if (protocol == gfx::GetAtom("WM_DELETE_WINDOW")) { - // We have received a close message from the window manager. - OnHostCloseRequested(); - } else if (protocol == gfx::GetAtom("_NET_WM_PING")) { - XEvent reply_event = *xev; - reply_event.xclient.window = x_root_window_; - - XSendEvent(xdisplay_, reply_event.xclient.window, x11::False, - SubstructureRedirectMask | SubstructureNotifyMask, - &reply_event); - } else if (protocol == gfx::GetAtom("_NET_WM_SYNC_REQUEST")) { - pending_counter_value_ = - xev->xclient.data.l[2] + - (static_cast<int64_t>(xev->xclient.data.l[3]) << 32); - pending_counter_value_is_extended_ = xev->xclient.data.l[4] != 0; - } - } else 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); - } - break; - } - case MappingNotify: { - switch (xev->xmapping.request) { - case MappingModifier: - case MappingKeyboard: - XRefreshKeyboardMapping(&xev->xmapping); - break; - case MappingPointer: - ui::DeviceDataManagerX11::GetInstance()->UpdateButtonMap(); - break; - default: - NOTIMPLEMENTED() << " Unknown request: " << xev->xmapping.request; - break; - } - break; - } - case MotionNotify: { - // Discard all but the most recent motion event that targets the same - // window with unchanged state. - XEvent last_event; - while (XPending(xev->xany.display)) { - XEvent next_event; - XPeekEvent(xev->xany.display, &next_event); - if (next_event.type == MotionNotify && - next_event.xmotion.window == xev->xmotion.window && - next_event.xmotion.subwindow == xev->xmotion.subwindow && - next_event.xmotion.state == xev->xmotion.state) { - XNextEvent(xev->xany.display, &last_event); - xev = &last_event; - } else { - break; - } - } - - ui::MouseEvent mouseev(xev); - DispatchMouseEvent(&mouseev); - break; - } - case PropertyNotify: { - XAtom changed_atom = xev->xproperty.atom; - if (changed_atom == gfx::GetAtom("_NET_WM_STATE")) { - OnWMStateUpdated(); - } else if (changed_atom == gfx::GetAtom("_NET_FRAME_EXTENTS")) { - OnFrameExtentsUpdated(); - } else if (changed_atom == gfx::GetAtom("_NET_WM_DESKTOP")) { - base::Optional<int> old_workspace = workspace_; - UpdateWorkspace(); - if (workspace_ != old_workspace) - OnHostWorkspaceChanged(); - } - break; - } - case SelectionNotify: { - drag_drop_client_->OnSelectionNotify(xev->xselection); - break; - } - } + x11_window_->ProcessEvent(xev); return ui::POST_DISPATCH_STOP_PROPAGATION; } -void DesktopWindowTreeHostX11::DelayedResize(const gfx::Size& size_in_pixels) { - if (configure_counter_value_is_extended_ && - (current_counter_value_ % 2) == 0) { - // Increase the |extended_update_counter_|, so the compositor will know we - // are not frozen and re-enable _NET_WM_SYNC_REQUEST, if it was disabled. - // Increase the |extended_update_counter_| to an odd number will not trigger - // a new resize. - SyncSetCounter(xdisplay_, extended_update_counter_, - ++current_counter_value_); - } - OnHostResizedInPixels(size_in_pixels); - ResetWindowRegion(); - delayed_resize_task_.Cancel(); -} - void DesktopWindowTreeHostX11::DelayedChangeFrameType(Widget::FrameType type) { SetUseNativeFrame(type == Widget::FRAME_TYPE_FORCE_NATIVE); // Replace the frame and layout the contents. Even though we don't have a @@ -2474,56 +1509,13 @@ void DesktopWindowTreeHostX11::EnableEventListening() { targeter_for_modal_.reset(); } -void DesktopWindowTreeHostX11::RestartDelayedResizeTask() { - if (update_counter_ == x11::None || configure_counter_value_ == 0) { - // WM doesn't support _NET_WM_SYNC_REQUEST. - // Or we are too slow, so _NET_WM_SYNC_REQUEST is disabled by the - // compositor. - delayed_resize_task_.Reset(base::BindOnce( - &DesktopWindowTreeHostX11::DelayedResize, - close_widget_factory_.GetWeakPtr(), bounds_in_pixels_.size())); - base::ThreadTaskRunnerHandle::Get()->PostTask( - FROM_HERE, delayed_resize_task_.callback()); - return; - } - - if (configure_counter_value_is_extended_) { - current_counter_value_ = configure_counter_value_; - configure_counter_value_ = 0; - // Make sure the counter is even number. - if ((current_counter_value_ % 2) == 1) - ++current_counter_value_; - } - - // If _NET_WM_SYNC_REQUEST is used to synchronize with compositor during - // resizing, the compositor will not resize the window, until last resize is - // handled, so we don't need accumulate resize events. - DelayedResize(bounds_in_pixels_.size()); -} - aura::Window* DesktopWindowTreeHostX11::content_window() { return desktop_native_widget_aura_->content_window(); } void DesktopWindowTreeHostX11::OnCompleteSwapWithNewSize( const gfx::Size& size) { - if (configure_counter_value_is_extended_) { - if ((current_counter_value_ % 2) == 1) { - // An increase 3 means that the frame was not drawn as fast as possible. - // This can trigger different handling from the compositor. - // Setting an even number to |extended_update_counter_| will trigger a - // new resize. - current_counter_value_ += 3; - SyncSetCounter(xdisplay_, extended_update_counter_, - current_counter_value_); - } - return; - } - - if (configure_counter_value_ != 0) { - SyncSetCounter(xdisplay_, update_counter_, configure_counter_value_); - configure_counter_value_ = 0; - } + x11_window_->NotifySwapAfterResize(); } base::flat_map<std::string, std::string> @@ -2533,6 +1525,11 @@ DesktopWindowTreeHostX11::GetKeyboardLayoutMap() { return {}; } +void DesktopWindowTreeHostX11::SetVisualId(VisualID visual_id) { + DCHECK_EQ(x11_window_->window(), x11::None); + x11_window_->set_visual_id(visual_id); +} + //////////////////////////////////////////////////////////////////////////////// // DesktopWindowTreeHost, public: diff --git a/chromium/ui/views/widget/desktop_aura/desktop_window_tree_host_x11.h b/chromium/ui/views/widget/desktop_aura/desktop_window_tree_host_x11.h index 92f49c677d6..e32726ad461 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 @@ -8,32 +8,38 @@ #include <stddef.h> #include <stdint.h> -#include "base/cancelable_callback.h" +#include <list> +#include <memory> +#include <set> +#include <string> +#include <vector> + #include "base/containers/flat_set.h" #include "base/macros.h" #include "base/memory/weak_ptr.h" #include "base/observer_list.h" #include "ui/aura/scoped_window_targeter.h" #include "ui/aura/window_tree_host.h" -#include "ui/base/cursor/cursor_loader_x11.h" +#include "ui/base/x/x11_window.h" #include "ui/events/platform/platform_event_dispatcher.h" -#include "ui/gfx/geometry/insets.h" #include "ui/gfx/geometry/rect.h" #include "ui/gfx/geometry/size.h" -#include "ui/gfx/x/x11.h" +#include "ui/gfx/x/x11_types.h" #include "ui/views/views_export.h" #include "ui/views/widget/desktop_aura/desktop_window_tree_host.h" namespace gfx { class ImageSkia; -class ImageSkiaRep; } namespace ui { enum class DomCode; class EventHandler; class KeyboardHook; -class XScopedEventSelector; +class KeyEvent; +class MouseEvent; +class TouchEvent; +class ScrollEvent; } namespace views { @@ -45,7 +51,8 @@ class X11DesktopWindowMoveClient; class VIEWS_EXPORT DesktopWindowTreeHostX11 : public DesktopWindowTreeHost, public aura::WindowTreeHost, - public ui::PlatformEventDispatcher { + public ui::PlatformEventDispatcher, + public ui::XWindow::Delegate { public: DesktopWindowTreeHostX11( internal::NativeWidgetDelegate* native_widget_delegate, @@ -91,6 +98,10 @@ class VIEWS_EXPORT DesktopWindowTreeHostX11 // Returns a map of KeyboardEvent code to KeyboardEvent key values. base::flat_map<std::string, std::string> GetKeyboardLayoutMap() override; + // This must be called before the window is created, because the visual cannot + // be changed after. + void SetVisualId(VisualID visual_id); + protected: // Overridden from DesktopWindowTreeHost: void Init(const Widget::InitParams& params) override; @@ -127,8 +138,8 @@ class VIEWS_EXPORT DesktopWindowTreeHostX11 bool IsMaximized() const override; bool IsMinimized() const override; bool HasCapture() const override; - void SetAlwaysOnTop(bool always_on_top) override; - bool IsAlwaysOnTop() const override; + void SetZOrderLevel(ui::ZOrderLevel order) override; + ui::ZOrderLevel GetZOrderLevel() const override; void SetVisibleOnAllWorkspaces(bool always_visible) override; bool IsVisibleOnAllWorkspaces() const override; bool SetWindowTitle(const base::string16& title) override; @@ -182,6 +193,30 @@ class VIEWS_EXPORT DesktopWindowTreeHostX11 void OnDisplayMetricsChanged(const display::Display& display, uint32_t changed_metrics) override; + // Overridden from ui::XWindow::Delegate + void OnXWindowCreated() override; + void OnXWindowMapped() override; + void OnXWindowUnmapped() override; + void OnXWindowStateChanged() override; + void OnXWindowWorkspaceChanged() override; + void OnXWindowKeyEvent(ui::KeyEvent* key_event) override; + void OnXWindowMouseEvent(ui::MouseEvent* mouseev) override; + void OnXWindowTouchEvent(ui::TouchEvent* touch_event) override; + void OnXWindowScrollEvent(ui::ScrollEvent* scroll_event) override; + void OnXWindowSelectionEvent(XEvent* xev) override; + void OnXWindowDragDropEvent(XEvent* xev) override; + void OnXWindowChildCrossingEvent(XEvent* xev) override; + void OnXWindowRawKeyEvent(XEvent* xev) override; + void OnXWindowSizeChanged(const gfx::Size& size) override; + void OnXWindowCloseRequested() override; + void OnXWindowMoved(const gfx::Point& window_origin) override; + void OnXWindowDamageEvent(const gfx::Rect& damage_rect) override; + void OnXWindowLostPointerGrab() override; + void OnXWindowLostCapture() override; + void OnXWindowIsActiveChanged(bool active) override; + gfx::Size GetMinimumSizeForXWindow() override; + gfx::Size GetMaximumSizeForXWindow() override; + private: friend class DesktopWindowTreeHostX11HighDPITest; @@ -198,53 +233,6 @@ class VIEWS_EXPORT DesktopWindowTreeHostX11 // fullscreen. gfx::Size AdjustSize(const gfx::Size& requested_size); - // If mapped, sends a message to the window manager to enable or disable the - // states |state1| and |state2|. Otherwise, the states will be enabled or - // disabled on the next map. It's the caller's responsibility to make sure - // atoms are set and unset in the appropriate pairs. For example, if a caller - // sets (_NET_WM_STATE_MAXIMIZED_VERT, _NET_WM_STATE_MAXIMIZED_HORZ), it would - // be invalid to unset the maximized state by making two calls like - // (_NET_WM_STATE_MAXIMIZED_VERT, x11::None), (_NET_WM_STATE_MAXIMIZED_HORZ, - // x11::None). - void SetWMSpecState(bool enabled, XAtom state1, XAtom state2); - - // Called when |xwindow_|'s _NET_WM_STATE property is updated. - void OnWMStateUpdated(); - - // Updates |window_properties_| with |new_window_properties|. - void UpdateWindowProperties( - const base::flat_set<XAtom>& new_window_properties); - - // Called when |xwindow_|'s _NET_FRAME_EXTENTS property is updated. - void OnFrameExtentsUpdated(); - - // Record the activation state. - void BeforeActivationStateChanged(); - - // Handle the state change since BeforeActivationStateChanged(). - void AfterActivationStateChanged(); - - // Called on an XEnterWindowEvent, XLeaveWindowEvent, XIEnterEvent, or an - // XILeaveEvent. - void OnCrossingEvent(bool enter, - bool focus_in_window_or_ancestor, - int mode, - int detail); - - // Called on an XFocusInEvent, XFocusOutEvent, XIFocusInEvent, or an - // XIFocusOutEvent. - void OnFocusEvent(bool focus_in, int mode, int detail); - - // Makes a round trip to the X server to get the enclosing workspace for this - // window. - void UpdateWorkspace(); - - // Updates |xwindow_|'s minimum and maximum size. - void UpdateMinAndMaxSize(); - - // Updates |xwindow_|'s _NET_WM_USER_TIME if |xwindow_| is active. - void UpdateWMUserTime(const ui::PlatformEvent& event); - // Sets whether the window's borders are provided by the window manager. void SetUseNativeFrame(bool use_native_frame); @@ -264,10 +252,6 @@ class VIEWS_EXPORT DesktopWindowTreeHostX11 // Resets the window region for the current widget bounds if necessary. void ResetWindowRegion(); - // Serializes an image to the format used by _NET_WM_ICON. - void SerializeImageRepresentation(const gfx::ImageSkiaRep& rep, - std::vector<unsigned long>* data); - // See comment for variable open_windows_. static std::list<XID>& open_windows(); @@ -283,7 +267,6 @@ class VIEWS_EXPORT DesktopWindowTreeHostX11 bool CanDispatchEvent(const ui::PlatformEvent& event) override; uint32_t DispatchEvent(const ui::PlatformEvent& event) override; - void DelayedResize(const gfx::Size& size_in_pixels); void DelayedChangeFrameType(Widget::FrameType new_type); gfx::Rect ToDIPRect(const gfx::Rect& rect_in_pixels) const; @@ -292,10 +275,6 @@ class VIEWS_EXPORT DesktopWindowTreeHostX11 // Enables event listening after closing |dialog|. void EnableEventListening(); - // Removes |delayed_resize_task_| from the task queue (if it's in - // the queue) and adds it back at the end of the queue. - void RestartDelayedResizeTask(); - // Set visibility and fire OnNativeWidgetVisibilityChanged() if it changed. void SetVisible(bool visible); @@ -305,65 +284,15 @@ class VIEWS_EXPORT DesktopWindowTreeHostX11 // Callback for a swapbuffer after resize. void OnCompleteSwapWithNewSize(const gfx::Size& size); - // X11 things - // The display and the native X window hosting the root window. - XDisplay* xdisplay_; - ::Window xwindow_ = 0; - - // Events selected on |xwindow_|. - std::unique_ptr<ui::XScopedEventSelector> xwindow_events_; - - // The native root window. - ::Window x_root_window_; - - // Whether the window is mapped with respect to the X server. - bool window_mapped_in_server_ = false; - - // Whether the window is visible with respect to Aura. - bool window_mapped_in_client_ = false; - - // The bounds of |xwindow_|. - gfx::Rect bounds_in_pixels_; - - // Whenever the bounds are set, we keep the previous set of bounds around so - // we can have a better chance of getting the real - // |restored_bounds_in_pixels_|. Window managers tend to send a Configure - // message with the maximized bounds, and then set the window maximized - // property. (We don't rely on this for when we request that the window be - // maximized, only when we detect that some other process has requested that - // we become the maximized window.) - gfx::Rect previous_bounds_in_pixels_; - // The bounds of our window before we were maximized. gfx::Rect restored_bounds_in_pixels_; - // |xwindow_|'s minimum size. - gfx::Size min_size_in_pixels_; - - // |xwindow_|'s maximum size. - gfx::Size max_size_in_pixels_; - - // The workspace containing |xwindow_|. This will be base::nullopt when - // _NET_WM_DESKTOP is unset. - base::Optional<int> workspace_; - - // The window manager state bits. - base::flat_set<XAtom> window_properties_; - // Whether |xwindow_| was requested to be fullscreen via SetFullscreen(). bool is_fullscreen_ = false; - // True if the window should stay on top of most other windows. - bool is_always_on_top_ = false; - - // True if the window has title-bar / borders provided by the window manager. - bool use_native_frame_ = false; - - // True if a Maximize() call should be done after mapping the window. - bool should_maximize_after_map_ = false; - - // Whether we used an ARGB visual for our window. - bool use_argb_visual_ = false; + // The z-order level of the window; the window exhibits "always on top" + // behavior if > 0. + ui::ZOrderLevel z_order_ = ui::ZOrderLevel::kNormal; DesktopDragDropClientAuraX11* drag_drop_client_ = nullptr; @@ -384,16 +313,6 @@ class VIEWS_EXPORT DesktopWindowTreeHostX11 base::ObserverList<DesktopWindowTreeHostObserverX11>::Unchecked observer_list_; - // The window shape if the window is non-rectangular. - gfx::XScopedPtr<_XRegion, gfx::XObjectDeleter<_XRegion, int, XDestroyRegion>> - window_shape_; - - // Whether |window_shape_| was set via SetShape(). - bool custom_window_shape_ = false; - - // The size of the window manager provided borders (if any). - gfx::Insets native_window_frame_borders_in_pixels_; - // The current DesktopWindowTreeHostX11 which has capture. Set synchronously // when capture is requested via SetCapture(). static DesktopWindowTreeHostX11* g_current_capture; @@ -402,76 +321,21 @@ class VIEWS_EXPORT DesktopWindowTreeHostX11 // destroyed. static std::list<XID>* open_windows_; - base::string16 window_title_; - - // Whether we currently are flashing our frame. This feature is implemented - // by setting the urgency hint with the window manager, which can draw - // attention to the window or completely ignore the hint. We stop flashing - // the frame when |xwindow_| gains focus or handles a mouse button event. - bool urgency_hint_set_ = false; - - // Does |xwindow_| have the pointer grab (XI2 or normal)? - bool has_pointer_grab_ = false; - - // Is this window able to receive focus? - bool activatable_ = true; - - // Was this window initialized with the override_redirect window attribute? - bool override_redirect_ = false; - - // The focus-tracking state variables are as described in - // gtk/docs/focus_tracking.txt - // - // |xwindow_| is active iff: - // (|has_window_focus_| || |has_pointer_focus_|) && - // !|ignore_keyboard_input_| - - // Is the pointer in |xwindow_| or one of its children? - bool has_pointer_ = false; - - // Is |xwindow_| or one of its children focused? - bool has_window_focus_ = false; - - // (An ancestor window or the PointerRoot is focused) && |has_pointer_|. - // |has_pointer_focus_| == true is the odd case where we will receive keyboard - // input when |has_window_focus_| == false. |has_window_focus_| and - // |has_pointer_focus_| are mutually exclusive. - bool has_pointer_focus_ = false; - - // X11 does not support defocusing windows; you can only focus a different - // window. If we would like to be defocused, we just ignore keyboard input we - // no longer care about. - bool ignore_keyboard_input_ = false; - - // Used for tracking activation state in {Before|After}ActivationStateChanged. - bool was_active_ = false; - bool had_pointer_ = false; - bool had_pointer_grab_ = false; - bool had_window_focus_ = false; - // Cached value for SetVisible. Not the same as the IsVisible public API. bool is_compositor_set_visible_ = false; // Captures system key events when keyboard lock is requested. std::unique_ptr<ui::KeyboardHook> keyboard_hook_; - base::CancelableOnceCallback<void()> delayed_resize_task_; - std::unique_ptr<aura::ScopedWindowTargeter> targeter_for_modal_; uint32_t modal_dialog_counter_ = 0; - // Used for synchronizing between |xwindow_| between desktop compositor during - // resizing. - XID update_counter_ = x11::None; - XID extended_update_counter_ = x11::None; - int64_t pending_counter_value_ = 0; - int64_t configure_counter_value_ = 0; - int64_t current_counter_value_ = 0; - bool pending_counter_value_is_extended_ = false; - bool configure_counter_value_is_extended_ = false; std::unique_ptr<CompositorObserver> compositor_observer_; + std::unique_ptr<ui::XWindow> x11_window_; + + // The display and the native X window hosting the root window. base::WeakPtrFactory<DesktopWindowTreeHostX11> close_widget_factory_{this}; base::WeakPtrFactory<DesktopWindowTreeHostX11> weak_factory_{this}; 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 2578d410387..702502cff05 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 @@ -52,7 +52,7 @@ class WMStateWaiter : public X11PropertyChangeWaiter { bool ShouldKeepOnWaiting(const ui::PlatformEvent& event) override { std::vector<Atom> hints; if (ui::GetAtomArrayProperty(xwindow(), "_NET_WM_STATE", &hints)) - return base::ContainsValue(hints, gfx::GetAtom(hint_)) != wait_till_set_; + return base::Contains(hints, gfx::GetAtom(hint_)) != wait_till_set_; return true; } diff --git a/chromium/ui/views/widget/desktop_aura/x11_desktop_handler.cc b/chromium/ui/views/widget/desktop_aura/x11_desktop_handler.cc index 144f0ba0c1c..cfe62d62144 100644 --- a/chromium/ui/views/widget/desktop_aura/x11_desktop_handler.cc +++ b/chromium/ui/views/widget/desktop_aura/x11_desktop_handler.cc @@ -10,8 +10,9 @@ #include "ui/aura/env.h" #include "ui/aura/window_event_dispatcher.h" #include "ui/base/x/x11_menu_list.h" -#include "ui/base/x/x11_window_event_manager.h" +#include "ui/base/x/x11_util.h" #include "ui/events/platform/platform_event_source.h" +#include "ui/events/x/x11_window_event_manager.h" #include "ui/gfx/x/x11.h" #include "ui/gfx/x/x11_atom_cache.h" #include "ui/gfx/x/x11_error_tracker.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 784577da76d..100a1e856aa 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 @@ -41,8 +41,7 @@ class MinimizeWaiter : public X11PropertyChangeWaiter { bool ShouldKeepOnWaiting(const ui::PlatformEvent& event) override { std::vector<Atom> wm_states; if (ui::GetAtomArrayProperty(xwindow(), "_NET_WM_STATE", &wm_states)) { - return !base::ContainsValue(wm_states, - gfx::GetAtom("_NET_WM_STATE_HIDDEN")); + return !base::Contains(wm_states, gfx::GetAtom("_NET_WM_STATE_HIDDEN")); } return true; } @@ -79,7 +78,7 @@ class StackingClientListWaiter : public X11PropertyChangeWaiter { ui::GetXWindowStack(ui::GetX11RootWindow(), &stack); return !std::all_of( expected_windows_.cbegin(), expected_windows_.cend(), - [&stack](XID window) { return base::ContainsValue(stack, window); }); + [&stack](XID window) { return base::Contains(stack, window); }); } std::vector<XID> expected_windows_; diff --git a/chromium/ui/views/widget/desktop_aura/x11_whole_screen_move_loop.cc b/chromium/ui/views/widget/desktop_aura/x11_whole_screen_move_loop.cc index eaf642aa5ba..cbeea44f3da 100644 --- a/chromium/ui/views/widget/desktop_aura/x11_whole_screen_move_loop.cc +++ b/chromium/ui/views/widget/desktop_aura/x11_whole_screen_move_loop.cc @@ -23,13 +23,13 @@ #include "ui/aura/window_tree_host.h" #include "ui/base/x/x11_pointer_grab.h" #include "ui/base/x/x11_util.h" -#include "ui/base/x/x11_window_event_manager.h" #include "ui/events/event.h" #include "ui/events/event_utils.h" #include "ui/events/keycodes/keyboard_code_conversion_x.h" #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/x11_window_event_manager.h" #include "ui/gfx/x/x11.h" namespace views { @@ -53,8 +53,7 @@ X11WholeScreenMoveLoop::X11WholeScreenMoveLoop(X11MoveLoopDelegate* delegate) should_reset_mouse_flags_(false), grab_input_window_(x11::None), grabbed_pointer_(false), - canceled_(false), - weak_factory_(this) {} + canceled_(false) {} X11WholeScreenMoveLoop::~X11WholeScreenMoveLoop() = default; diff --git a/chromium/ui/views/widget/desktop_aura/x11_whole_screen_move_loop.h b/chromium/ui/views/widget/desktop_aura/x11_whole_screen_move_loop.h index 62631fa24e2..529cb62bac6 100644 --- a/chromium/ui/views/widget/desktop_aura/x11_whole_screen_move_loop.h +++ b/chromium/ui/views/widget/desktop_aura/x11_whole_screen_move_loop.h @@ -91,7 +91,7 @@ class X11WholeScreenMoveLoop : public X11MoveLoop, bool canceled_; std::unique_ptr<ui::MouseEvent> last_motion_in_screen_; - base::WeakPtrFactory<X11WholeScreenMoveLoop> weak_factory_; + base::WeakPtrFactory<X11WholeScreenMoveLoop> weak_factory_{this}; DISALLOW_COPY_AND_ASSIGN(X11WholeScreenMoveLoop); }; diff --git a/chromium/ui/views/widget/native_widget_aura.cc b/chromium/ui/views/widget/native_widget_aura.cc index 6c45f99e96e..e160ccebd32 100644 --- a/chromium/ui/views/widget/native_widget_aura.cc +++ b/chromium/ui/views/widget/native_widget_aura.cc @@ -34,7 +34,6 @@ #include "ui/display/screen.h" #include "ui/events/event.h" #include "ui/gfx/canvas.h" -#include "ui/gfx/font_list.h" #include "ui/native_theme/native_theme_aura.h" #include "ui/views/drag_utils.h" #include "ui/views/views_delegate.h" @@ -57,7 +56,6 @@ #if defined(OS_WIN) #include "base/win/scoped_gdi_object.h" -#include "ui/gfx/system_fonts_win.h" #include "ui/views/widget/desktop_aura/desktop_window_tree_host_win.h" #endif @@ -82,7 +80,7 @@ DEFINE_UI_CLASS_PROPERTY_KEY(internal::NativeWidgetPrivate*, nullptr) void SetRestoreBounds(aura::Window* window, const gfx::Rect& bounds) { - window->SetProperty(aura::client::kRestoreBoundsKey, new gfx::Rect(bounds)); + window->SetProperty(aura::client::kRestoreBoundsKey, bounds); } void SetIcon(aura::Window* window, @@ -91,7 +89,7 @@ void SetIcon(aura::Window* window, if (value.isNull()) window->ClearProperty(key); else - window->SetProperty(key, new gfx::ImageSkia(value)); + window->SetProperty(key, value); } } // namespace @@ -104,8 +102,7 @@ NativeWidgetAura::NativeWidgetAura(internal::NativeWidgetDelegate* delegate) window_(new aura::Window(this, aura::client::WINDOW_TYPE_UNKNOWN)), ownership_(Widget::InitParams::NATIVE_WIDGET_OWNS_WIDGET), destroying_(false), - cursor_(gfx::kNullCursor), - close_widget_factory_(this) { + cursor_(gfx::kNullCursor) { aura::client::SetFocusChangeObserver(window_, this); wm::SetActivationChangeObserver(window_, this); } @@ -207,8 +204,8 @@ void NativeWidgetAura::InitNativeWidget(const Widget::InitParams& params) { ->set_parent_controls_visibility(true); } } - // SetAlwaysOnTop before SetParent so that always-on-top container is used. - SetAlwaysOnTop(params.keep_on_top); + // SetZOrderLevel before SetParent so that always-on-top container is used. + SetZOrderLevel(params.EffectiveZOrderLevel()); // Make sure we have a real |window_bounds|. aura::Window* parent_or_context = parent ? parent : context; @@ -365,7 +362,7 @@ void NativeWidgetAura::CenterWindow(const gfx::Size& size) { if (!window_) return; - window_->SetProperty(aura::client::kPreferredSize, new gfx::Size(size)); + window_->SetProperty(aura::client::kPreferredSize, size); gfx::Rect parent_bounds(window_->parent()->GetBoundsInRootWindow()); // When centering window, we take the intersection of the host and @@ -613,13 +610,16 @@ bool NativeWidgetAura::IsActive() const { return window_ && wm::IsActiveWindow(window_); } -void NativeWidgetAura::SetAlwaysOnTop(bool on_top) { +void NativeWidgetAura::SetZOrderLevel(ui::ZOrderLevel order) { if (window_) - window_->SetProperty(aura::client::kAlwaysOnTopKey, on_top); + window_->SetProperty(aura::client::kZOrderingKey, order); } -bool NativeWidgetAura::IsAlwaysOnTop() const { - return window_ && window_->GetProperty(aura::client::kAlwaysOnTopKey); +ui::ZOrderLevel NativeWidgetAura::GetZOrderLevel() const { + if (window_) + return window_->GetProperty(aura::client::kZOrderingKey); + + return ui::ZOrderLevel::kNormal; } void NativeWidgetAura::SetVisibleOnAllWorkspaces(bool always_visible) { @@ -691,12 +691,12 @@ void NativeWidgetAura::FlashFrame(bool flash) { } void NativeWidgetAura::RunShellDrag(View* view, - const ui::OSExchangeData& data, + std::unique_ptr<ui::OSExchangeData> data, const gfx::Point& location, int operation, ui::DragDropTypes::DragEventSource source) { if (window_) - views::RunShellDrag(window_, data, location, operation, source); + views::RunShellDrag(window_, std::move(data), location, operation, source); } void NativeWidgetAura::SchedulePaintInRect(const gfx::Rect& rect) { @@ -1034,7 +1034,8 @@ void NativeWidgetAura::OnDragExited() { drop_helper_->OnDragExit(); } -int NativeWidgetAura::OnPerformDrop(const ui::DropTargetEvent& event) { +int NativeWidgetAura::OnPerformDrop(const ui::DropTargetEvent& event, + std::unique_ptr<ui::OSExchangeData> data) { DCHECK(drop_helper_.get() != nullptr); return drop_helper_->OnDrop(event.data(), event.location(), last_drop_operation_); @@ -1224,15 +1225,6 @@ void NativeWidgetPrivate::ReparentNativeView(gfx::NativeView native_view, } // static -gfx::FontList NativeWidgetPrivate::GetWindowTitleFontList() { -#if defined(OS_WIN) - return gfx::FontList(gfx::win::GetSystemFont(gfx::win::SystemFont::kCaption)); -#else - return gfx::FontList(); -#endif -} - -// static gfx::NativeView NativeWidgetPrivate::GetGlobalCapture( gfx::NativeView native_view) { aura::client::CaptureClient* capture_client = diff --git a/chromium/ui/views/widget/native_widget_aura.h b/chromium/ui/views/widget/native_widget_aura.h index e9bd036faf8..8fea00d5680 100644 --- a/chromium/ui/views/widget/native_widget_aura.h +++ b/chromium/ui/views/widget/native_widget_aura.h @@ -9,6 +9,7 @@ #include "base/macros.h" #include "base/memory/weak_ptr.h" +#include "build/build_config.h" #include "ui/aura/client/drag_drop_delegate.h" #include "ui/aura/client/focus_change_observer.h" #include "ui/aura/window_delegate.h" @@ -20,6 +21,10 @@ #include "ui/wm/public/activation_change_observer.h" #include "ui/wm/public/activation_delegate.h" +#if defined(OS_MACOSX) +#error This file must not be included on macOS; Chromium Mac doesn't use Aura. +#endif + namespace aura { class Window; } @@ -111,8 +116,8 @@ class VIEWS_EXPORT NativeWidgetAura : public internal::NativeWidgetPrivate, void Activate() override; void Deactivate() override; bool IsActive() const override; - void SetAlwaysOnTop(bool always_on_top) override; - bool IsAlwaysOnTop() const override; + void SetZOrderLevel(ui::ZOrderLevel order) override; + ui::ZOrderLevel GetZOrderLevel() const override; void SetVisibleOnAllWorkspaces(bool always_visible) override; bool IsVisibleOnAllWorkspaces() const override; void Maximize() override; @@ -128,7 +133,7 @@ class VIEWS_EXPORT NativeWidgetAura : public internal::NativeWidgetPrivate, void SetAspectRatio(const gfx::SizeF& aspect_ratio) override; void FlashFrame(bool flash_frame) override; void RunShellDrag(View* view, - const ui::OSExchangeData& data, + std::unique_ptr<ui::OSExchangeData> data, const gfx::Point& location, int operation, ui::DragDropTypes::DragEventSource source) override; @@ -204,7 +209,8 @@ class VIEWS_EXPORT NativeWidgetAura : public internal::NativeWidgetPrivate, void OnDragEntered(const ui::DropTargetEvent& event) override; int OnDragUpdated(const ui::DropTargetEvent& event) override; void OnDragExited() override; - int OnPerformDrop(const ui::DropTargetEvent& event) override; + int OnPerformDrop(const ui::DropTargetEvent& event, + std::unique_ptr<ui::OSExchangeData> data) override; protected: ~NativeWidgetAura() override; @@ -243,7 +249,7 @@ class VIEWS_EXPORT NativeWidgetAura : public internal::NativeWidgetPrivate, // The following factory is used for calls to close the NativeWidgetAura // instance. - base::WeakPtrFactory<NativeWidgetAura> close_widget_factory_; + base::WeakPtrFactory<NativeWidgetAura> close_widget_factory_{this}; DISALLOW_COPY_AND_ASSIGN(NativeWidgetAura); }; diff --git a/chromium/ui/views/widget/native_widget_mac.h b/chromium/ui/views/widget/native_widget_mac.h index 8733e1b1775..e64872d3357 100644 --- a/chromium/ui/views/widget/native_widget_mac.h +++ b/chromium/ui/views/widget/native_widget_mac.h @@ -18,10 +18,12 @@ class NativeWidgetMacNSWindow; namespace remote_cocoa { namespace mojom { -class BridgedNativeWidget; class CreateWindowParams; +class NativeWidgetNSWindow; class ValidateUserInterfaceItemResult; } // namespace mojom +class ApplicationHost; +class NativeWidgetNSWindowBridge; } // namespace remote_cocoa namespace views { @@ -30,10 +32,7 @@ class HitTestNativeWidgetMac; class MockNativeWidgetMac; class WidgetTest; } - -class BridgeFactoryHost; -class BridgedNativeWidgetImpl; -class BridgedNativeWidgetHostImpl; +class NativeWidgetMacNSWindowHost; class VIEWS_EXPORT NativeWidgetMac : public internal::NativeWidgetPrivate { public: @@ -41,7 +40,7 @@ class VIEWS_EXPORT NativeWidgetMac : public internal::NativeWidgetPrivate { ~NativeWidgetMac() override; // Informs |delegate_| that the native widget is about to be destroyed. - // BridgedNativeWidgetImpl::OnWindowWillClose() invokes this early when the + // NativeWidgetNSWindowBridge::OnWindowWillClose() invokes this early when the // NSWindowDelegate informs the bridge that the window is being closed (later, // invoking OnWindowDestroyed()). void WindowDestroying(); @@ -134,8 +133,8 @@ class VIEWS_EXPORT NativeWidgetMac : public internal::NativeWidgetPrivate { void Activate() override; void Deactivate() override; bool IsActive() const override; - void SetAlwaysOnTop(bool always_on_top) override; - bool IsAlwaysOnTop() const override; + void SetZOrderLevel(ui::ZOrderLevel order) override; + ui::ZOrderLevel GetZOrderLevel() const override; void SetVisibleOnAllWorkspaces(bool always_visible) override; bool IsVisibleOnAllWorkspaces() const override; void Maximize() override; @@ -151,7 +150,7 @@ class VIEWS_EXPORT NativeWidgetMac : public internal::NativeWidgetPrivate { void SetAspectRatio(const gfx::SizeF& aspect_ratio) override; void FlashFrame(bool flash_frame) override; void RunShellDrag(View* view, - const ui::OSExchangeData& data, + std::unique_ptr<ui::OSExchangeData> data, const gfx::Point& location, int operation, ui::DragDropTypes::DragEventSource source) override; @@ -187,7 +186,7 @@ class VIEWS_EXPORT NativeWidgetMac : public internal::NativeWidgetPrivate { const Widget::InitParams& widget_params, remote_cocoa::mojom::CreateWindowParams* params) {} - // Creates the NSWindow that will be passed to the BridgedNativeWidgetImpl. + // Creates the NSWindow that will be passed to the NativeWidgetNSWindowBridge. // Called by InitNativeWidget. The return value will be autoreleased. // Note that some tests (in particular, views_unittests that interact // with ScopedFakeNSWindowFullscreen, on 10.10) assume that these windows @@ -199,7 +198,7 @@ class VIEWS_EXPORT NativeWidgetMac : public internal::NativeWidgetPrivate { // Return the BridgeFactoryHost that is to be used for creating this window // and all of its child windows. This will return nullptr if the native // windows are to be created in the current process. - virtual BridgeFactoryHost* GetBridgeFactoryHost(); + virtual remote_cocoa::ApplicationHost* GetRemoteCocoaApplicationHost(); // Called after the window has been initialized. Allows subclasses to perform // additional initialization. @@ -209,10 +208,16 @@ class VIEWS_EXPORT NativeWidgetMac : public internal::NativeWidgetPrivate { virtual void OnWindowDestroying(gfx::NativeWindow window) {} internal::NativeWidgetDelegate* delegate() { return delegate_; } - remote_cocoa::mojom::BridgedNativeWidget* bridge() const; - BridgedNativeWidgetImpl* bridge_impl() const; - BridgedNativeWidgetHostImpl* bridge_host() const { - return bridge_host_.get(); + + // Return the mojo interface for the NSWindow. The interface may be + // implemented in-process or out-of-process. + remote_cocoa::mojom::NativeWidgetNSWindow* GetNSWindowMojo() const; + + // Return the bridge structure only if this widget is in-process. + remote_cocoa::NativeWidgetNSWindowBridge* GetInProcessNSWindowBridge() const; + + NativeWidgetMacNSWindowHost* GetNSWindowHost() const { + return ns_window_host_.get(); } private: @@ -221,13 +226,15 @@ class VIEWS_EXPORT NativeWidgetMac : public internal::NativeWidgetPrivate { friend class views::test::WidgetTest; internal::NativeWidgetDelegate* delegate_; - std::unique_ptr<BridgedNativeWidgetHostImpl> bridge_host_; + std::unique_ptr<NativeWidgetMacNSWindowHost> ns_window_host_; Widget::InitParams::Ownership ownership_; // Internal name. std::string name_; + Widget::InitParams::Type type_; + DISALLOW_COPY_AND_ASSIGN(NativeWidgetMac); }; diff --git a/chromium/ui/views/widget/native_widget_mac.mm b/chromium/ui/views/widget/native_widget_mac.mm index 0d7c1c9aada..9043f5bb363 100644 --- a/chromium/ui/views/widget/native_widget_mac.mm +++ b/chromium/ui/views/widget/native_widget_mac.mm @@ -11,12 +11,13 @@ #include "base/bind.h" #include "base/lazy_instance.h" #include "base/mac/scoped_nsobject.h" +#include "base/no_destructor.h" #include "base/strings/sys_string_conversions.h" #include "base/threading/thread_task_runner_handle.h" #include "components/crash/core/common/crash_key.h" #import "components/remote_cocoa/app_shim/bridged_content_view.h" -#import "components/remote_cocoa/app_shim/bridged_native_widget_impl.h" #import "components/remote_cocoa/app_shim/native_widget_mac_nswindow.h" +#import "components/remote_cocoa/app_shim/native_widget_ns_window_bridge.h" #import "components/remote_cocoa/app_shim/views_nswindow_delegate.h" #import "ui/base/cocoa/constrained_window/constrained_window_animation.h" #import "ui/base/cocoa/window_size_constants.h" @@ -25,11 +26,10 @@ #include "ui/events/gestures/gesture_recognizer_impl_mac.h" #include "ui/gfx/font_list.h" #import "ui/gfx/mac/coordinate_conversion.h" -#import "ui/gfx/mac/nswindow_frame_controls.h" #include "ui/native_theme/native_theme.h" #include "ui/native_theme/native_theme_mac.h" -#import "ui/views/cocoa/bridged_native_widget_host_impl.h" #import "ui/views/cocoa/drag_drop_client_mac.h" +#import "ui/views/cocoa/native_widget_mac_ns_window_host.h" #include "ui/views/widget/drop_helper.h" #include "ui/views/widget/widget_delegate.h" #include "ui/views/window/native_frame_view.h" @@ -37,6 +37,7 @@ using remote_cocoa::mojom::WindowVisibilityState; namespace views { + namespace { base::LazyInstance<ui::GestureRecognizerImplMac>::Leaky @@ -65,6 +66,42 @@ NSInteger StyleMaskForParams(const Widget::InitParams& params) { return NSBorderlessWindowMask; } +CGWindowLevel CGWindowLevelForZOrderLevel(ui::ZOrderLevel level, + Widget::InitParams::Type type) { + switch (level) { + case ui::ZOrderLevel::kNormal: + return kCGNormalWindowLevel; + case ui::ZOrderLevel::kFloatingWindow: + if (type == Widget::InitParams::TYPE_MENU) + return kCGPopUpMenuWindowLevel; + else + return kCGFloatingWindowLevel; + case ui::ZOrderLevel::kFloatingUIElement: + if (type == Widget::InitParams::TYPE_DRAG) + return kCGDraggingWindowLevel; + else + return kCGStatusWindowLevel; + case ui::ZOrderLevel::kSecuritySurface: + return kCGScreenSaverWindowLevel - 1; + } +} + +ui::ZOrderLevel ZOrderLevelForCGWindowLevel(CGWindowLevel level) { + switch (level) { + case kCGNormalWindowLevel: + return ui::ZOrderLevel::kNormal; + case kCGFloatingWindowLevel: + case kCGPopUpMenuWindowLevel: + default: + return ui::ZOrderLevel::kFloatingWindow; + case kCGStatusWindowLevel: + case kCGDraggingWindowLevel: + return ui::ZOrderLevel::kFloatingUIElement; + case kCGScreenSaverWindowLevel - 1: + return ui::ZOrderLevel::kSecuritySurface; + } +} + } // namespace //////////////////////////////////////////////////////////////////////////////// @@ -72,7 +109,7 @@ NSInteger StyleMaskForParams(const Widget::InitParams& params) { NativeWidgetMac::NativeWidgetMac(internal::NativeWidgetDelegate* delegate) : delegate_(delegate), - bridge_host_(new BridgedNativeWidgetHostImpl(this)), + ns_window_host_(new NativeWidgetMacNSWindowHost(this)), ownership_(Widget::InitParams::NATIVE_WIDGET_OWNS_WIDGET) {} NativeWidgetMac::~NativeWidgetMac() { @@ -88,8 +125,8 @@ void NativeWidgetMac::WindowDestroying() { } void NativeWidgetMac::WindowDestroyed() { - DCHECK(bridge()); - bridge_host_.reset(); + DCHECK(GetNSWindowMojo()); + ns_window_host_.reset(); // |OnNativeWidgetDestroyed| may delete |this| if the object does not own // itself. bool should_delete_this = @@ -125,12 +162,14 @@ bool NativeWidgetMac::ExecuteCommand( void NativeWidgetMac::InitNativeWidget(const Widget::InitParams& params) { ownership_ = params.ownership; name_ = params.name; - BridgedNativeWidgetHostImpl* parent_host = - BridgedNativeWidgetHostImpl::GetFromNativeView(params.parent); + type_ = params.type; + NativeWidgetMacNSWindowHost* parent_host = + NativeWidgetMacNSWindowHost::GetFromNativeView(params.parent); // Determine the factory through which to create the bridge - BridgeFactoryHost* bridge_factory_host = - parent_host ? parent_host->bridge_factory_host() : GetBridgeFactoryHost(); + remote_cocoa::ApplicationHost* application_host = + parent_host ? parent_host->application_host() + : GetRemoteCocoaApplicationHost(); // Compute the parameters to describe the NSWindow. auto create_window_params = remote_cocoa::mojom::CreateWindowParams::New(); @@ -141,35 +180,35 @@ void NativeWidgetMac::InitNativeWidget(const Widget::InitParams& params) { create_window_params->window_title_hidden = false; PopulateCreateWindowParams(params, create_window_params.get()); - if (bridge_factory_host) { - bridge_host_->CreateRemoteBridge(bridge_factory_host, - std::move(create_window_params)); + if (application_host) { + ns_window_host_->CreateRemoteNSWindow(application_host, + std::move(create_window_params)); } else { base::scoped_nsobject<NativeWidgetMacNSWindow> window( [CreateNSWindow(create_window_params.get()) retain]); - bridge_host_->CreateLocalBridge(std::move(window)); + ns_window_host_->CreateInProcessNSWindowBridge(std::move(window)); } - bridge_host_->SetParent(parent_host); - bridge_host_->InitWindow(params); + ns_window_host_->SetParent(parent_host); + ns_window_host_->InitWindow(params); OnWindowInitialized(); - // Only set always-on-top here if it is true since setting it may affect how - // the window is treated by Expose. - if (params.keep_on_top) - SetAlwaysOnTop(true); + // Only set the z-order here if it is non-default since setting it may affect + // how the window is treated by Expose. + if (params.EffectiveZOrderLevel() != ui::ZOrderLevel::kNormal) + SetZOrderLevel(params.EffectiveZOrderLevel()); delegate_->OnNativeWidgetCreated(); DCHECK(GetWidget()->GetRootView()); - bridge_host_->SetRootView(GetWidget()->GetRootView()); - bridge()->CreateContentView(bridge_host_->GetRootViewNSViewId(), - GetWidget()->GetRootView()->bounds()); + ns_window_host_->SetRootView(GetWidget()->GetRootView()); + GetNSWindowMojo()->CreateContentView(ns_window_host_->GetRootViewNSViewId(), + GetWidget()->GetRootView()->bounds()); if (auto* focus_manager = GetWidget()->GetFocusManager()) { - bridge()->MakeFirstResponder(); - bridge_host_->SetFocusManager(focus_manager); + GetNSWindowMojo()->MakeFirstResponder(); + ns_window_host_->SetFocusManager(focus_manager); } - bridge_host_->CreateCompositor(params); + ns_window_host_->CreateCompositor(params); if (g_init_native_widget_callback) g_init_native_widget_callback->Run(this); @@ -177,7 +216,7 @@ void NativeWidgetMac::InitNativeWidget(const Widget::InitParams& params) { void NativeWidgetMac::OnWidgetInitDone() { OnSizeConstraintsChanged(); - bridge_host_->OnWidgetInitDone(); + ns_window_host_->OnWidgetInitDone(); } NonClientFrameView* NativeWidgetMac::CreateNonClientFrameView() { @@ -215,7 +254,7 @@ gfx::NativeView NativeWidgetMac::GetNativeView() const { } gfx::NativeWindow NativeWidgetMac::GetNativeWindow() const { - return bridge_host_ ? bridge_host_->GetLocalNSWindow() : nil; + return ns_window_host_ ? ns_window_host_->GetInProcessNSWindow() : nil; } Widget* NativeWidgetMac::GetTopLevelWidget() { @@ -224,66 +263,66 @@ Widget* NativeWidgetMac::GetTopLevelWidget() { } const ui::Compositor* NativeWidgetMac::GetCompositor() const { - return bridge_host_ && bridge_host_->layer() - ? bridge_host_->layer()->GetCompositor() + return ns_window_host_ && ns_window_host_->layer() + ? ns_window_host_->layer()->GetCompositor() : nullptr; } const ui::Layer* NativeWidgetMac::GetLayer() const { - return bridge_host_ ? bridge_host_->layer() : nullptr; + return ns_window_host_ ? ns_window_host_->layer() : nullptr; } void NativeWidgetMac::ReorderNativeViews() { - if (bridge_host_) - bridge_host_->ReorderChildViews(); + if (ns_window_host_) + ns_window_host_->ReorderChildViews(); } void NativeWidgetMac::ViewRemoved(View* view) { DragDropClientMac* client = - bridge_host_ ? bridge_host_->drag_drop_client() : nullptr; + ns_window_host_ ? ns_window_host_->drag_drop_client() : nullptr; if (client) client->drop_helper()->ResetTargetViewIfEquals(view); } void NativeWidgetMac::SetNativeWindowProperty(const char* name, void* value) { - if (bridge_host_) - bridge_host_->SetNativeWindowProperty(name, value); + if (ns_window_host_) + ns_window_host_->SetNativeWindowProperty(name, value); } void* NativeWidgetMac::GetNativeWindowProperty(const char* name) const { - if (bridge_host_) - return bridge_host_->GetNativeWindowProperty(name); + if (ns_window_host_) + return ns_window_host_->GetNativeWindowProperty(name); return nullptr; } TooltipManager* NativeWidgetMac::GetTooltipManager() const { - if (bridge_host_) - return bridge_host_->tooltip_manager(); + if (ns_window_host_) + return ns_window_host_->tooltip_manager(); return nullptr; } void NativeWidgetMac::SetCapture() { - if (bridge()) - bridge()->AcquireCapture(); + if (GetNSWindowMojo()) + GetNSWindowMojo()->AcquireCapture(); } void NativeWidgetMac::ReleaseCapture() { - if (bridge()) - bridge()->ReleaseCapture(); + if (GetNSWindowMojo()) + GetNSWindowMojo()->ReleaseCapture(); } bool NativeWidgetMac::HasCapture() const { - return bridge_host_ && bridge_host_->IsMouseCaptureActive(); + return ns_window_host_ && ns_window_host_->IsMouseCaptureActive(); } ui::InputMethod* NativeWidgetMac::GetInputMethod() { - return bridge_host_ ? bridge_host_->GetInputMethod() : nullptr; + return ns_window_host_ ? ns_window_host_->GetInputMethod() : nullptr; } void NativeWidgetMac::CenterWindow(const gfx::Size& size) { - bridge()->SetSizeAndCenter(size, GetWidget()->GetMinimumSize()); + GetNSWindowMojo()->SetSizeAndCenter(size, GetWidget()->GetMinimumSize()); } void NativeWidgetMac::GetWindowPlacement( @@ -299,9 +338,9 @@ void NativeWidgetMac::GetWindowPlacement( } bool NativeWidgetMac::SetWindowTitle(const base::string16& title) { - if (!bridge_host_) + if (!ns_window_host_) return false; - return bridge_host_->SetWindowTitle(title); + return ns_window_host_->SetWindowTitle(title); } void NativeWidgetMac::SetWindowIcons(const gfx::ImageSkia& window_icon, @@ -321,21 +360,23 @@ void NativeWidgetMac::InitModalType(ui::ModalType modal_type) { // A peculiarity of the constrained window framework is that it permits a // dialog of MODAL_TYPE_WINDOW to have a null parent window; falling back to // a non-modal window in this case. - DCHECK(bridge_host_->parent() || modal_type == ui::MODAL_TYPE_WINDOW); + DCHECK(ns_window_host_->parent() || modal_type == ui::MODAL_TYPE_WINDOW); // Everything happens upon show. } gfx::Rect NativeWidgetMac::GetWindowBoundsInScreen() const { - return bridge_host_ ? bridge_host_->GetWindowBoundsInScreen() : gfx::Rect(); + return ns_window_host_ ? ns_window_host_->GetWindowBoundsInScreen() + : gfx::Rect(); } gfx::Rect NativeWidgetMac::GetClientAreaBoundsInScreen() const { - return bridge_host_ ? bridge_host_->GetContentBoundsInScreen() : gfx::Rect(); + return ns_window_host_ ? ns_window_host_->GetContentBoundsInScreen() + : gfx::Rect(); } gfx::Rect NativeWidgetMac::GetRestoredBounds() const { - return bridge_host_ ? bridge_host_->GetRestoredBounds() : gfx::Rect(); + return ns_window_host_ ? ns_window_host_->GetRestoredBounds() : gfx::Rect(); } std::string NativeWidgetMac::GetWorkspace() const { @@ -343,17 +384,17 @@ std::string NativeWidgetMac::GetWorkspace() const { } void NativeWidgetMac::SetBounds(const gfx::Rect& bounds) { - if (bridge_host_) - bridge_host_->SetBounds(bounds); + if (ns_window_host_) + ns_window_host_->SetBounds(bounds); } void NativeWidgetMac::SetBoundsConstrained(const gfx::Rect& bounds) { - if (!bridge_host_) + if (!ns_window_host_) return; gfx::Rect new_bounds(bounds); - if (bridge_host_->parent()) { + if (ns_window_host_->parent()) { new_bounds.AdjustToFit( - gfx::Rect(bridge_host_->parent()->GetWindowBoundsInScreen().size())); + gfx::Rect(ns_window_host_->parent()->GetWindowBoundsInScreen().size())); } else { new_bounds = ConstrainBoundsToDisplayWorkArea(new_bounds); } @@ -367,36 +408,35 @@ void NativeWidgetMac::SetSize(const gfx::Size& size) { } void NativeWidgetMac::StackAbove(gfx::NativeView native_view) { - if (!bridge()) + if (!GetNSWindowMojo()) return; auto* sibling_host = - BridgedNativeWidgetHostImpl::GetFromNativeView(native_view); + NativeWidgetMacNSWindowHost::GetFromNativeView(native_view); if (!sibling_host) { // This will only work if |this| is in-process. - DCHECK(!bridge_host_->bridge_factory_host()); + DCHECK(!ns_window_host_->application_host()); NSInteger view_parent = native_view.GetNativeNSView().window.windowNumber; [GetNativeWindow().GetNativeNSWindow() orderWindow:NSWindowAbove relativeTo:view_parent]; return; } - if (bridge_host_->bridge_factory_host() == - sibling_host->bridge_factory_host()) { - // Check if |native_view|'s BridgedNativeWidgetHostImpl corresponds to the + if (ns_window_host_->application_host() == sibling_host->application_host()) { + // Check if |native_view|'s NativeWidgetMacNSWindowHost corresponds to the // same process as |this|. - bridge()->StackAbove(sibling_host->bridged_native_widget_id()); + GetNSWindowMojo()->StackAbove(sibling_host->bridged_native_widget_id()); return; } - NOTREACHED() << "|native_view|'s BridgedNativeWidgetHostImpl isn't same " + NOTREACHED() << "|native_view|'s NativeWidgetMacNSWindowHost isn't same " "process |this|"; } void NativeWidgetMac::StackAtTop() { - if (bridge()) - bridge()->StackAtTop(); + if (GetNSWindowMojo()) + GetNSWindowMojo()->StackAtTop(); } void NativeWidgetMac::SetShape(std::unique_ptr<Widget::ShapeRects> shape) { @@ -404,20 +444,20 @@ void NativeWidgetMac::SetShape(std::unique_ptr<Widget::ShapeRects> shape) { } void NativeWidgetMac::Close() { - if (bridge()) - bridge()->CloseWindow(); + if (GetNSWindowMojo()) + GetNSWindowMojo()->CloseWindow(); } void NativeWidgetMac::CloseNow() { - if (bridge_host_) - bridge_host_->CloseWindowNow(); - // Note: |bridge_host_| will be deleted here, and |this| will be deleted here - // when ownership_ == NATIVE_WIDGET_OWNS_WIDGET, + if (ns_window_host_) + ns_window_host_->CloseWindowNow(); + // Note: |ns_window_host_| will be deleted here, and |this| will be deleted + // here when ownership_ == NATIVE_WIDGET_OWNS_WIDGET, } void NativeWidgetMac::Show(ui::WindowShowState show_state, const gfx::Rect& restore_bounds) { - if (!bridge()) + if (!GetNSWindowMojo()) return; switch (show_state) { @@ -439,7 +479,7 @@ void NativeWidgetMac::Show(ui::WindowShowState show_state, window_state = WindowVisibilityState::kShowInactive; else if (show_state == ui::SHOW_STATE_MINIMIZED) window_state = WindowVisibilityState::kHideWindow; - bridge()->SetVisibilityState(window_state); + GetNSWindowMojo()->SetVisibilityState(window_state); // Ignore the SetInitialFocus() result. BridgedContentView should get // firstResponder status regardless. @@ -447,19 +487,20 @@ void NativeWidgetMac::Show(ui::WindowShowState show_state, } void NativeWidgetMac::Hide() { - if (!bridge()) + if (!GetNSWindowMojo()) return; - bridge()->SetVisibilityState(WindowVisibilityState::kHideWindow); + GetNSWindowMojo()->SetVisibilityState(WindowVisibilityState::kHideWindow); } bool NativeWidgetMac::IsVisible() const { - return bridge_host_ && bridge_host_->IsVisible(); + return ns_window_host_ && ns_window_host_->IsVisible(); } void NativeWidgetMac::Activate() { - if (!bridge()) + if (!GetNSWindowMojo()) return; - bridge()->SetVisibilityState(WindowVisibilityState::kShowAndActivateWindow); + GetNSWindowMojo()->SetVisibilityState( + WindowVisibilityState::kShowAndActivateWindow); } void NativeWidgetMac::Deactivate() { @@ -467,22 +508,30 @@ void NativeWidgetMac::Deactivate() { } bool NativeWidgetMac::IsActive() const { - return bridge_host_ ? bridge_host_->IsWindowKey() : false; + return ns_window_host_ ? ns_window_host_->IsWindowKey() : false; } -void NativeWidgetMac::SetAlwaysOnTop(bool always_on_top) { - gfx::SetNSWindowAlwaysOnTop(GetNativeWindow().GetNativeNSWindow(), - always_on_top); +void NativeWidgetMac::SetZOrderLevel(ui::ZOrderLevel order) { + NSWindow* window = GetNativeWindow().GetNativeNSWindow(); + [window setLevel:CGWindowLevelForZOrderLevel(order, type_)]; + + // Windows that have a higher window level than NSNormalWindowLevel default to + // NSWindowCollectionBehaviorTransient. Set the value explicitly here to match + // normal windows. + NSWindowCollectionBehavior behavior = + [window collectionBehavior] | NSWindowCollectionBehaviorManaged; + [window setCollectionBehavior:behavior]; } -bool NativeWidgetMac::IsAlwaysOnTop() const { - return gfx::IsNSWindowAlwaysOnTop(GetNativeWindow().GetNativeNSWindow()); +ui::ZOrderLevel NativeWidgetMac::GetZOrderLevel() const { + return ZOrderLevelForCGWindowLevel( + [GetNativeWindow().GetNativeNSWindow() level]); } void NativeWidgetMac::SetVisibleOnAllWorkspaces(bool always_visible) { - if (!bridge()) + if (!GetNSWindowMojo()) return; - bridge()->SetVisibleOnAllSpaces(always_visible); + GetNSWindowMojo()->SetVisibleOnAllSpaces(always_visible); } bool NativeWidgetMac::IsVisibleOnAllWorkspaces() const { @@ -494,9 +543,9 @@ void NativeWidgetMac::Maximize() { } void NativeWidgetMac::Minimize() { - if (!bridge()) + if (!GetNSWindowMojo()) return; - bridge()->SetMiniaturized(true); + GetNSWindowMojo()->SetMiniaturized(true); } bool NativeWidgetMac::IsMaximized() const { @@ -506,46 +555,46 @@ bool NativeWidgetMac::IsMaximized() const { } bool NativeWidgetMac::IsMinimized() const { - if (!bridge_host_) + if (!ns_window_host_) return false; - return bridge_host_->IsMiniaturized(); + return ns_window_host_->IsMiniaturized(); } void NativeWidgetMac::Restore() { - if (!bridge()) + if (!GetNSWindowMojo()) return; - bridge()->SetFullscreen(false); - bridge()->SetMiniaturized(false); + GetNSWindowMojo()->SetFullscreen(false); + GetNSWindowMojo()->SetMiniaturized(false); } void NativeWidgetMac::SetFullscreen(bool fullscreen) { - if (!bridge_host_) + if (!ns_window_host_) return; - bridge_host_->SetFullscreen(fullscreen); + ns_window_host_->SetFullscreen(fullscreen); } bool NativeWidgetMac::IsFullscreen() const { - return bridge_host_ && bridge_host_->target_fullscreen_state(); + return ns_window_host_ && ns_window_host_->target_fullscreen_state(); } void NativeWidgetMac::SetCanAppearInExistingFullscreenSpaces( bool can_appear_in_existing_fullscreen_spaces) { - if (!bridge()) + if (!GetNSWindowMojo()) return; - bridge()->SetCanAppearInExistingFullscreenSpaces( + GetNSWindowMojo()->SetCanAppearInExistingFullscreenSpaces( can_appear_in_existing_fullscreen_spaces); } void NativeWidgetMac::SetOpacity(float opacity) { - if (!bridge()) + if (!GetNSWindowMojo()) return; - bridge()->SetOpacity(opacity); + GetNSWindowMojo()->SetOpacity(opacity); } void NativeWidgetMac::SetAspectRatio(const gfx::SizeF& aspect_ratio) { - if (!bridge()) + if (!GetNSWindowMojo()) return; - bridge()->SetContentAspectRatio(aspect_ratio); + GetNSWindowMojo()->SetContentAspectRatio(aspect_ratio); } void NativeWidgetMac::FlashFrame(bool flash_frame) { @@ -553,12 +602,12 @@ void NativeWidgetMac::FlashFrame(bool flash_frame) { } void NativeWidgetMac::RunShellDrag(View* view, - const ui::OSExchangeData& data, + std::unique_ptr<ui::OSExchangeData> data, const gfx::Point& location, int operation, ui::DragDropTypes::DragEventSource source) { - bridge_host_->drag_drop_client()->StartDragAndDrop(view, data, operation, - source); + ns_window_host_->drag_drop_client()->StartDragAndDrop(view, std::move(data), + operation, source); } void NativeWidgetMac::SchedulePaintInRect(const gfx::Rect& rect) { @@ -571,8 +620,8 @@ void NativeWidgetMac::SchedulePaintInRect(const gfx::Rect& rect) { target_rect.origin.y = NSHeight(client_rect) - target_rect.origin.y - NSHeight(target_rect); [GetNativeView().GetNativeNSView() setNeedsDisplayInRect:target_rect]; - if (bridge_host_ && bridge_host_->layer()) - bridge_host_->layer()->SchedulePaint(rect); + if (ns_window_host_ && ns_window_host_->layer()) + ns_window_host_->layer()->SchedulePaint(rect); } void NativeWidgetMac::ScheduleLayout() { @@ -582,15 +631,15 @@ void NativeWidgetMac::ScheduleLayout() { } void NativeWidgetMac::SetCursor(gfx::NativeCursor cursor) { - if (bridge_impl()) - bridge_impl()->SetCursor(cursor); + if (GetInProcessNSWindowBridge()) + GetInProcessNSWindowBridge()->SetCursor(cursor); } void NativeWidgetMac::ShowEmojiPanel() { // We must plumb the call to ui::ShowEmojiPanel() over the bridge so that it // is called from the correct process. - if (bridge()) - bridge()->ShowEmojiPanel(); + if (GetNSWindowMojo()) + GetNSWindowMojo()->ShowEmojiPanel(); } bool NativeWidgetMac::IsMouseEventsEnabled() const { @@ -608,36 +657,37 @@ void NativeWidgetMac::ClearNativeFocus() { // To quote DesktopWindowTreeHostX11, "This method is weird and misnamed." // The goal is to set focus to the content window, thereby removing focus from // any NSView in the window that doesn't belong to toolkit-views. - if (!bridge()) + if (!GetNSWindowMojo()) return; - bridge()->MakeFirstResponder(); + GetNSWindowMojo()->MakeFirstResponder(); } gfx::Rect NativeWidgetMac::GetWorkAreaBoundsInScreen() const { - return bridge_host_ ? bridge_host_->GetCurrentDisplay().work_area() - : gfx::Rect(); + return ns_window_host_ ? ns_window_host_->GetCurrentDisplay().work_area() + : gfx::Rect(); } Widget::MoveLoopResult NativeWidgetMac::RunMoveLoop( const gfx::Vector2d& drag_offset, Widget::MoveLoopSource source, Widget::MoveLoopEscapeBehavior escape_behavior) { - if (!bridge_impl()) + if (!GetInProcessNSWindowBridge()) return Widget::MOVE_LOOP_CANCELED; ReleaseCapture(); - return bridge_impl()->RunMoveLoop(drag_offset) ? Widget::MOVE_LOOP_SUCCESSFUL - : Widget::MOVE_LOOP_CANCELED; + return GetInProcessNSWindowBridge()->RunMoveLoop(drag_offset) + ? Widget::MOVE_LOOP_SUCCESSFUL + : Widget::MOVE_LOOP_CANCELED; } void NativeWidgetMac::EndMoveLoop() { - if (bridge_impl()) - bridge_impl()->EndMoveLoop(); + if (GetInProcessNSWindowBridge()) + GetInProcessNSWindowBridge()->EndMoveLoop(); } void NativeWidgetMac::SetVisibilityChangedAnimationsEnabled(bool value) { - if (bridge()) - bridge()->SetAnimationEnabled(value); + if (GetNSWindowMojo()) + GetNSWindowMojo()->SetAnimationEnabled(value); } void NativeWidgetMac::SetVisibilityAnimationDuration( @@ -663,8 +713,8 @@ void NativeWidgetMac::SetVisibilityAnimationTransition( transitions = remote_cocoa::mojom::VisibilityTransition::kBoth; break; } - if (bridge()) - bridge()->SetTransitionsToAnimate(transitions); + if (GetNSWindowMojo()) + GetNSWindowMojo()->SetTransitionsToAnimate(transitions); } bool NativeWidgetMac::IsTranslucentWindowOpacitySupported() const { @@ -677,10 +727,10 @@ ui::GestureRecognizer* NativeWidgetMac::GetGestureRecognizer() { void NativeWidgetMac::OnSizeConstraintsChanged() { Widget* widget = GetWidget(); - bridge()->SetSizeConstraints(widget->GetMinimumSize(), - widget->GetMaximumSize(), - widget->widget_delegate()->CanResize(), - widget->widget_delegate()->CanMaximize()); + GetNSWindowMojo()->SetSizeConstraints( + widget->GetMinimumSize(), widget->GetMaximumSize(), + widget->widget_delegate()->CanResize(), + widget->widget_delegate()->CanMaximize()); } std::string NativeWidgetMac::GetName() const { @@ -707,19 +757,24 @@ void NativeWidgetMac::SetInitNativeWidgetCallback( NativeWidgetMacNSWindow* NativeWidgetMac::CreateNSWindow( const remote_cocoa::mojom::CreateWindowParams* params) { - return BridgedNativeWidgetImpl::CreateNSWindow(params).autorelease(); + return remote_cocoa::NativeWidgetNSWindowBridge::CreateNSWindow(params) + .autorelease(); } -BridgeFactoryHost* NativeWidgetMac::GetBridgeFactoryHost() { +remote_cocoa::ApplicationHost* +NativeWidgetMac::GetRemoteCocoaApplicationHost() { return nullptr; } -remote_cocoa::mojom::BridgedNativeWidget* NativeWidgetMac::bridge() const { - return bridge_host_ ? bridge_host_->bridge() : nullptr; +remote_cocoa::mojom::NativeWidgetNSWindow* NativeWidgetMac::GetNSWindowMojo() + const { + return ns_window_host_ ? ns_window_host_->GetNSWindowMojo() : nullptr; } -BridgedNativeWidgetImpl* NativeWidgetMac::bridge_impl() const { - return bridge_host_ ? bridge_host_->bridge_impl() : nullptr; +remote_cocoa::NativeWidgetNSWindowBridge* +NativeWidgetMac::GetInProcessNSWindowBridge() const { + return ns_window_host_ ? ns_window_host_->GetInProcessNSWindowBridge() + : nullptr; } //////////////////////////////////////////////////////////////////////////////// @@ -776,9 +831,9 @@ NativeWidgetPrivate* NativeWidgetPrivate::GetNativeWidgetForNativeView( // static NativeWidgetPrivate* NativeWidgetPrivate::GetNativeWidgetForNativeWindow( gfx::NativeWindow window) { - if (BridgedNativeWidgetHostImpl* bridge_host_impl = - BridgedNativeWidgetHostImpl::GetFromNativeWindow(window)) { - return bridge_host_impl->native_widget_mac(); + if (NativeWidgetMacNSWindowHost* ns_window_host_impl = + NativeWidgetMacNSWindowHost::GetFromNativeWindow(window)) { + return ns_window_host_impl->native_widget_mac(); } return nullptr; // Not created by NativeWidgetMac. } @@ -786,21 +841,21 @@ NativeWidgetPrivate* NativeWidgetPrivate::GetNativeWidgetForNativeWindow( // static NativeWidgetPrivate* NativeWidgetPrivate::GetTopLevelNativeWidget( gfx::NativeView native_view) { - BridgedNativeWidgetHostImpl* bridge_host = - BridgedNativeWidgetHostImpl::GetFromNativeView(native_view); - if (!bridge_host) + NativeWidgetMacNSWindowHost* window_host = + NativeWidgetMacNSWindowHost::GetFromNativeView(native_view); + if (!window_host) return nullptr; - while (bridge_host->parent()) - bridge_host = bridge_host->parent(); - return bridge_host->native_widget_mac(); + while (window_host->parent()) + window_host = window_host->parent(); + return window_host->native_widget_mac(); } // static void NativeWidgetPrivate::GetAllChildWidgets(gfx::NativeView native_view, Widget::Widgets* children) { - BridgedNativeWidgetHostImpl* bridge_host = - BridgedNativeWidgetHostImpl::GetFromNativeView(native_view); - if (!bridge_host) { + NativeWidgetMacNSWindowHost* window_host = + NativeWidgetMacNSWindowHost::GetFromNativeView(native_view); + if (!window_host) { NSView* ns_view = native_view.GetNativeNSView(); // The NSWindow is not itself a views::Widget, but it may have children that // are. Support returning Widgets that are parented to the NSWindow, except: @@ -824,37 +879,37 @@ void NativeWidgetPrivate::GetAllChildWidgets(gfx::NativeView native_view, // If |native_view| is a subview of the contentView, it will share an // NSWindow, but will itself be a native child of the Widget. That is, adding - // bridge_host->..->GetWidget() to |children| would be adding the _parent_ of + // window_host->..->GetWidget() to |children| would be adding the _parent_ of // |native_view|, not the Widget for |native_view|. |native_view| doesn't have // a corresponding Widget of its own in this case (and so can't have Widget // children of its own on Mac). - if (bridge_host->native_widget_mac()->GetNativeView() != native_view) + if (window_host->native_widget_mac()->GetNativeView() != native_view) return; // Code expects widget for |native_view| to be added to |children|. - if (bridge_host->native_widget_mac()->GetWidget()) - children->insert(bridge_host->native_widget_mac()->GetWidget()); + if (window_host->native_widget_mac()->GetWidget()) + children->insert(window_host->native_widget_mac()->GetWidget()); // When the NSWindow *is* a Widget, only consider children(). I.e. do not - // look through -[NSWindow childWindows] as done for the (!bridge_host) case + // look through -[NSWindow childWindows] as done for the (!window_host) case // above. -childWindows does not support hidden windows, and anything in there // which is not in children() would have been added by AppKit. - for (BridgedNativeWidgetHostImpl* child : bridge_host->children()) + for (NativeWidgetMacNSWindowHost* child : window_host->children()) GetAllChildWidgets(child->native_widget_mac()->GetNativeView(), children); } // static void NativeWidgetPrivate::GetAllOwnedWidgets(gfx::NativeView native_view, Widget::Widgets* owned) { - BridgedNativeWidgetHostImpl* bridge_host = - BridgedNativeWidgetHostImpl::GetFromNativeView(native_view); - if (!bridge_host) { + NativeWidgetMacNSWindowHost* window_host = + NativeWidgetMacNSWindowHost::GetFromNativeView(native_view); + if (!window_host) { GetAllChildWidgets(native_view, owned); return; } - if (bridge_host->native_widget_mac()->GetNativeView() != native_view) + if (window_host->native_widget_mac()->GetNativeView() != native_view) return; - for (BridgedNativeWidgetHostImpl* child : bridge_host->children()) + for (NativeWidgetMacNSWindowHost* child : window_host->children()) GetAllChildWidgets(child->native_widget_mac()->GetNativeView(), owned); } @@ -869,25 +924,25 @@ void NativeWidgetPrivate::ReparentNativeView(gfx::NativeView native_view, return; } - BridgedNativeWidgetHostImpl* bridge_host = - BridgedNativeWidgetHostImpl::GetFromNativeView(native_view); - DCHECK(bridge_host); + NativeWidgetMacNSWindowHost* window_host = + NativeWidgetMacNSWindowHost::GetFromNativeView(native_view); + DCHECK(window_host); gfx::NativeView bridge_view = - bridge_host->native_widget_mac()->GetNativeView(); + window_host->native_widget_mac()->GetNativeView(); gfx::NativeWindow bridge_window = - bridge_host->native_widget_mac()->GetNativeWindow(); + window_host->native_widget_mac()->GetNativeWindow(); bool bridge_is_top_level = - bridge_host->native_widget_mac()->GetWidget()->is_top_level(); + window_host->native_widget_mac()->GetWidget()->is_top_level(); DCHECK([native_view.GetNativeNSView() isDescendantOf:bridge_view.GetNativeNSView()]); DCHECK(bridge_window && ![bridge_window.GetNativeNSWindow() isSheet]); - BridgedNativeWidgetHostImpl* parent_bridge_host = - BridgedNativeWidgetHostImpl::GetFromNativeView(new_parent); + NativeWidgetMacNSWindowHost* parent_window_host = + NativeWidgetMacNSWindowHost::GetFromNativeView(new_parent); // Early out for no-op changes. if (native_view == bridge_view && bridge_is_top_level && - bridge_host->parent() == parent_bridge_host) { + window_host->parent() == parent_window_host) { return; } @@ -899,13 +954,13 @@ void NativeWidgetPrivate::ReparentNativeView(gfx::NativeView native_view, child->NotifyNativeViewHierarchyWillChange(); // Update |brige_host|'s parent only if - // BridgedNativeWidgetImpl::ReparentNativeView will. + // NativeWidgetNSWindowBridge::ReparentNativeView will. if (native_view == bridge_view) { - bridge_host->SetParent(parent_bridge_host); + window_host->SetParent(parent_window_host); if (!bridge_is_top_level) { - // Make |bridge_host|'s NSView be a child of |new_parent| by adding it as + // Make |window_host|'s NSView be a child of |new_parent| by adding it as // a subview. Note that this will have the effect of removing - // |bridge_host|'s NSView from its NSWindow. The |NSWindow| must remain + // |window_host|'s NSView from its NSWindow. The |NSWindow| must remain // visible because it controls the bounds and visibility of the ui::Layer, // so just hide it by setting alpha value to zero. // TODO(ccameron): This path likely violates assumptions. Verify that this @@ -930,15 +985,9 @@ void NativeWidgetPrivate::ReparentNativeView(gfx::NativeView native_view, } // static -gfx::FontList NativeWidgetPrivate::GetWindowTitleFontList() { - NOTIMPLEMENTED(); - return gfx::FontList(); -} - -// static gfx::NativeView NativeWidgetPrivate::GetGlobalCapture( gfx::NativeView native_view) { - return BridgedNativeWidgetHostImpl::GetGlobalCaptureView(); + return NativeWidgetMacNSWindowHost::GetGlobalCaptureView(); } } // namespace internal diff --git a/chromium/ui/views/widget/native_widget_mac_unittest.mm b/chromium/ui/views/widget/native_widget_mac_unittest.mm index 94a6c99e4e3..5974ae32fbe 100644 --- a/chromium/ui/views/widget/native_widget_mac_unittest.mm +++ b/chromium/ui/views/widget/native_widget_mac_unittest.mm @@ -20,8 +20,8 @@ #include "base/test/test_timeouts.h" #include "base/threading/thread_task_runner_handle.h" #import "components/remote_cocoa/app_shim/bridged_content_view.h" -#import "components/remote_cocoa/app_shim/bridged_native_widget_impl.h" #import "components/remote_cocoa/app_shim/native_widget_mac_nswindow.h" +#import "components/remote_cocoa/app_shim/native_widget_ns_window_bridge.h" #import "testing/gtest_mac.h" #include "third_party/skia/include/core/SkBitmap.h" #include "third_party/skia/include/core/SkCanvas.h" @@ -33,7 +33,7 @@ #include "ui/events/test/event_generator.h" #import "ui/gfx/mac/coordinate_conversion.h" #include "ui/views/bubble/bubble_dialog_delegate_view.h" -#include "ui/views/cocoa/bridged_native_widget_host_impl.h" +#include "ui/views/cocoa/native_widget_mac_ns_window_host.h" #include "ui/views/controls/button/label_button.h" #include "ui/views/controls/label.h" #include "ui/views/controls/native/native_view_host.h" @@ -92,12 +92,12 @@ namespace views { namespace test { -// BridgedNativeWidgetImpl friend to access private members. +// NativeWidgetNSWindowBridge friend to access private members. class BridgedNativeWidgetTestApi { public: explicit BridgedNativeWidgetTestApi(NSWindow* window) { - bridge_ = - BridgedNativeWidgetHostImpl::GetFromNativeWindow(window)->bridge_impl(); + bridge_ = NativeWidgetMacNSWindowHost::GetFromNativeWindow(window) + ->GetInProcessNSWindowBridge(); } // Simulate a frame swap from the compositor. @@ -116,7 +116,7 @@ class BridgedNativeWidgetTestApi { } private: - BridgedNativeWidgetImpl* bridge_; + remote_cocoa::NativeWidgetNSWindowBridge* bridge_; DISALLOW_COPY_AND_ASSIGN(BridgedNativeWidgetTestApi); }; @@ -152,7 +152,7 @@ class TestWindowNativeWidgetMac : public NativeWidgetMac { DISALLOW_COPY_AND_ASSIGN(TestWindowNativeWidgetMac); }; -// Tests for parts of NativeWidgetMac not covered by BridgedNativeWidgetImpl, +// Tests for parts of NativeWidgetMac not covered by NativeWidgetNSWindowBridge, // which need access to Cocoa APIs. class NativeWidgetMacTest : public WidgetTest { public: @@ -782,8 +782,8 @@ TEST_F(NativeWidgetMacTest, NonWidgetParent) { EXPECT_NE(child, top_level_widget->GetWidget()); // To verify the parent, we need to use NativeWidgetMac APIs. - BridgedNativeWidgetHostImpl* bridged_native_widget_host = - BridgedNativeWidgetHostImpl::GetFromNativeWindow( + NativeWidgetMacNSWindowHost* bridged_native_widget_host = + NativeWidgetMacNSWindowHost::GetFromNativeWindow( child->GetNativeWindow()); EXPECT_EQ(bridged_native_widget_host->parent() ->native_widget_mac() @@ -1331,7 +1331,7 @@ TEST_F(NativeWidgetMacTest, ShowAnimationControl) { EXPECT_TRUE([retained_animation isAnimating]); // Hide without waiting for the animation to complete. Animation should cancel - // and clear references from BridgedNativeWidgetImpl. + // and clear references from NativeWidgetNSWindowBridge. modal_dialog_widget->Hide(); EXPECT_FALSE([retained_animation isAnimating]); EXPECT_FALSE(test_api.show_animation()); @@ -1434,12 +1434,12 @@ TEST_F(NativeWidgetMacTest, WindowModalSheet) { // TODO(tapted): Ideally [native_parent orderOut:nil] would also work here. // But it does not. AppKit's childWindow management breaks down after an - // -orderOut: (see BridgedNativeWidgetImpl::OnVisibilityChanged()). For - // regular child windows, BridgedNativeWidgetImpl fixes the behavior with its - // own management. However, it can't do that for sheets without encountering - // http://crbug.com/605098 and http://crbug.com/667602. -[NSApp hide:] makes - // the NSWindow hidden in a different way, which does not break like - // -orderOut: does. Which is good, because a user can always do -[NSApp + // -orderOut: (see NativeWidgetNSWindowBridge::OnVisibilityChanged()). For + // regular child windows, NativeWidgetNSWindowBridge fixes the behavior with + // its own management. However, it can't do that for sheets without + // encountering http://crbug.com/605098 and http://crbug.com/667602. -[NSApp + // hide:] makes the NSWindow hidden in a different way, which does not break + // like -orderOut: does. Which is good, because a user can always do -[NSApp // hide:], e.g., with Cmd+h, and that needs to work correctly. [NSApp hide:nil]; @@ -1620,14 +1620,14 @@ TEST_F(NativeWidgetMacTest, NoopReparentNativeView) { NSWindow* parent = MakeBorderlessNativeParent(); Widget* dialog = views::DialogDelegate::CreateDialogWidget( new DialogDelegateView, nullptr, [parent contentView]); - BridgedNativeWidgetHostImpl* bridge_host = - BridgedNativeWidgetHostImpl::GetFromNativeWindow( + NativeWidgetMacNSWindowHost* window_host = + NativeWidgetMacNSWindowHost::GetFromNativeWindow( dialog->GetNativeWindow()); - EXPECT_EQ(bridge_host->parent()->native_widget_mac()->GetNativeWindow(), + EXPECT_EQ(window_host->parent()->native_widget_mac()->GetNativeWindow(), parent); Widget::ReparentNativeView(dialog->GetNativeView(), [parent contentView]); - EXPECT_EQ(bridge_host->parent()->native_widget_mac()->GetNativeWindow(), + EXPECT_EQ(window_host->parent()->native_widget_mac()->GetNativeWindow(), parent); [parent close]; @@ -1636,13 +1636,13 @@ TEST_F(NativeWidgetMacTest, NoopReparentNativeView) { parent = parent_widget->GetNativeWindow().GetNativeNSWindow(); dialog = views::DialogDelegate::CreateDialogWidget( new DialogDelegateView, nullptr, [parent contentView]); - bridge_host = BridgedNativeWidgetHostImpl::GetFromNativeWindow( + window_host = NativeWidgetMacNSWindowHost::GetFromNativeWindow( dialog->GetNativeWindow()); - EXPECT_EQ(bridge_host->parent()->native_widget_mac()->GetNativeWindow(), + EXPECT_EQ(window_host->parent()->native_widget_mac()->GetNativeWindow(), parent); Widget::ReparentNativeView(dialog->GetNativeView(), [parent contentView]); - EXPECT_EQ(bridge_host->parent()->native_widget_mac()->GetNativeWindow(), + EXPECT_EQ(window_host->parent()->native_widget_mac()->GetNativeWindow(), parent); parent_widget->CloseNow(); @@ -2202,9 +2202,9 @@ class NativeWidgetMacFullKeyboardAccessTest : public NativeWidgetMacTest { NativeWidgetMacTest::SetUp(); widget_ = CreateTopLevelPlatformWidget(); - bridge_ = BridgedNativeWidgetHostImpl::GetFromNativeWindow( + bridge_ = NativeWidgetMacNSWindowHost::GetFromNativeWindow( widget_->GetNativeWindow()) - ->bridge_impl(); + ->GetInProcessNSWindowBridge(); fake_full_keyboard_access_ = ui::test::ScopedFakeFullKeyboardAccess::GetInstance(); DCHECK(fake_full_keyboard_access_); @@ -2217,7 +2217,7 @@ class NativeWidgetMacFullKeyboardAccessTest : public NativeWidgetMacTest { } Widget* widget_ = nullptr; - BridgedNativeWidgetImpl* bridge_ = nullptr; + remote_cocoa::NativeWidgetNSWindowBridge* bridge_ = nullptr; ui::test::ScopedFakeFullKeyboardAccess* fake_full_keyboard_access_ = nullptr; }; diff --git a/chromium/ui/views/widget/native_widget_private.cc b/chromium/ui/views/widget/native_widget_private.cc index 1983a9cdbc6..a5933a86da9 100644 --- a/chromium/ui/views/widget/native_widget_private.cc +++ b/chromium/ui/views/widget/native_widget_private.cc @@ -26,7 +26,5 @@ void NativeWidgetPrivate::ShowEmojiPanel() { ui::ShowEmojiPanel(); } -void NativeWidgetPrivate::OnCanActivateChanged() {} - } // namespace internal } // namespace views diff --git a/chromium/ui/views/widget/native_widget_private.h b/chromium/ui/views/widget/native_widget_private.h index 27f114fb516..84acc003c84 100644 --- a/chromium/ui/views/widget/native_widget_private.h +++ b/chromium/ui/views/widget/native_widget_private.h @@ -15,7 +15,6 @@ #include "ui/views/widget/widget.h" namespace gfx { -class FontList; class ImageSkia; class Rect; } @@ -70,8 +69,6 @@ class VIEWS_EXPORT NativeWidgetPrivate : public NativeWidget { static void ReparentNativeView(gfx::NativeView native_view, gfx::NativeView new_parent); - static gfx::FontList GetWindowTitleFontList(); - // Returns the NativeView with capture, otherwise NULL if there is no current // capture set, or if |native_view| has no root. static gfx::NativeView GetGlobalCapture(gfx::NativeView native_view); @@ -190,8 +187,8 @@ class VIEWS_EXPORT NativeWidgetPrivate : public NativeWidget { virtual void Activate() = 0; virtual void Deactivate() = 0; virtual bool IsActive() const = 0; - virtual void SetAlwaysOnTop(bool always_on_top) = 0; - virtual bool IsAlwaysOnTop() const = 0; + virtual void SetZOrderLevel(ui::ZOrderLevel order) = 0; + virtual ui::ZOrderLevel GetZOrderLevel() const = 0; virtual void SetVisibleOnAllWorkspaces(bool always_visible) = 0; virtual bool IsVisibleOnAllWorkspaces() const = 0; virtual void Maximize() = 0; @@ -207,7 +204,7 @@ class VIEWS_EXPORT NativeWidgetPrivate : public NativeWidget { virtual void SetAspectRatio(const gfx::SizeF& aspect_ratio) = 0; virtual void FlashFrame(bool flash) = 0; virtual void RunShellDrag(View* view, - const ui::OSExchangeData& data, + std::unique_ptr<ui::OSExchangeData> data, const gfx::Point& location, int operation, ui::DragDropTypes::DragEventSource source) = 0; @@ -233,7 +230,6 @@ class VIEWS_EXPORT NativeWidgetPrivate : public NativeWidget { virtual bool IsTranslucentWindowOpacitySupported() const = 0; virtual ui::GestureRecognizer* GetGestureRecognizer() = 0; virtual void OnSizeConstraintsChanged() = 0; - virtual void OnCanActivateChanged(); // Returns an internal name that matches the name of the associated Widget. virtual std::string GetName() const = 0; diff --git a/chromium/ui/views/widget/root_view.cc b/chromium/ui/views/widget/root_view.cc index 9a79e764159..836fb739a14 100644 --- a/chromium/ui/views/widget/root_view.cc +++ b/chromium/ui/views/widget/root_view.cc @@ -141,9 +141,6 @@ class PostEventDispatchHandler : public ui::EventHandler { DISALLOW_COPY_AND_ASSIGN(PostEventDispatchHandler); }; -// static -const char RootView::kViewClassName[] = "RootView"; - //////////////////////////////////////////////////////////////////////////////// // RootView, public: @@ -175,8 +172,7 @@ RootView::RootView(Widget* widget) RootView::~RootView() { // If we have children remove them explicitly so to make sure a remove // notification is sent for each one of them. - if (!children().empty()) - RemoveAllChildViews(true); + RemoveAllChildViews(true); } // Tree operations ------------------------------------------------------------- @@ -319,10 +315,6 @@ bool RootView::IsDrawn() const { return GetVisible(); } -const char* RootView::GetClassName() const { - return kViewClassName; -} - void RootView::SchedulePaintInRect(const gfx::Rect& rect) { if (layer()) { layer()->SchedulePaint(rect); @@ -616,7 +608,7 @@ void RootView::GetAccessibleNodeData(ui::AXNodeData* node_data) { void RootView::UpdateParentLayer() { if (layer()) - ReparentLayer(gfx::Vector2d(GetMirroredX(), y()), widget_->GetLayer()); + ReparentLayer(widget_->GetLayer()); } //////////////////////////////////////////////////////////////////////////////// @@ -767,5 +759,8 @@ ui::EventDispatchDetails RootView::PostDispatchEvent(ui::EventTarget* target, return details; } +BEGIN_METADATA(RootView) +METADATA_PARENT_CLASS(View) +END_METADATA() } // namespace internal } // namespace views diff --git a/chromium/ui/views/widget/root_view.h b/chromium/ui/views/widget/root_view.h index 224a1a4db6c..02d38107754 100644 --- a/chromium/ui/views/widget/root_view.h +++ b/chromium/ui/views/widget/root_view.h @@ -52,7 +52,7 @@ class VIEWS_EXPORT RootView : public View, public FocusTraversable, public ui::EventProcessor { public: - static const char kViewClassName[]; + METADATA_HEADER(RootView); // Creation and lifetime ----------------------------------------------------- explicit RootView(Widget* widget); @@ -107,7 +107,6 @@ class VIEWS_EXPORT RootView : public View, const Widget* GetWidget() const override; Widget* GetWidget() override; bool IsDrawn() const override; - const char* GetClassName() const override; void SchedulePaintInRect(const gfx::Rect& rect) override; bool OnMousePressed(const ui::MouseEvent& event) override; bool OnMouseDragged(const ui::MouseEvent& event) override; diff --git a/chromium/ui/views/widget/widget.cc b/chromium/ui/views/widget/widget.cc index 0f01bf0c73c..0cb75272c2c 100644 --- a/chromium/ui/views/widget/widget.cc +++ b/chromium/ui/views/widget/widget.cc @@ -13,7 +13,6 @@ #include "base/macros.h" #include "base/strings/utf_string_conversions.h" #include "base/trace_event/trace_event.h" -#include "ui/aura/window.h" #include "ui/base/cursor/cursor.h" #include "ui/base/default_style.h" #include "ui/base/default_theme_provider.h" @@ -150,7 +149,6 @@ Widget::InitParams::InitParams(Type type) opacity(INFER_OPACITY), accept_events(true), activatable(ACTIVATABLE_DEFAULT), - keep_on_top(type == TYPE_MENU || type == TYPE_DRAG), visible_on_all_workspaces(false), ownership(NATIVE_WIDGET_OWNS_WIDGET), mirror_origin_in_rtl(false), @@ -178,6 +176,22 @@ bool Widget::InitParams::CanActivate() const { type != InitParams::TYPE_DRAG; } +ui::ZOrderLevel Widget::InitParams::EffectiveZOrderLevel() const { + if (z_order.has_value()) + return z_order.value(); + + switch (type) { + case TYPE_MENU: + return ui::ZOrderLevel::kFloatingWindow; + break; + case TYPE_DRAG: + return ui::ZOrderLevel::kFloatingUIElement; + break; + default: + return ui::ZOrderLevel::kNormal; + } +} + //////////////////////////////////////////////////////////////////////////////// // Widget, public: @@ -629,6 +643,8 @@ void Widget::Show() { const ui::Layer* layer = GetLayer(); TRACE_EVENT1("views", "Widget::Show", "layer", layer ? layer->name() : "none"); + ui::WindowShowState preferred_show_state = + CanActivate() ? ui::SHOW_STATE_NORMAL : ui::SHOW_STATE_INACTIVE; if (non_client_view_) { // While initializing, the kiosk mode will go to full screen before the // widget gets shown. In that case we stay in full screen mode, regardless @@ -645,11 +661,9 @@ void Widget::Show() { // |saved_show_state_| only applies the first time the window is shown. // If we don't reset the value the window may be shown maximized every time // it is subsequently shown after being hidden. - saved_show_state_ = ui::SHOW_STATE_NORMAL; + saved_show_state_ = preferred_show_state; } else { - native_widget_->Show( - CanActivate() ? ui::SHOW_STATE_NORMAL : ui::SHOW_STATE_INACTIVE, - gfx::Rect()); + native_widget_->Show(preferred_show_state, gfx::Rect()); } } @@ -683,12 +697,12 @@ bool Widget::IsActive() const { return native_widget_->IsActive(); } -void Widget::SetAlwaysOnTop(bool on_top) { - native_widget_->SetAlwaysOnTop(on_top); +void Widget::SetZOrderLevel(ui::ZOrderLevel order) { + native_widget_->SetZOrderLevel(order); } -bool Widget::IsAlwaysOnTop() const { - return native_widget_->IsAlwaysOnTop(); +ui::ZOrderLevel Widget::GetZOrderLevel() const { + return native_widget_->GetZOrderLevel(); } void Widget::SetVisibleOnAllWorkspaces(bool always_visible) { @@ -808,7 +822,7 @@ ui::InputMethod* Widget::GetInputMethod() { } void Widget::RunShellDrag(View* view, - const ui::OSExchangeData& data, + std::unique_ptr<ui::OSExchangeData> data, const gfx::Point& location, int operation, ui::DragDropTypes::DragEventSource source) { @@ -819,7 +833,8 @@ void Widget::RunShellDrag(View* view, observer.OnWidgetDragWillStart(this); WidgetDeletionObserver widget_deletion_observer(this); - native_widget_->RunShellDrag(view, data, location, operation, source); + native_widget_->RunShellDrag(view, std::move(data), location, operation, + source); // The widget may be destroyed during the drag operation. if (!widget_deletion_observer.IsWidgetAlive()) @@ -1044,11 +1059,6 @@ void Widget::OnSizeConstraintsChanged() { void Widget::OnOwnerClosing() {} -void Widget::OnCanActivateChanged() { - if (native_widget_) - native_widget_->OnCanActivateChanged(); -} - std::string Widget::GetName() const { return native_widget_->GetName(); } @@ -1515,6 +1525,10 @@ void Widget::DestroyRootView() { focus_manager_->SetFocusedView(nullptr); NotifyWillRemoveView(root_view_.get()); non_client_view_ = nullptr; + // Remove all children before the unique_ptr reset so that + // GetWidget()->GetRootView() doesn't return nullptr while the views hierarchy + // is being torn down. + root_view_->RemoveAllChildViews(true); root_view_.reset(); } @@ -1638,8 +1652,8 @@ void Widget::UnlockPaintAsActive() { } void Widget::UpdatePaintAsActiveState(bool paint_as_active) { - if (non_client_view()) - non_client_view()->frame_view()->PaintAsActiveChanged(paint_as_active); + if (non_client_view_) + non_client_view_->frame_view()->PaintAsActiveChanged(paint_as_active); if (widget_delegate()) widget_delegate()->OnPaintAsActiveChanged(paint_as_active); } diff --git a/chromium/ui/views/widget/widget.h b/chromium/ui/views/widget/widget.h index 8a073d74f34..83ba608199b 100644 --- a/chromium/ui/views/widget/widget.h +++ b/chromium/ui/views/widget/widget.h @@ -217,6 +217,10 @@ class VIEWS_EXPORT Widget : public internal::NativeWidgetDelegate, // case where |activatable| is |ACTIVATABLE_DEFAULT|. bool CanActivate() const; + // Returns the z-order level, based on the overriding |z_order| but also + // taking into account special levels due to |type|. + ui::ZOrderLevel EffectiveZOrderLevel() const; + Type type; // If null, a default implementation will be constructed. The default // implementation deletes itself when the Widget closes. @@ -234,7 +238,7 @@ class VIEWS_EXPORT Widget : public internal::NativeWidgetDelegate, WindowOpacity opacity; bool accept_events; Activatable activatable; - bool keep_on_top; + base::Optional<ui::ZOrderLevel> z_order; bool visible_on_all_workspaces; // See Widget class comment above. Ownership ownership; @@ -294,7 +298,6 @@ class VIEWS_EXPORT Widget : public internal::NativeWidgetDelegate, std::string wm_class_class; // If true then the widget uses software compositing. Defaults to false. - // Only used on Windows. bool force_software_compositing; // Used if widget is not activatable to do determine if mouse events should @@ -537,12 +540,11 @@ class VIEWS_EXPORT Widget : public internal::NativeWidgetDelegate, // Returns whether the Widget is the currently active window. virtual bool IsActive() const; - // Sets the widget to be on top of all other widgets in the windowing system. - void SetAlwaysOnTop(bool on_top); + // Sets the z-order of the widget. This only applies to top-level widgets. + void SetZOrderLevel(ui::ZOrderLevel order); - // Returns whether the widget has been set to be on top of most other widgets - // in the windowing system. - bool IsAlwaysOnTop() const; + // Gets the z-order of the widget. This only applies to top-level widgets. + ui::ZOrderLevel GetZOrderLevel() const; // Sets the widget to be visible on all work spaces. void SetVisibleOnAllWorkspaces(bool always_visible); @@ -627,7 +629,7 @@ class VIEWS_EXPORT Widget : public internal::NativeWidgetDelegate, // OnDragDone() is called on it. |location| is in the widget's coordinate // system. void RunShellDrag(View* view, - const ui::OSExchangeData& data, + std::unique_ptr<ui::OSExchangeData> data, const gfx::Point& location, int operation, ui::DragDropTypes::DragEventSource source); @@ -814,9 +816,6 @@ class VIEWS_EXPORT Widget : public internal::NativeWidgetDelegate, // Called when the delegate's CanResize or CanMaximize changes. void OnSizeConstraintsChanged(); - // Called when WidgetDelegate::CanActivate() changes. - void OnCanActivateChanged(); - // Notification that our owner is closing. // NOTE: this is not invoked for aura as it's currently not needed there. // Under aura menus close by way of activation getting reset when the owner diff --git a/chromium/ui/views/widget/widget_delegate.cc b/chromium/ui/views/widget/widget_delegate.cc index 570fefddf39..703ee49a88e 100644 --- a/chromium/ui/views/widget/widget_delegate.cc +++ b/chromium/ui/views/widget/widget_delegate.cc @@ -23,16 +23,7 @@ WidgetDelegate::~WidgetDelegate() { } void WidgetDelegate::SetCanActivate(bool can_activate) { - if (can_activate == can_activate_) - return; - - const bool previous_value = CanActivate(); can_activate_ = can_activate; - if (previous_value != CanActivate()) { - Widget* widget = GetWidget(); - if (widget) - widget->OnCanActivateChanged(); - } } void WidgetDelegate::OnWidgetMove() { @@ -190,8 +181,6 @@ bool WidgetDelegate::ShouldDescendIntoChildForEventHandling( //////////////////////////////////////////////////////////////////////////////// // WidgetDelegateView: -// static -const char WidgetDelegateView::kViewClassName[] = "WidgetDelegateView"; WidgetDelegateView::WidgetDelegateView() { // A WidgetDelegate should be deleted on DeleteDelegate. @@ -216,8 +205,8 @@ views::View* WidgetDelegateView::GetContentsView() { return this; } -const char* WidgetDelegateView::GetClassName() const { - return kViewClassName; -} +BEGIN_METADATA(WidgetDelegateView) +METADATA_PARENT_CLASS(View) +END_METADATA() } // namespace views diff --git a/chromium/ui/views/widget/widget_delegate.h b/chromium/ui/views/widget/widget_delegate.h index 1f827524f4b..77585733e69 100644 --- a/chromium/ui/views/widget/widget_delegate.h +++ b/chromium/ui/views/widget/widget_delegate.h @@ -212,8 +212,7 @@ class VIEWS_EXPORT WidgetDelegate { // view's hierarchy and is expected to be deleted on DeleteDelegate call. class VIEWS_EXPORT WidgetDelegateView : public WidgetDelegate, public View { public: - // Internal class name. - static const char kViewClassName[]; + METADATA_HEADER(WidgetDelegateView); WidgetDelegateView(); ~WidgetDelegateView() override; @@ -224,9 +223,6 @@ class VIEWS_EXPORT WidgetDelegateView : public WidgetDelegate, public View { const Widget* GetWidget() const override; views::View* GetContentsView() override; - // View: - const char* GetClassName() const override; - private: DISALLOW_COPY_AND_ASSIGN(WidgetDelegateView); }; diff --git a/chromium/ui/views/widget/widget_hwnd_utils.cc b/chromium/ui/views/widget/widget_hwnd_utils.cc index 5bcb8d8b9ba..9edfa2c00c4 100644 --- a/chromium/ui/views/widget/widget_hwnd_utils.cc +++ b/chromium/ui/views/widget/widget_hwnd_utils.cc @@ -45,7 +45,7 @@ void CalculateWindowStylesFromInitParams( DCHECK_NE(Widget::InitParams::ACTIVATABLE_DEFAULT, params.activatable); if (params.activatable == Widget::InitParams::ACTIVATABLE_NO) *ex_style |= WS_EX_NOACTIVATE; - if (params.keep_on_top) + if (params.EffectiveZOrderLevel() != ui::ZOrderLevel::kNormal) *ex_style |= WS_EX_TOPMOST; if (params.mirror_origin_in_rtl) *ex_style |= l10n_util::GetExtendedTooltipStyles(); @@ -109,6 +109,9 @@ void CalculateWindowStylesFromInitParams( case Widget::InitParams::TYPE_MENU: *style |= WS_POPUP; break; + case Widget::InitParams::TYPE_TOOLTIP: + *style |= WS_POPUP; + break; default: NOTREACHED(); } diff --git a/chromium/ui/views/widget/widget_interactive_uitest.cc b/chromium/ui/views/widget/widget_interactive_uitest.cc index afd98271104..75467723dfd 100644 --- a/chromium/ui/views/widget/widget_interactive_uitest.cc +++ b/chromium/ui/views/widget/widget_interactive_uitest.cc @@ -19,7 +19,6 @@ #include "base/timer/timer.h" #include "base/win/windows_version.h" #include "build/build_config.h" -#include "ui/aura/window.h" #include "ui/base/ime/input_method.h" #include "ui/base/ime/text_input_client.h" #include "ui/base/resource/resource_bundle.h" @@ -45,6 +44,7 @@ #include "ui/wm/public/activation_client.h" #if defined(OS_WIN) +#include "ui/aura/window.h" #include "ui/aura/window_tree_host.h" #include "ui/views/widget/desktop_aura/desktop_native_widget_aura.h" #include "ui/views/win/hwnd_util.h" diff --git a/chromium/ui/views/widget/widget_unittest.cc b/chromium/ui/views/widget/widget_unittest.cc index 9571a4f9477..64a5731a565 100644 --- a/chromium/ui/views/widget/widget_unittest.cc +++ b/chromium/ui/views/widget/widget_unittest.cc @@ -588,8 +588,8 @@ TEST_P(WidgetWithDestroyedNativeViewTest, Test) { widget.Activate(); widget.Deactivate(); widget.IsActive(); - widget.SetAlwaysOnTop(true); - widget.IsAlwaysOnTop(); + widget.SetZOrderLevel(ui::ZOrderLevel::kNormal); + widget.GetZOrderLevel(); widget.Maximize(); widget.Minimize(); widget.Restore(); @@ -3692,14 +3692,14 @@ TEST_F(DesktopWidgetTest, DISABLED_DestroyInSysCommandNCLButtonDownOnCaption) { #endif -// Test that SetAlwaysOnTop and IsAlwaysOnTop are consistent. -TEST_F(WidgetTest, AlwaysOnTop) { +// Test that the z-order levels round-trip. +TEST_F(WidgetTest, ZOrderLevel) { WidgetAutoclosePtr widget(CreateTopLevelNativeWidget()); - EXPECT_FALSE(widget->IsAlwaysOnTop()); - widget->SetAlwaysOnTop(true); - EXPECT_TRUE(widget->IsAlwaysOnTop()); - widget->SetAlwaysOnTop(false); - EXPECT_FALSE(widget->IsAlwaysOnTop()); + EXPECT_EQ(ui::ZOrderLevel::kNormal, widget->GetZOrderLevel()); + widget->SetZOrderLevel(ui::ZOrderLevel::kFloatingWindow); + EXPECT_EQ(ui::ZOrderLevel::kFloatingWindow, widget->GetZOrderLevel()); + widget->SetZOrderLevel(ui::ZOrderLevel::kNormal); + EXPECT_EQ(ui::ZOrderLevel::kNormal, widget->GetZOrderLevel()); } namespace { @@ -4201,7 +4201,7 @@ class CompositingWidgetTest : public DesktopWidgetTest { const Widget::InitParams::WindowOpacity opacity) { for (const auto& widget_type : widget_types_) { #if defined(OS_MACOSX) - // Tooltips are native on Mac. See BridgedNativeWidgetImpl::Init. + // Tooltips are native on Mac. See NativeWidgetNSWindowBridge::Init. if (widget_type == Widget::InitParams::TYPE_TOOLTIP) continue; #elif defined(OS_WIN) diff --git a/chromium/ui/views/widget/widget_utils_mac.mm b/chromium/ui/views/widget/widget_utils_mac.mm index 8d843b8352b..6a3b1dce6ef 100644 --- a/chromium/ui/views/widget/widget_utils_mac.mm +++ b/chromium/ui/views/widget/widget_utils_mac.mm @@ -4,13 +4,13 @@ #include "ui/views/widget/widget_utils_mac.h" -#import "components/remote_cocoa/app_shim/bridged_native_widget_impl.h" +#import "components/remote_cocoa/app_shim/native_widget_ns_window_bridge.h" namespace views { gfx::Size GetWindowSizeForClientSize(Widget* widget, const gfx::Size& size) { DCHECK(widget); - return BridgedNativeWidgetImpl::GetWindowSizeForClientSize( + return remote_cocoa::NativeWidgetNSWindowBridge::GetWindowSizeForClientSize( widget->GetNativeWindow().GetNativeNSWindow(), size); } diff --git a/chromium/ui/views/widget/window_reorderer.cc b/chromium/ui/views/widget/window_reorderer.cc index 003f6549333..ce52ff296c5 100644 --- a/chromium/ui/views/widget/window_reorderer.cc +++ b/chromium/ui/views/widget/window_reorderer.cc @@ -168,19 +168,24 @@ void WindowReorderer::ReorderChildWindows() { // windows not associated to a view are stacked above windows with an // associated view. for (View* view : base::Reversed(view_with_layer_order)) { - ui::Layer* layer = view->layer(); + std::vector<ui::Layer*> layers; aura::Window* window = nullptr; auto hosted_window_it = hosted_windows.find(view); if (hosted_window_it != hosted_windows.end()) { window = hosted_window_it->second; - layer = window->layer(); + layers.push_back(window->layer()); + } else { + layers = view->GetLayersInOrder(); + std::reverse(layers.begin(), layers.end()); } - DCHECK(layer); + DCHECK(!layers.empty()); if (window) parent_window_->StackChildAtBottom(window); - children_layer_order.emplace_back(layer); + + for (ui::Layer* layer : layers) + children_layer_order.emplace_back(layer); } std::reverse(children_layer_order.begin(), children_layer_order.end()); parent_window_->layer()->StackChildrenAtBottom(children_layer_order); diff --git a/chromium/ui/views/widget/window_reorderer_unittest.cc b/chromium/ui/views/widget/window_reorderer_unittest.cc index 856ee7e112e..32b8e67221a 100644 --- a/chromium/ui/views/widget/window_reorderer_unittest.cc +++ b/chromium/ui/views/widget/window_reorderer_unittest.cc @@ -259,5 +259,43 @@ TEST_F(WindowReordererTest, HostViewParentHasLayer) { parent->Close(); } +// Test that a layer added beneath a view is restacked correctly. +TEST_F(WindowReordererTest, ViewWithLayerBeneath) { + std::unique_ptr<Widget> parent( + CreateControlWidget(root_window(), gfx::Rect(0, 0, 100, 100))); + parent->Show(); + + aura::Window* parent_window = parent->GetNativeWindow(); + + View* contents_view = new View; + parent->SetContentsView(contents_view); + + View* view_with_layer_beneath = + contents_view->AddChildView(std::make_unique<View>()); + ui::Layer layer_beneath; + view_with_layer_beneath->AddLayerBeneathView(&layer_beneath); + + ASSERT_NE(nullptr, view_with_layer_beneath->layer()); + view_with_layer_beneath->layer()->set_name("view"); + layer_beneath.set_name("beneath"); + + // Verify that the initial ordering is correct. + EXPECT_EQ("beneath view", + ui::test::ChildLayerNamesAsString(*parent_window->layer())); + + // Add a hosted window to make WindowReorderer::ReorderChildWindows() restack + // layers. + std::unique_ptr<Widget> child_widget( + CreateControlWidget(parent_window, gfx::Rect(gfx::Rect(0, 0, 50, 50)))); + SetWindowAndLayerName(child_widget->GetNativeView(), "child_widget"); + child_widget->Show(); + View* host_view = contents_view->AddChildView(std::make_unique<View>()); + child_widget->GetNativeView()->SetProperty(kHostViewKey, host_view); + + // Verify the new order is correct. + EXPECT_EQ("beneath view child_widget", + ui::test::ChildLayerNamesAsString(*parent_window->layer())); +} + } // namespace } // namespace views diff --git a/chromium/ui/views/win/hwnd_message_handler.cc b/chromium/ui/views/win/hwnd_message_handler.cc index abde4d03cb3..4d674d15da3 100644 --- a/chromium/ui/views/win/hwnd_message_handler.cc +++ b/chromium/ui/views/win/hwnd_message_handler.cc @@ -18,6 +18,7 @@ #include "base/location.h" #include "base/macros.h" #include "base/message_loop/message_loop_current.h" +#include "base/metrics/histogram_functions.h" #include "base/single_thread_task_runner.h" #include "base/threading/thread_task_runner_handle.h" #include "base/time/time.h" @@ -61,6 +62,7 @@ #include "ui/views/widget/widget_hwnd_utils.h" #include "ui/views/win/fullscreen_handler.h" #include "ui/views/win/hwnd_message_handler_delegate.h" +#include "ui/views/win/hwnd_util.h" #include "ui/views/win/scoped_fullscreen_visibility.h" namespace views { @@ -275,6 +277,49 @@ HitTest GetWindowResizeHitTest(UINT param) { } } +void RecordDeltaBetweenTimeNowAndPerformanceCountHistogram( + base::TimeTicks event_time, + UINT64 performance_count, + POINTER_INPUT_TYPE pointer_input_type, + bool is_session_remote) { + // In remote session, sometimes |performance_count| drifts + // substantially in future compared to |TimeTicks::Now()| - enough to skew the + // histogram data. Additionally, user input over remote session already has + // lag, so user is less likely to be sensitive to the responsiveness of input + // in such case. So we are less concerned capturing the deltas in remote + // session scenario. + if (base::TimeTicks::IsHighResolution() && !is_session_remote) { + base::TimeTicks event_time_from_pointer = + base::TimeTicks::FromQPCValue(performance_count); + + double delta_between_event_timestamps = + (event_time - event_time_from_pointer).InMicrosecondsF(); + std::string pointer_type; + switch (pointer_input_type) { + case PT_PEN: + pointer_type = "Pen"; + break; + case PT_TOUCH: + pointer_type = "Touch"; + break; + default: + NOTREACHED(); + } + + std::string number_sign = + delta_between_event_timestamps >= 0 ? "Positive" : "Negative"; + base::TimeDelta delta_sample_value = base::TimeDelta::FromMicroseconds( + std::abs(delta_between_event_timestamps)); + + base::UmaHistogramCustomMicrosecondsTimes( + base::StringPrintf("Event.%s.InputEventTimeStamp." + "DeltaBetweenTimeNowAndPerformanceCount.%s", + pointer_type.c_str(), number_sign.c_str()), + delta_sample_value, base::TimeDelta::FromMicroseconds(1), + base::TimeDelta::FromMilliseconds(30), 30); + } +} + constexpr int kTouchDownContextResetTimeout = 500; // Windows does not flag synthesized mouse messages from touch or pen in all @@ -407,6 +452,7 @@ HWNDMessageHandler::HWNDMessageHandler(HWNDMessageHandlerDelegate* delegate, pointer_events_for_touch_(::features::IsUsingWMPointerForTouch()), precision_touchpad_scroll_phase_enabled_(base::FeatureList::IsEnabled( ::features::kPrecisionTouchpadScrollPhase)), + is_remote_session_(base::win::IsCurrentSessionRemote()), autohide_factory_(this) {} HWNDMessageHandler::~HWNDMessageHandler() { @@ -445,6 +491,16 @@ void HWNDMessageHandler::Init(HWND parent, const gfx::Rect& bounds) { DCHECK(delegate_->GetHWNDMessageDelegateInputMethod()); delegate_->GetHWNDMessageDelegateInputMethod()->AddObserver(this); + // The usual way for UI Automation to obtain a fragment root is through + // WM_GETOBJECT. However, if there's a relation such as "Controller For" + // between element A in one window and element B in another window, UIA might + // call element A to discover the relation, receive a pointer to element B, + // then ask element B for its fragment root, without having sent WM_GETOBJECT + // to element B's window. + // So we create the fragment root now to ensure it's ready if asked for. + if (::switches::IsExperimentalAccessibilityPlatformUIAEnabled()) + ax_fragment_root_ = std::make_unique<ui::AXFragmentRootWin>(hwnd(), this); + // Disable pen flicks (http://crbug.com/506977) base::win::DisableFlicks(hwnd()); } @@ -1216,6 +1272,14 @@ void HWNDMessageHandler::ApplyPanGestureFlingEnd() { ui::ScrollEventPhase::kNone); } +gfx::NativeViewAccessible HWNDMessageHandler::GetChildOfAXFragmentRoot() { + return delegate_->GetNativeViewAccessible(); +} + +gfx::NativeViewAccessible HWNDMessageHandler::GetParentOfAXFragmentRoot() { + return nullptr; +} + //////////////////////////////////////////////////////////////////////////////// // HWNDMessageHandler, private: @@ -1769,9 +1833,6 @@ LRESULT HWNDMessageHandler::OnGetObject(UINT message, if (is_uia_request && ::switches::IsExperimentalAccessibilityPlatformUIAEnabled()) { // Retrieve UIA object for the root view. - if (!ax_fragment_root_) - ax_fragment_root_ = std::make_unique<ui::AXFragmentRootWin>( - hwnd(), delegate_->GetNativeViewAccessible()); Microsoft::WRL::ComPtr<IRawElementProviderSimple> root; ax_fragment_root_->GetNativeViewAccessible()->QueryInterface( IID_PPV_ARGS(&root)); @@ -2401,6 +2462,7 @@ void HWNDMessageHandler::OnSettingChange(UINT flags, const wchar_t* section) { if (flags == SPI_SETWORKAREA) delegate_->HandleWorkAreaChanged(); SetMsgHandled(FALSE); + is_remote_session_ = base::win::IsCurrentSessionRemote(); } // If the work area is changing, then it could be as a result of the taskbar @@ -2837,6 +2899,8 @@ LRESULT HWNDMessageHandler::HandleMouseEventInternal(UINT message, } if (message == WM_RBUTTONUP && is_right_mouse_pressed_on_caption_) { + // TODO(pkasting): Maybe handle this in DesktopWindowTreeHostWin, where we + // handle alt-space, or in the frame itself. is_right_mouse_pressed_on_caption_ = false; ReleaseCapture(); // |point| is in window coordinates, but WM_NCHITTEST and TrackPopupMenu() @@ -2846,7 +2910,7 @@ LRESULT HWNDMessageHandler::HandleMouseEventInternal(UINT message, w_param = SendMessage(hwnd(), WM_NCHITTEST, 0, MAKELPARAM(screen_point.x, screen_point.y)); if (w_param == HTCAPTION || w_param == HTSYSMENU) { - gfx::ShowSystemMenuAtPoint(hwnd(), gfx::Point(screen_point)); + ShowSystemMenuAtScreenPixelLocation(hwnd(), gfx::Point(screen_point)); return 0; } } else if (message == WM_NCLBUTTONDOWN && @@ -3016,7 +3080,7 @@ LRESULT HWNDMessageHandler::HandlePointerEventTypeTouch(UINT message, ui::GetModifiersFromKeyState()); event.latency()->AddLatencyNumberWithTimestamp( - ui::INPUT_EVENT_LATENCY_ORIGINAL_COMPONENT, event_time, 1); + ui::INPUT_EVENT_LATENCY_ORIGINAL_COMPONENT, event_time); // Release the pointer id for touch release events. if (event_type == ui::ET_TOUCH_RELEASED) @@ -3027,6 +3091,10 @@ LRESULT HWNDMessageHandler::HandlePointerEventTypeTouch(UINT message, base::WeakPtr<HWNDMessageHandler> ref(msg_handler_weak_factory_.GetWeakPtr()); delegate_->HandleTouchEvent(&event); + RecordDeltaBetweenTimeNowAndPerformanceCountHistogram( + event_time, pointer_info.PerformanceCount, pointer_info.pointerType, + is_remote_session_); + if (ref) { // Mark touch released events handled. These will usually turn into tap // gestures, and doing this avoids propagating the event to other windows. @@ -3076,12 +3144,16 @@ LRESULT HWNDMessageHandler::HandlePointerEventTypePen(UINT message, // window, so use the weak ptr to check if destruction occured or not. base::WeakPtr<HWNDMessageHandler> ref(msg_handler_weak_factory_.GetWeakPtr()); if (event) { - if (event->IsTouchEvent()) + if (event->IsTouchEvent()) { delegate_->HandleTouchEvent(event->AsTouchEvent()); - else if (event->IsMouseEvent()) + RecordDeltaBetweenTimeNowAndPerformanceCountHistogram( + event->time_stamp(), pointer_pen_info.pointerInfo.PerformanceCount, + pointer_pen_info.pointerInfo.pointerType, is_remote_session_); + } else if (event->IsMouseEvent()) { delegate_->HandleMouseEvent(event->AsMouseEvent()); - else + } else { NOTREACHED(); + } last_touch_or_pen_message_time_ = ::GetMessageTime(); } @@ -3172,7 +3244,7 @@ void HWNDMessageHandler::GenerateTouchEvent(ui::EventType event_type, event.set_flags(ui::GetModifiersFromKeyState()); event.latency()->AddLatencyNumberWithTimestamp( - ui::INPUT_EVENT_LATENCY_ORIGINAL_COMPONENT, time_stamp, 1); + ui::INPUT_EVENT_LATENCY_ORIGINAL_COMPONENT, time_stamp); touch_events->push_back(event); } diff --git a/chromium/ui/views/win/hwnd_message_handler.h b/chromium/ui/views/win/hwnd_message_handler.h index 060aa2dc02a..7c8f2eb0019 100644 --- a/chromium/ui/views/win/hwnd_message_handler.h +++ b/chromium/ui/views/win/hwnd_message_handler.h @@ -22,6 +22,7 @@ #include "base/win/scoped_gdi_object.h" #include "base/win/win_util.h" #include "ui/accessibility/ax_enums.mojom.h" +#include "ui/accessibility/platform/ax_fragment_root_delegate_win.h" #include "ui/base/ime/input_method_observer.h" #include "ui/base/ui_base_types.h" #include "ui/base/win/window_event_target.h" @@ -81,7 +82,8 @@ constexpr int WM_WINDOWSIZINGFINISHED = WM_USER; // TODO(beng): This object should eventually *become* the WindowImpl. class VIEWS_EXPORT HWNDMessageHandler : public gfx::WindowImpl, public ui::InputMethodObserver, - public ui::WindowEventTarget { + public ui::WindowEventTarget, + public ui::AXFragmentRootDelegateWin { public: // See WindowImpl for details on |debugging_id|. HWNDMessageHandler(HWNDMessageHandlerDelegate* delegate, @@ -241,6 +243,10 @@ class VIEWS_EXPORT HWNDMessageHandler : public gfx::WindowImpl, void ApplyPanGestureFlingBegin() override; void ApplyPanGestureFlingEnd() override; + // Overridden from AXFragmentRootDelegateWin. + gfx::NativeViewAccessible GetChildOfAXFragmentRoot() override; + gfx::NativeViewAccessible GetParentOfAXFragmentRoot() override; + void ApplyPanGestureEvent(int scroll_x, int scroll_y, ui::EventMomentumPhase momentum_phase, @@ -759,6 +765,9 @@ class VIEWS_EXPORT HWNDMessageHandler : public gfx::WindowImpl, // the first message after frame type changes. bool needs_dwm_frame_clear_ = true; + // True if user is in remote session. + bool is_remote_session_; + // This is a map of the HMONITOR to full screeen window instance. It is safe // to keep a raw pointer to the HWNDMessageHandler instance as we track the // window destruction and ensure that the map is cleaned up. diff --git a/chromium/ui/views/win/hwnd_util.h b/chromium/ui/views/win/hwnd_util.h index 6e3e2f09ae0..1aaacb98715 100644 --- a/chromium/ui/views/win/hwnd_util.h +++ b/chromium/ui/views/win/hwnd_util.h @@ -5,6 +5,7 @@ #ifndef UI_VIEWS_WIN_HWND_UTIL_H_ #define UI_VIEWS_WIN_HWND_UTIL_H_ +#include "ui/gfx/geometry/point.h" #include "ui/gfx/geometry/rect.h" #include "ui/gfx/native_widget_types.h" #include "ui/views/views_export.h" @@ -28,6 +29,12 @@ VIEWS_EXPORT HWND HWNDForNativeWindow(const gfx::NativeWindow window); VIEWS_EXPORT gfx::Rect GetWindowBoundsForClientBounds( View* view, const gfx::Rect& client_bounds); + +// Shows |window|'s system menu (at a specified |point| in screen physical +// coordinates). +VIEWS_EXPORT void ShowSystemMenuAtScreenPixelLocation(HWND window, + const gfx::Point& point); + } // namespace views #endif // UI_VIEWS_WIN_HWND_UTIL_H_ diff --git a/chromium/ui/views/win/hwnd_util_aurawin.cc b/chromium/ui/views/win/hwnd_util_aurawin.cc index cbd06d0f505..f404e7e2ed3 100644 --- a/chromium/ui/views/win/hwnd_util_aurawin.cc +++ b/chromium/ui/views/win/hwnd_util_aurawin.cc @@ -4,6 +4,7 @@ #include "ui/views/win/hwnd_util.h" +#include "base/i18n/rtl.h" #include "ui/aura/window.h" #include "ui/aura/window_tree_host.h" #include "ui/views/widget/widget.h" @@ -44,4 +45,17 @@ gfx::Rect GetWindowBoundsForClientBounds(View* view, return client_bounds; } +void ShowSystemMenuAtScreenPixelLocation(HWND window, const gfx::Point& point) { + UINT flags = TPM_LEFTALIGN | TPM_TOPALIGN | TPM_RIGHTBUTTON | TPM_RETURNCMD; + if (base::i18n::IsRTL()) + flags |= TPM_RIGHTALIGN; + HMENU menu = GetSystemMenu(window, FALSE); + + const int command = + TrackPopupMenu(menu, flags, point.x(), point.y(), 0, window, nullptr); + + if (command) + SendMessage(window, WM_SYSCOMMAND, command, 0); +} + } // namespace views diff --git a/chromium/ui/views/win/pen_event_processor.cc b/chromium/ui/views/win/pen_event_processor.cc index 6e8f488afee..36de1decc92 100644 --- a/chromium/ui/views/win/pen_event_processor.cc +++ b/chromium/ui/views/win/pen_event_processor.cc @@ -199,7 +199,7 @@ std::unique_ptr<ui::Event> PenEventProcessor::GenerateTouchEvent( flags | ui::GetModifiersFromKeyState()); event->set_hovering(event_type == ui::ET_TOUCH_RELEASED); event->latency()->AddLatencyNumberWithTimestamp( - ui::INPUT_EVENT_LATENCY_ORIGINAL_COMPONENT, event_time, 1); + ui::INPUT_EVENT_LATENCY_ORIGINAL_COMPONENT, event_time); return event; } diff --git a/chromium/ui/views/window/client_view.cc b/chromium/ui/views/window/client_view.cc index 88842b61df8..646b1ea8f54 100644 --- a/chromium/ui/views/window/client_view.cc +++ b/chromium/ui/views/window/client_view.cc @@ -12,10 +12,6 @@ namespace views { -// static -const char ClientView::kViewClassName[] = - "ui/views/window/ClientView"; - /////////////////////////////////////////////////////////////////////////////// // ClientView, public: @@ -70,9 +66,6 @@ void ClientView::Layout() { contents_view_->SetBounds(0, 0, width(), height()); } -const char* ClientView::GetClassName() const { - return kViewClassName; -} void ClientView::GetAccessibleNodeData(ui::AXNodeData* node_data) { node_data->role = ax::mojom::Role::kClient; @@ -97,4 +90,8 @@ void ClientView::ViewHierarchyChanged( } } +BEGIN_METADATA(ClientView) +METADATA_PARENT_CLASS(View) +END_METADATA() + } // namespace views diff --git a/chromium/ui/views/window/client_view.h b/chromium/ui/views/window/client_view.h index c0b4e5b1c44..b237a75462b 100644 --- a/chromium/ui/views/window/client_view.h +++ b/chromium/ui/views/window/client_view.h @@ -22,8 +22,7 @@ class Widget; // "DialogClientView". class VIEWS_EXPORT ClientView : public View { public: - // Internal class name - static const char kViewClassName[]; + METADATA_HEADER(ClientView); // Constructs a ClientView object for the specified widget with the specified // contents. Since this object is created during the process of creating @@ -61,7 +60,6 @@ class VIEWS_EXPORT ClientView : public View { gfx::Size GetMinimumSize() const override; gfx::Size GetMaximumSize() const override; void Layout() override; - const char* GetClassName() const override; protected: // Overridden from View: diff --git a/chromium/ui/views/window/custom_frame_view.cc b/chromium/ui/views/window/custom_frame_view.cc index ad809569a9b..4be7f5a667c 100644 --- a/chromium/ui/views/window/custom_frame_view.cc +++ b/chromium/ui/views/window/custom_frame_view.cc @@ -16,6 +16,7 @@ #include "ui/base/resource/resource_bundle.h" #include "ui/gfx/canvas.h" #include "ui/gfx/font.h" +#include "ui/gfx/font_list.h" #include "ui/gfx/geometry/rect.h" #include "ui/gfx/image/image.h" #include "ui/strings/grit/ui_strings.h" @@ -33,6 +34,7 @@ #if defined(OS_WIN) #include "ui/display/win/screen_win.h" +#include "ui/gfx/system_fonts_win.h" #endif namespace views { @@ -67,12 +69,6 @@ constexpr SkColor kDefaultColorFrame = SkColorSetRGB(66, 116, 201); constexpr SkColor kDefaultColorFrameInactive = SkColorSetRGB(161, 182, 228); #endif -const gfx::FontList& GetTitleFontList() { - static const gfx::FontList title_font_list = - internal::NativeWidgetPrivate::GetWindowTitleFontList(); - return title_font_list; -} - void LayoutButton(ImageButton* button, const gfx::Rect& bounds) { button->SetVisible(true); button->SetImageVerticalAlignment(ImageButton::ALIGN_BOTTOM); @@ -311,7 +307,7 @@ int CustomFrameView::IconSize() const { #else // The icon never shrinks below 16 px on a side. constexpr int kIconMinimumSize = 16; - return std::max(GetTitleFontList().GetHeight(), kIconMinimumSize); + return std::max(GetWindowTitleFontList().GetHeight(), kIconMinimumSize); #endif } @@ -393,7 +389,7 @@ void CustomFrameView::PaintTitleBar(gfx::Canvas* canvas) { gfx::Rect rect = title_bounds_; rect.set_x(GetMirroredXForRect(title_bounds_)); - canvas->DrawStringRect(delegate->GetWindowTitle(), GetTitleFontList(), + canvas->DrawStringRect(delegate->GetWindowTitle(), GetWindowTitleFontList(), SK_ColorWHITE, rect); } @@ -547,7 +543,7 @@ void CustomFrameView::LayoutTitleBar() { // The offset between the window left edge and the title text. int title_x = show_window_icon ? icon_bounds.right() + kTitleIconOffsetX : icon_bounds.x(); - int title_height = GetTitleFontList().GetHeight(); + int title_height = GetWindowTitleFontList().GetHeight(); // We bias the title position so that when the difference between the icon and // title heights is odd, the extra pixel of the title is above the vertical // midline rather than below. This compensates for how the icon is already @@ -625,4 +621,13 @@ ImageButton* CustomFrameView::GetImageButton(views::FrameButton frame_button) { return button; } +// static +gfx::FontList CustomFrameView::GetWindowTitleFontList() { +#if defined(OS_WIN) + return gfx::FontList(gfx::win::GetSystemFont(gfx::win::SystemFont::kCaption)); +#else + return gfx::FontList(); +#endif +} + } // namespace views diff --git a/chromium/ui/views/window/custom_frame_view.h b/chromium/ui/views/window/custom_frame_view.h index e791aca92f4..759b9f3f864 100644 --- a/chromium/ui/views/window/custom_frame_view.h +++ b/chromium/ui/views/window/custom_frame_view.h @@ -14,6 +14,10 @@ #include "ui/views/window/frame_buttons.h" #include "ui/views/window/non_client_view.h" +namespace gfx { +class FontList; +} + namespace views { class FrameBackground; @@ -58,6 +62,10 @@ class VIEWS_EXPORT CustomFrameView : public NonClientFrameView, // Overridden from ButtonListener: void ButtonPressed(Button* sender, const ui::Event& event) override; + // Returns the font list to use in the window's title bar. + // TODO(https://crbug.com/968860): Move this into the typography provider. + static gfx::FontList GetWindowTitleFontList(); + private: friend class CustomFrameViewTest; diff --git a/chromium/ui/views/window/dialog_client_view.cc b/chromium/ui/views/window/dialog_client_view.cc index 84f4db075d0..4c946da528f 100644 --- a/chromium/ui/views/window/dialog_client_view.cc +++ b/chromium/ui/views/window/dialog_client_view.cc @@ -15,7 +15,6 @@ #include "ui/views/controls/button/image_button.h" #include "ui/views/controls/button/label_button.h" #include "ui/views/controls/button/md_text_button.h" -#include "ui/views/event_utils.h" #include "ui/views/layout/grid_layout.h" #include "ui/views/layout/layout_provider.h" #include "ui/views/style/platform_style.h" @@ -77,8 +76,8 @@ DialogClientView::DialogClientView(Widget* owner, View* contents_view) // Doing this now ensures this accelerator will have lower priority than // one set by the contents view. AddAccelerator(ui::Accelerator(ui::VKEY_ESCAPE, ui::EF_NONE)); - button_row_container_ = new ButtonRowContainer(this); - AddChildView(button_row_container_); + button_row_container_ = + AddChildView(std::make_unique<ButtonRowContainer>(this)); } DialogClientView::~DialogClientView() { @@ -167,9 +166,7 @@ gfx::Size DialogClientView::GetMaximumSize() const { void DialogClientView::VisibilityChanged(View* starting_from, bool is_visible) { ClientView::VisibilityChanged(starting_from, is_visible); - - if (is_visible) - view_shown_time_stamp_ = base::TimeTicks::Now(); + input_protector_.VisibilityChanged(is_visible); } void DialogClientView::Layout() { @@ -242,7 +239,7 @@ void DialogClientView::ButtonPressed(Button* sender, const ui::Event& event) { if (!GetDialogDelegate()) return; - if (IsPossiblyUnintendedInteraction(view_shown_time_stamp_, event)) + if (input_protector_.IsPossiblyUnintendedInteraction(event)) return; if (sender == ok_button_) @@ -254,7 +251,7 @@ void DialogClientView::ButtonPressed(Button* sender, const ui::Event& event) { } void DialogClientView::ResetViewShownTimeStampForTesting() { - view_shown_time_stamp_ = base::TimeTicks(); + input_protector_.ResetForTesting(); } //////////////////////////////////////////////////////////////////////////////// @@ -296,7 +293,7 @@ void DialogClientView::UpdateDialogButton(LabelButton** member, // MdTextButton, make it so. Note that some overrides may not always update // the title (they should). See http://crbug.com/697303 . const base::string16 title = delegate->GetDialogButtonLabel(type); - std::unique_ptr<LabelButton> button = nullptr; + std::unique_ptr<LabelButton> button; const bool is_default = delegate->GetDefaultDialogButton() == type && (type != ui::DIALOG_BUTTON_CANCEL || @@ -311,7 +308,7 @@ void DialogClientView::UpdateDialogButton(LabelButton** member, button->SetGroup(kButtonGroup); - *member = button.release(); + *member = button_row_container_->AddChildView(std::move(button)); } delegate->UpdateButton(*member, type); @@ -346,19 +343,26 @@ void DialogClientView::SetupLayout() { // Clobber any existing LayoutManager since it has weak references to child // Views which may be removed by SetupViews(). - GridLayout* layout = button_row_container_->SetLayoutManager( - std::make_unique<views::GridLayout>(button_row_container_)); - layout->set_minimum_size(minimum_size_); + button_row_container_->SetLayoutManager(nullptr); SetupViews(); + const std::array<View*, kNumButtons> views = GetButtonRowViews(); // Visibility changes on |extra_view_| must be observed to re-Layout. However, // when hidden it's not included in the button row (it can't influence layout) // and it can't be added to |button_row_container_| (GridLayout complains). // So add it, hidden, to |this| so it can be observed. - if (extra_view_ && !views[0]) - AddChildView(extra_view_); + if (extra_view_) { + if (!views[0]) + AddChildView(extra_view_); + else + button_row_container_->AddChildViewAt(extra_view_, 0); + } + + GridLayout* layout = button_row_container_->SetLayoutManager( + std::make_unique<views::GridLayout>()); + layout->set_minimum_size(minimum_size_); if (std::count(views.begin(), views.end(), nullptr) == kNumButtons) return; @@ -402,7 +406,7 @@ void DialogClientView::SetupLayout() { button_row_insets_.top()); for (size_t view_index = 0; view_index < kNumButtons; ++view_index) { if (views[view_index]) { - layout->AddView(views[view_index]); + layout->AddExistingView(views[view_index]); link[link_index++] = kViewToColumnIndex[view_index]; } else { layout->SkipColumns(1); @@ -437,21 +441,24 @@ void DialogClientView::SetupLayout() { } void DialogClientView::SetupViews() { - button_row_container_->RemoveAllChildViews(false /* delete children */); - // If SetupLayout() "stored" a hidden |extra_view_| in |this|, ensure it can - // be re-added to the layout when becoming visible. - if (extra_view_) - RemoveChildView(extra_view_); - - UpdateDialogButton(&ok_button_, ui::DIALOG_BUTTON_OK); - UpdateDialogButton(&cancel_button_, ui::DIALOG_BUTTON_CANCEL); + if (PlatformStyle::kIsOkButtonLeading) { + UpdateDialogButton(&ok_button_, ui::DIALOG_BUTTON_OK); + UpdateDialogButton(&cancel_button_, ui::DIALOG_BUTTON_CANCEL); + } else { + UpdateDialogButton(&cancel_button_, ui::DIALOG_BUTTON_CANCEL); + UpdateDialogButton(&ok_button_, ui::DIALOG_BUTTON_OK); + } if (extra_view_) return; - extra_view_ = GetDialogDelegate()->CreateExtraView(); + extra_view_ = GetDialogDelegate()->CreateExtraView().release(); if (extra_view_ && Button::AsButton(extra_view_)) extra_view_->SetGroup(kButtonGroup); } +BEGIN_METADATA(DialogClientView) +METADATA_PARENT_CLASS(ClientView) +END_METADATA() + } // namespace views diff --git a/chromium/ui/views/window/dialog_client_view.h b/chromium/ui/views/window/dialog_client_view.h index e36af7a2038..fb557cbf4ff 100644 --- a/chromium/ui/views/window/dialog_client_view.h +++ b/chromium/ui/views/window/dialog_client_view.h @@ -10,6 +10,7 @@ #include "base/time/time.h" #include "ui/base/ui_base_types.h" #include "ui/views/controls/button/button.h" +#include "ui/views/input_event_activation_protector.h" #include "ui/views/window/client_view.h" #include "ui/views/window/dialog_observer.h" @@ -33,6 +34,8 @@ class VIEWS_EXPORT DialogClientView : public ClientView, public ButtonListener, public DialogObserver { public: + METADATA_HEADER(DialogClientView); + DialogClientView(Widget* widget, View* contents_view); ~DialogClientView() override; @@ -140,8 +143,7 @@ class VIEWS_EXPORT DialogClientView : public ClientView, // SetupLayout(). Everything will be manually updated afterwards. bool adding_or_removing_views_ = false; - // Time when view has been shown. - base::TimeTicks view_shown_time_stamp_; + InputEventActivationProtector input_protector_; DISALLOW_COPY_AND_ASSIGN(DialogClientView); }; diff --git a/chromium/ui/views/window/dialog_client_view_unittest.cc b/chromium/ui/views/window/dialog_client_view_unittest.cc index a7be998428c..152917b805b 100644 --- a/chromium/ui/views/window/dialog_client_view_unittest.cc +++ b/chromium/ui/views/window/dialog_client_view_unittest.cc @@ -68,7 +68,9 @@ class DialogClientViewTest : public test::WidgetTest, // DialogDelegateView would delete this, but |this| is owned by the test. } - View* CreateExtraView() override { return next_extra_view_.release(); } + std::unique_ptr<View> CreateExtraView() override { + return std::move(next_extra_view_); + } bool GetExtraViewPadding(int* padding) override { if (extra_view_padding_) @@ -512,10 +514,11 @@ TEST_F(DialogClientViewTest, FocusChangingButtons) { } // Ensures that clicks are ignored for short time after view has been shown. -TEST_F(DialogClientViewTest, IgnorePossiblyUnintendedClicks) { +TEST_F(DialogClientViewTest, IgnorePossiblyUnintendedClicks_ClickAfterShown) { widget()->Show(); SetDialogButtons(ui::DIALOG_BUTTON_CANCEL | ui::DIALOG_BUTTON_OK); + // Should ignore clicks right after the dialog is shown. ui::MouseEvent mouse_event(ui::ET_MOUSE_PRESSED, gfx::Point(), gfx::Point(), ui::EventTimeForNow(), ui::EF_NONE, ui::EF_NONE); client_view()->ButtonPressed(client_view()->ok_button(), mouse_event); @@ -531,4 +534,45 @@ TEST_F(DialogClientViewTest, IgnorePossiblyUnintendedClicks) { EXPECT_TRUE(widget()->IsClosed()); } +// Ensures that repeated clicks with short intervals after view has been shown +// are also ignored. +TEST_F(DialogClientViewTest, IgnorePossiblyUnintendedClicks_RepeatedClicks) { + widget()->Show(); + SetDialogButtons(ui::DIALOG_BUTTON_CANCEL | ui::DIALOG_BUTTON_OK); + + const base::TimeTicks kNow = ui::EventTimeForNow(); + const base::TimeDelta kShortClickInterval = + base::TimeDelta::FromMilliseconds(GetDoubleClickInterval()); + + // Should ignore clicks right after the dialog is shown. + ui::MouseEvent mouse_event(ui::ET_MOUSE_PRESSED, gfx::Point(), gfx::Point(), + kNow, ui::EF_NONE, ui::EF_NONE); + client_view()->ButtonPressed(client_view()->ok_button(), mouse_event); + client_view()->ButtonPressed(client_view()->cancel_button(), mouse_event); + EXPECT_FALSE(widget()->IsClosed()); + + // Should ignore repeated clicks with short intervals, even though enough time + // has passed since the dialog was shown. + const base::TimeDelta kRepeatedClickInterval = kShortClickInterval / 2; + const size_t kNumClicks = 4; + ASSERT_TRUE(kNumClicks * kRepeatedClickInterval > kShortClickInterval); + base::TimeTicks event_time = kNow; + for (size_t i = 0; i < kNumClicks; i++) { + client_view()->ButtonPressed( + client_view()->cancel_button(), + ui::MouseEvent(ui::ET_MOUSE_PRESSED, gfx::Point(), gfx::Point(), + event_time, ui::EF_NONE, ui::EF_NONE)); + EXPECT_FALSE(widget()->IsClosed()); + event_time += kRepeatedClickInterval; + } + + // Sufficient time passed, events are now allowed. + event_time += kShortClickInterval; + client_view()->ButtonPressed( + client_view()->cancel_button(), + ui::MouseEvent(ui::ET_MOUSE_PRESSED, gfx::Point(), gfx::Point(), + event_time, ui::EF_NONE, ui::EF_NONE)); + EXPECT_TRUE(widget()->IsClosed()); +} + } // namespace views diff --git a/chromium/ui/views/window/dialog_delegate.cc b/chromium/ui/views/window/dialog_delegate.cc index badb55d8df8..6f1ed11c1cf 100644 --- a/chromium/ui/views/window/dialog_delegate.cc +++ b/chromium/ui/views/window/dialog_delegate.cc @@ -122,7 +122,7 @@ bool DialogDelegate::IsDialogButtonEnabled(ui::DialogButton button) const { return true; } -View* DialogDelegate::CreateExtraView() { +std::unique_ptr<View> DialogDelegate::CreateExtraView() { return nullptr; } @@ -130,7 +130,7 @@ bool DialogDelegate::GetExtraViewPadding(int* padding) { return false; } -View* DialogDelegate::CreateFootnoteView() { +std::unique_ptr<View> DialogDelegate::CreateFootnoteView() { return nullptr; } diff --git a/chromium/ui/views/window/dialog_delegate.h b/chromium/ui/views/window/dialog_delegate.h index 253777d0ba4..114e1f970ae 100644 --- a/chromium/ui/views/window/dialog_delegate.h +++ b/chromium/ui/views/window/dialog_delegate.h @@ -5,6 +5,8 @@ #ifndef UI_VIEWS_WINDOW_DIALOG_DELEGATE_H_ #define UI_VIEWS_WINDOW_DIALOG_DELEGATE_H_ +#include <memory> + #include "base/compiler_specific.h" #include "base/macros.h" #include "base/strings/string16.h" @@ -68,7 +70,7 @@ class VIEWS_EXPORT DialogDelegate : public WidgetDelegate { // Override this function to display an extra view adjacent to the buttons. // Overrides may construct the view; this will only be called once per dialog. - virtual View* CreateExtraView(); + virtual std::unique_ptr<View> CreateExtraView(); // Override this function to adjust the padding between the extra view and // the confirm/cancel buttons. Note that if there are no buttons, this will @@ -78,7 +80,7 @@ class VIEWS_EXPORT DialogDelegate : public WidgetDelegate { // Override this function to display a footnote view below the buttons. // Overrides may construct the view; this will only be called once per dialog. - virtual View* CreateFootnoteView(); + virtual std::unique_ptr<View> CreateFootnoteView(); // For Dialog boxes, if there is a "Cancel" button or no dialog button at all, // this is called when the user presses the "Cancel" button. diff --git a/chromium/ui/views/window/non_client_view.cc b/chromium/ui/views/window/non_client_view.cc index 3c583973553..167888d4b39 100644 --- a/chromium/ui/views/window/non_client_view.cc +++ b/chromium/ui/views/window/non_client_view.cc @@ -15,14 +15,11 @@ #include "ui/views/widget/widget.h" #include "ui/views/window/client_view.h" -namespace views { - -// static -const char NonClientFrameView::kViewClassName[] = - "ui/views/window/NonClientFrameView"; +#if defined(OS_WIN) +#include "ui/display/win/screen_win.h" +#endif -const char NonClientView::kViewClassName[] = - "ui/views/window/NonClientView"; +namespace views { // The frame view and the client view are always at these specific indices, // because the RootView message dispatch sends messages to items higher in the @@ -40,6 +37,17 @@ bool NonClientFrameView::GetClientMask(const gfx::Size& size, return false; } +#if defined(OS_WIN) +gfx::Point NonClientFrameView::GetSystemMenuScreenPixelLocation() const { + gfx::Point point(GetMirroredXInView(GetBoundsForClientView().x()), + GetSystemMenuY()); + View::ConvertPointToScreen(this, &point); + point = display::win::ScreenWin::DIPToScreenPoint(point); + // The native system menu seems to overlap the titlebar by 1 px. Match that. + return point - gfx::Vector2d(0, 1); +} +#endif + //////////////////////////////////////////////////////////////////////////////// // NonClientView, public: @@ -195,10 +203,6 @@ void NonClientView::GetAccessibleNodeData(ui::AXNodeData* node_data) { node_data->SetName(accessible_name_); } -const char* NonClientView::GetClassName() const { - return kViewClassName; -} - View* NonClientView::GetTooltipHandlerForPoint(const gfx::Point& point) { // The same logic as for |TargetForRect()| applies here. if (frame_view_->parent() == this) { @@ -244,6 +248,10 @@ View* NonClientView::TargetForRect(View* root, const gfx::Rect& rect) { return ViewTargeterDelegate::TargetForRect(root, rect); } +BEGIN_METADATA(NonClientView) +METADATA_PARENT_CLASS(View) +END_METADATA() + //////////////////////////////////////////////////////////////////////////////// // NonClientFrameView, public: @@ -308,10 +316,6 @@ void NonClientFrameView::GetAccessibleNodeData(ui::AXNodeData* node_data) { node_data->role = ax::mojom::Role::kClient; } -const char* NonClientFrameView::GetClassName() const { - return kViewClassName; -} - void NonClientFrameView::OnThemeChanged() { SchedulePaint(); } @@ -333,4 +337,17 @@ bool NonClientFrameView::DoesIntersectRect(const View* target, return !GetWidget()->client_view()->bounds().Intersects(rect); } +//////////////////////////////////////////////////////////////////////////////// +// NonClientFrameView, private: + +#if defined(OS_WIN) +int NonClientFrameView::GetSystemMenuY() const { + return GetBoundsForClientView().y(); +} +#endif + +BEGIN_METADATA(NonClientFrameView) +METADATA_PARENT_CLASS(View) +END_METADATA() + } // namespace views diff --git a/chromium/ui/views/window/non_client_view.h b/chromium/ui/views/window/non_client_view.h index 080e99d3938..a37c98cc9f6 100644 --- a/chromium/ui/views/window/non_client_view.h +++ b/chromium/ui/views/window/non_client_view.h @@ -6,6 +6,7 @@ #define UI_VIEWS_WINDOW_NON_CLIENT_VIEW_H_ #include "base/macros.h" +#include "build/build_config.h" #include "ui/views/view.h" #include "ui/views/view_targeter_delegate.h" @@ -23,8 +24,7 @@ class ClientView; class VIEWS_EXPORT NonClientFrameView : public View, public ViewTargeterDelegate { public: - // Internal class name. - static const char kViewClassName[]; + METADATA_HEADER(NonClientFrameView); enum { // Various edges of the frame border have a 1 px shadow along their edges; @@ -66,6 +66,12 @@ class VIEWS_EXPORT NonClientFrameView : public View, // used. virtual bool GetClientMask(const gfx::Size& size, SkPath* mask) const; +#if defined(OS_WIN) + // Returns the point in screen physical coordinates at which the system menu + // should be opened. + virtual gfx::Point GetSystemMenuScreenPixelLocation() const; +#endif + // This function must ask the ClientView to do a hittest. We don't do this in // the parent NonClientView because that makes it more difficult to calculate // hittests for regions that are partially obscured by the ClientView, e.g. @@ -89,7 +95,6 @@ class VIEWS_EXPORT NonClientFrameView : public View, // View: void GetAccessibleNodeData(ui::AXNodeData* node_data) override; - const char* GetClassName() const override; void OnThemeChanged() override; protected: @@ -100,6 +105,13 @@ class VIEWS_EXPORT NonClientFrameView : public View, const gfx::Rect& rect) const override; private: +#if defined(OS_WIN) + // Returns the y coordinate, in local coordinates, at which the system menu + // should be opened. Since this is in DIP, it does not include the 1 px + // offset into the caption area; the caller will take care of this. + virtual int GetSystemMenuY() const; +#endif + DISALLOW_COPY_AND_ASSIGN(NonClientFrameView); }; @@ -141,8 +153,7 @@ class VIEWS_EXPORT NonClientFrameView : public View, // class VIEWS_EXPORT NonClientView : public View, public ViewTargeterDelegate { public: - // Internal class name. - static const char kViewClassName[]; + METADATA_HEADER(NonClientView); NonClientView(); ~NonClientView() override; @@ -217,7 +228,6 @@ class VIEWS_EXPORT NonClientView : public View, public ViewTargeterDelegate { gfx::Size GetMaximumSize() const override; void Layout() override; void GetAccessibleNodeData(ui::AXNodeData* node_data) override; - const char* GetClassName() const override; views::View* GetTooltipHandlerForPoint(const gfx::Point& point) override; |