diff options
23 files changed, 757 insertions, 24 deletions
diff --git a/ui/aura/window.cc b/ui/aura/window.cc index c1f7487..5160bf6 100644 --- a/ui/aura/window.cc +++ b/ui/aura/window.cc @@ -340,6 +340,12 @@ void Window::StackChildAbove(Window* child, Window* target) { StackChildRelativeTo(child, target, STACK_ABOVE); } +void Window::StackChildAtBottom(Window* child) { + if (children_.size() <= 1 || child == children_.front()) + return; // At the bottom already. + StackChildBelow(child, children_.front()); +} + void Window::StackChildBelow(Window* child, Window* target) { StackChildRelativeTo(child, target, STACK_BELOW); } diff --git a/ui/aura/window.h b/ui/aura/window.h index e2c0d83..cd6fa63 100644 --- a/ui/aura/window.h +++ b/ui/aura/window.h @@ -175,6 +175,9 @@ class AURA_EXPORT Window : public ui::LayerDelegate, // see WindowTest.StackingMadrigal for details. void StackChildAbove(Window* child, Window* target); + // Stacks the specified child of this window at the bottom of the z-order. + void StackChildAtBottom(Window* child); + // Stacks |child| below |target|. Does nothing if |child| is already below // |target|. void StackChildBelow(Window* child, Window* target); diff --git a/ui/views/controls/native/native_view_host_aura.cc b/ui/views/controls/native/native_view_host_aura.cc index fb8a725..3a3ed14 100644 --- a/ui/views/controls/native/native_view_host_aura.cc +++ b/ui/views/controls/native/native_view_host_aura.cc @@ -8,6 +8,7 @@ #include "ui/aura/focus_manager.h" #include "ui/aura/window.h" #include "ui/views/controls/native/native_view_host.h" +#include "ui/views/view_constants_aura.h" #include "ui/views/widget/widget.h" namespace views { @@ -18,18 +19,23 @@ NativeViewHostAura::NativeViewHostAura(NativeViewHost* host) } NativeViewHostAura::~NativeViewHostAura() { - if (host_->native_view()) + if (host_->native_view()) { + host_->native_view()->ClearProperty(views::kHostViewKey); host_->native_view()->RemoveObserver(this); + } } //////////////////////////////////////////////////////////////////////////////// // NativeViewHostAura, NativeViewHostWrapper implementation: void NativeViewHostAura::NativeViewWillAttach() { host_->native_view()->AddObserver(this); + host_->native_view()->SetProperty(views::kHostViewKey, + static_cast<View*>(host_)); } void NativeViewHostAura::NativeViewDetaching(bool destroyed) { if (!destroyed) { + host_->native_view()->ClearProperty(views::kHostViewKey); host_->native_view()->RemoveObserver(this); host_->native_view()->Hide(); if (host_->native_view()->parent()) diff --git a/ui/views/controls/native/native_view_host_aura_unittest.cc b/ui/views/controls/native/native_view_host_aura_unittest.cc index 3099932..7653cc9 100644 --- a/ui/views/controls/native/native_view_host_aura_unittest.cc +++ b/ui/views/controls/native/native_view_host_aura_unittest.cc @@ -10,6 +10,7 @@ #include "ui/views/controls/native/native_view_host.h" #include "ui/views/test/views_test_base.h" #include "ui/views/view.h" +#include "ui/views/view_constants_aura.h" #include "ui/views/widget/widget.h" namespace views { @@ -23,6 +24,10 @@ class NativeViewHostAuraTest : public ViewsTestBase { return static_cast<NativeViewHostAura*>(host_->native_wrapper_.get()); } + NativeViewHost* host() { + return host_.get(); + } + Widget* child() { return child_.get(); } @@ -73,4 +78,21 @@ TEST_F(NativeViewHostAuraTest, StopObservingNativeViewOnDestruct) { EXPECT_FALSE(child_win->HasObserver(aura_host)); } +// Tests that the kHostViewKey is correctly set and cleared. +TEST_F(NativeViewHostAuraTest, HostViewPropertyKey) { + // Create the NativeViewHost and attach a NativeView. + CreateHost(); + aura::Window* child_win = child()->GetNativeView(); + EXPECT_EQ(host(), child_win->GetProperty(views::kHostViewKey)); + + host()->Detach(); + EXPECT_FALSE(child_win->GetProperty(views::kHostViewKey)); + + host()->Attach(child_win); + EXPECT_EQ(host(), child_win->GetProperty(views::kHostViewKey)); + + DestroyHost(); + EXPECT_FALSE(child_win->GetProperty(views::kHostViewKey)); +} + } // namespace views diff --git a/ui/views/view.cc b/ui/views/view.cc index 43719f3..18656ea 100644 --- a/ui/views/view.cc +++ b/ui/views/view.cc @@ -535,12 +535,6 @@ ui::Layer* View::RecreateLayer() { return NULL; CreateLayer(); - - // TODO(pkotwicz): Remove this once ReorderLayers() stacks layers not attached - // to a view above layers attached to a view. - if (layer->parent()) - layer->parent()->StackAtTop(layer); - layer_->set_scale_content(layer->scale_content()); return layer; } @@ -1458,8 +1452,8 @@ void View::ReorderLayers() { while (v && !v->layer()) v = v->parent(); + Widget* widget = GetWidget(); if (!v) { - Widget* widget = GetWidget(); if (widget) { ui::Layer* layer = widget->GetLayer(); if (layer) @@ -1468,15 +1462,29 @@ void View::ReorderLayers() { } else { v->ReorderChildLayers(v->layer()); } + + if (widget) { + // Reorder the widget's child NativeViews in case a child NativeView is + // associated with a view (eg via a NativeViewHost). Always do the + // reordering because the associated NativeView's layer (if it has one) + // is parented to the widget's layer regardless of whether the host view has + // an ancestor with a layer. + widget->ReorderNativeViews(); + } } void View::ReorderChildLayers(ui::Layer* parent_layer) { if (layer() && layer() != parent_layer) { DCHECK_EQ(parent_layer, layer()->parent()); - parent_layer->StackAtTop(layer()); + parent_layer->StackAtBottom(layer()); } else { - for (Views::const_iterator i(children_.begin()); i != children_.end(); ++i) - (*i)->ReorderChildLayers(parent_layer); + // Iterate backwards through the children so that a child with a layer + // which is further to the back is stacked above one which is further to + // the front. + for (Views::const_reverse_iterator it(children_.rbegin()); + it != children_.rend(); ++it) { + (*it)->ReorderChildLayers(parent_layer); + } } } diff --git a/ui/views/view.h b/ui/views/view.h index db2452b..4b10f0c 100644 --- a/ui/views/view.h +++ b/ui/views/view.h @@ -1104,7 +1104,11 @@ class VIEWS_EXPORT View : public ui::LayerDelegate, virtual void ReorderLayers(); // This reorders the immediate children of |*parent_layer| to match the - // order of the view tree. + // order of the view tree. Child layers which are owned by a view are + // reordered so that they are below any child layers not owned by a view. + // Widget::ReorderNativeViews() should be called to reorder any child layers + // with an associated view. Widget::ReorderNativeViews() may reorder layers + // below layers owned by a view. virtual void ReorderChildLayers(ui::Layer* parent_layer); // Input --------------------------------------------------------------------- diff --git a/ui/views/view_constants_aura.cc b/ui/views/view_constants_aura.cc new file mode 100644 index 0000000..977050f --- /dev/null +++ b/ui/views/view_constants_aura.cc @@ -0,0 +1,16 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "ui/views/view_constants_aura.h" + +#include "ui/aura/window_property.h" +#include "ui/views/view.h" + +DECLARE_EXPORTED_WINDOW_PROPERTY_TYPE(VIEWS_EXPORT, views::View*); + +namespace views { + +DEFINE_WINDOW_PROPERTY_KEY(views::View*, kHostViewKey, NULL); + +} // namespace views diff --git a/ui/views/view_constants_aura.h b/ui/views/view_constants_aura.h new file mode 100644 index 0000000..e0d718d --- /dev/null +++ b/ui/views/view_constants_aura.h @@ -0,0 +1,22 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef UI_VIEWS_VIEW_CONSTANTS_AURA_H_ +#define UI_VIEWS_VIEW_CONSTANTS_AURA_H_ + +#include "ui/aura/window.h" +#include "ui/views/views_export.h" + +namespace views { +class View; + +// A property key to indicate the view the window is associated with. If +// specified, the z-order of the view, relative to other views, dictates the +// z-order of the window and its associated layer. The associated view must +// have the same parent widget as the window on which the property is set. +VIEWS_EXPORT extern const aura::WindowProperty<View*>* const kHostViewKey; + +} // namespace views + +#endif // UI_VIEWS_VIEW_CONSTANTS_AURA_H_ diff --git a/ui/views/view_unittest.cc b/ui/views/view_unittest.cc index a5ccc2e..60aadde 100644 --- a/ui/views/view_unittest.cc +++ b/ui/views/view_unittest.cc @@ -3367,7 +3367,7 @@ TEST_F(ViewLayerTest, AcquireLayer) { } // Verify that new layer scales content only if the old layer does. -TEST_F(ViewLayerTest, RecreateLayer) { +TEST_F(ViewLayerTest, RecreateLayerScaling) { scoped_ptr<View> v(new View()); v->SetPaintToLayer(true); // Set to non default value. @@ -3377,6 +3377,67 @@ TEST_F(ViewLayerTest, RecreateLayer) { EXPECT_FALSE(new_layer->scale_content()); } +// Verify the z-order of the layers as a result of calling RecreateLayer(). +TEST_F(ViewLayerTest, RecreateLayerZOrder) { + scoped_ptr<View> v(new View()); + v->SetPaintToLayer(true); + + View* v1 = new View(); + v1->SetPaintToLayer(true); + v->AddChildView(v1); + View* v2 = new View(); + v2->SetPaintToLayer(true); + v->AddChildView(v2); + + // Test the initial z-order. + const std::vector<ui::Layer*>& child_layers_pre = v->layer()->children(); + ASSERT_EQ(2u, child_layers_pre.size()); + EXPECT_EQ(v1->layer(), child_layers_pre[0]); + EXPECT_EQ(v2->layer(), child_layers_pre[1]); + + scoped_ptr<ui::Layer> v1_old_layer(v1->RecreateLayer()); + + // Test the new layer order. |v1_old_layer| should be above the layers + // for |v1| and |v2|. + const std::vector<ui::Layer*>& child_layers_post = v->layer()->children(); + ASSERT_EQ(3u, child_layers_post.size()); + EXPECT_EQ(v1->layer(), child_layers_post[0]); + EXPECT_EQ(v2->layer(), child_layers_post[1]); + EXPECT_EQ(v1_old_layer, child_layers_post[2]); +} + +// Verify the z-order of the layers as a result of calling RecreateLayer when +// the widget is the parent with the layer. +TEST_F(ViewLayerTest, RecreateLayerZOrderWidgetParent) { + View* v = new View(); + widget()->SetContentsView(v); + + View* v1 = new View(); + v1->SetPaintToLayer(true); + v->AddChildView(v1); + View* v2 = new View(); + v2->SetPaintToLayer(true); + v->AddChildView(v2); + + ui::Layer* root_layer = GetRootLayer(); + + // Test the initial z-order. + const std::vector<ui::Layer*>& child_layers_pre = root_layer->children(); + ASSERT_EQ(2u, child_layers_pre.size()); + EXPECT_EQ(v1->layer(), child_layers_pre[0]); + EXPECT_EQ(v2->layer(), child_layers_pre[1]); + + scoped_ptr<ui::Layer> v1_old_layer(v1->RecreateLayer()); + + // Test the new layer order. |v1_old_layer| should be above the layers + // for |v1| and |v2|. + const std::vector<ui::Layer*>& child_layers_post = root_layer->children(); + ASSERT_EQ(3u, child_layers_post.size()); + EXPECT_EQ(v1->layer(), child_layers_post[0]); + EXPECT_EQ(v2->layer(), child_layers_post[1]); + EXPECT_EQ(v1_old_layer, child_layers_post[2]); +} + #endif // USE_AURA } // namespace views diff --git a/ui/views/views.gyp b/ui/views/views.gyp index 635ed67..a8d4bfa 100644 --- a/ui/views/views.gyp +++ b/ui/views/views.gyp @@ -328,6 +328,8 @@ 'view.h', 'view_constants.cc', 'view_constants.h', + 'view_constants_aura.cc', + 'view_constants_aura.h', 'view_aura.cc', 'view_model.cc', 'view_model.h', @@ -421,6 +423,8 @@ 'widget/widget_message_filter.cc', 'widget/widget_message_filter.h', 'widget/widget_observer.h', + 'widget/window_reorderer.cc', + 'widget/window_reorderer.h', 'win/fullscreen_handler.cc', 'win/fullscreen_handler.h', 'win/hwnd_message_handler.cc', @@ -493,6 +497,8 @@ 'sources/': [ ['exclude', 'corewm'], ['exclude', 'widget/desktop_aura'], + ['exclude', 'widget/window_reorderer.h'], + ['exclude', 'widget/window_reorderer.cc'], ], 'sources!': [ 'widget/native_widget_aura_window_observer.cc', @@ -723,6 +729,7 @@ 'widget/native_widget_unittest.cc', 'widget/native_widget_win_unittest.cc', 'widget/widget_unittest.cc', + 'widget/window_reorderer_unittest.cc', 'run_all_unittests.cc', ], 'conditions': [ @@ -766,6 +773,7 @@ 'sources/': [ ['exclude', 'corewm'], ['exclude', 'widget/desktop_aura'], + ['exclude', 'widget/window_reorderer_unittest.cc'] ], }], ], diff --git a/ui/views/widget/desktop_aura/desktop_native_widget_aura.cc b/ui/views/widget/desktop_aura/desktop_native_widget_aura.cc index 26828e0..c4634c3 100644 --- a/ui/views/widget/desktop_aura/desktop_native_widget_aura.cc +++ b/ui/views/widget/desktop_aura/desktop_native_widget_aura.cc @@ -41,6 +41,7 @@ #include "ui/views/widget/widget.h" #include "ui/views/widget/widget_aura_utils.h" #include "ui/views/widget/widget_delegate.h" +#include "ui/views/widget/window_reorderer.h" DECLARE_EXPORTED_WINDOW_PROPERTY_TYPE(VIEWS_EXPORT, views::DesktopNativeWidgetAura*); @@ -274,6 +275,9 @@ void DesktopNativeWidgetAura::InitNativeWidget( shadow_controller_.reset( new corewm::ShadowController( aura::client::GetActivationClient(root_window_.get()))); + + window_reorderer_.reset(new WindowReorderer(window_, + GetWidget()->GetRootView())); } NonClientFrameView* DesktopNativeWidgetAura::CreateNonClientFrameView() { @@ -320,6 +324,10 @@ ui::Layer* DesktopNativeWidgetAura::GetLayer() { return window_->layer(); } +void DesktopNativeWidgetAura::ReorderNativeViews() { + window_reorderer_->ReorderChildWindows(); +} + void DesktopNativeWidgetAura::ViewRemoved(View* view) { } diff --git a/ui/views/widget/desktop_aura/desktop_native_widget_aura.h b/ui/views/widget/desktop_aura/desktop_native_widget_aura.h index d46f0ba..fed511c 100644 --- a/ui/views/widget/desktop_aura/desktop_native_widget_aura.h +++ b/ui/views/widget/desktop_aura/desktop_native_widget_aura.h @@ -37,6 +37,7 @@ class DesktopRootWindowHost; class DropHelper; class NativeWidgetAuraWindowObserver; class TooltipManagerAura; +class WindowReorderer; class VIEWS_EXPORT DesktopNativeWidgetAura : public internal::NativeWidgetPrivate, @@ -82,6 +83,7 @@ class VIEWS_EXPORT DesktopNativeWidgetAura virtual const ui::Compositor* GetCompositor() const OVERRIDE; virtual ui::Compositor* GetCompositor() OVERRIDE; virtual ui::Layer* GetLayer() OVERRIDE; + virtual void ReorderNativeViews() OVERRIDE; virtual void ViewRemoved(View* view) OVERRIDE; virtual void SetNativeWindowProperty(const char* name, void* value) OVERRIDE; virtual void* GetNativeWindowProperty(const char* name) const OVERRIDE; @@ -244,6 +246,10 @@ class VIEWS_EXPORT DesktopNativeWidgetAura scoped_ptr<corewm::ShadowController> shadow_controller_; + // Reorders child windows of |window_| associated with a view based on the + // order of the associated views in the widget's view hierarchy. + scoped_ptr<WindowReorderer> window_reorderer_; + DISALLOW_COPY_AND_ASSIGN(DesktopNativeWidgetAura); }; diff --git a/ui/views/widget/native_widget_aura.cc b/ui/views/widget/native_widget_aura.cc index 37a6e41..413e1ce9 100644 --- a/ui/views/widget/native_widget_aura.cc +++ b/ui/views/widget/native_widget_aura.cc @@ -39,6 +39,7 @@ #include "ui/views/widget/tooltip_manager_aura.h" #include "ui/views/widget/widget_aura_utils.h" #include "ui/views/widget/widget_delegate.h" +#include "ui/views/widget/window_reorderer.h" #if defined(OS_WIN) #include "base/win/scoped_gdi_object.h" @@ -172,6 +173,9 @@ void NativeWidgetAura::InitNativeWidget(const Widget::InitParams& params) { GetWidget()->widget_delegate()->CanMaximize()); window_->SetProperty(aura::client::kCanResizeKey, GetWidget()->widget_delegate()->CanResize()); + + window_reorderer_.reset(new WindowReorderer(window_, + GetWidget()->GetRootView())); } NonClientFrameView* NativeWidgetAura::CreateNonClientFrameView() { @@ -223,6 +227,10 @@ ui::Layer* NativeWidgetAura::GetLayer() { return window_->layer(); } +void NativeWidgetAura::ReorderNativeViews() { + window_reorderer_->ReorderChildWindows(); +} + void NativeWidgetAura::ViewRemoved(View* view) { DCHECK(drop_helper_.get() != NULL); drop_helper_->ResetTargetViewIfEquals(view); diff --git a/ui/views/widget/native_widget_aura.h b/ui/views/widget/native_widget_aura.h index 3dc64d6..c40901a 100644 --- a/ui/views/widget/native_widget_aura.h +++ b/ui/views/widget/native_widget_aura.h @@ -29,6 +29,7 @@ namespace views { class DropHelper; class NativeWidgetAuraWindowObserver; class TooltipManagerAura; +class WindowReorderer; class VIEWS_EXPORT NativeWidgetAura : public internal::NativeWidgetPrivate, @@ -58,6 +59,7 @@ class VIEWS_EXPORT NativeWidgetAura virtual const ui::Compositor* GetCompositor() const OVERRIDE; virtual ui::Compositor* GetCompositor() OVERRIDE; virtual ui::Layer* GetLayer() OVERRIDE; + virtual void ReorderNativeViews() OVERRIDE; virtual void ViewRemoved(View* view) OVERRIDE; virtual void SetNativeWindowProperty(const char* name, void* value) OVERRIDE; virtual void* GetNativeWindowProperty(const char* name) const OVERRIDE; @@ -205,6 +207,10 @@ class VIEWS_EXPORT NativeWidgetAura scoped_ptr<TooltipManagerAura> tooltip_manager_; + // Reorders child windows of |window_| associated with a view based on the + // order of the associated views in the widget's view hierarchy. + scoped_ptr<WindowReorderer> window_reorderer_; + scoped_ptr<NativeWidgetAuraWindowObserver> active_window_observer_; scoped_ptr<DropHelper> drop_helper_; diff --git a/ui/views/widget/native_widget_aura_unittest.cc b/ui/views/widget/native_widget_aura_unittest.cc index 13e7de5..ae8b223 100644 --- a/ui/views/widget/native_widget_aura_unittest.cc +++ b/ui/views/widget/native_widget_aura_unittest.cc @@ -315,8 +315,7 @@ TEST_F(NativeWidgetAuraTest, ReleaseCaptureOnTouchRelease) { // Verifies views with layers are targeted for events properly. TEST_F(NativeWidgetAuraTest, PreferViewLayersToChildWindows) { - // Create two widget, |parent_root| and |child|. |child| is a child of - // |parent_root|. + // Create two widgets: |parent| and |child|. |child| is a child of |parent|. views::View* parent_root = new views::View; scoped_ptr<Widget> parent(new Widget()); Widget::InitParams parent_params(Widget::InitParams::TYPE_WINDOW_FRAMELESS); @@ -341,13 +340,21 @@ TEST_F(NativeWidgetAuraTest, PreferViewLayersToChildWindows) { parent->GetNativeWindow()->GetEventHandlerForPoint( gfx::Point(50, 50))); - // Create a view with a layer and stack it at the top (above |child|). + // Create a view with a layer and stack it at the bottom (below |child|). views::View* view_with_layer = new views::View; parent_root->AddChildView(view_with_layer); view_with_layer->SetBounds(0, 0, 50, 50); view_with_layer->SetPaintToLayer(true); - // Point is over |view_with_layer|, it should get the event. + // Make sure that |child| still gets the event. + EXPECT_EQ(child->GetNativeWindow(), + parent->GetNativeWindow()->GetEventHandlerForPoint( + gfx::Point(20, 20))); + + // Move |view_with_layer| to the top and make sure it gets the + // event when the point is within |view_with_layer|'s bounds. + view_with_layer->layer()->parent()->StackAtTop( + view_with_layer->layer()); EXPECT_EQ(parent->GetNativeWindow(), parent->GetNativeWindow()->GetEventHandlerForPoint( gfx::Point(20, 20))); @@ -357,13 +364,6 @@ TEST_F(NativeWidgetAuraTest, PreferViewLayersToChildWindows) { parent->GetNativeWindow()->GetEventHandlerForPoint( gfx::Point(70, 70))); - // Move |child| to the top and make sure it gets the event. - child->GetNativeWindow()->layer()->parent()->StackAtTop( - child->GetNativeWindow()->layer()); - EXPECT_EQ(child->GetNativeWindow(), - parent->GetNativeWindow()->GetEventHandlerForPoint( - gfx::Point(20, 20))); - delete view_with_layer; view_with_layer = NULL; diff --git a/ui/views/widget/native_widget_private.h b/ui/views/widget/native_widget_private.h index 2bb61c0..b9cc7af 100644 --- a/ui/views/widget/native_widget_private.h +++ b/ui/views/widget/native_widget_private.h @@ -100,6 +100,15 @@ class VIEWS_EXPORT NativeWidgetPrivate : public NativeWidget { // Returns the NativeWidget's layer, if any. virtual ui::Layer* GetLayer() = 0; + // Reorders the widget's child NativeViews which are associated to the view + // tree (eg via a NativeViewHost) to match the z-order of the views in the + // view tree. The z-order of views with layers relative to views with + // associated NativeViews is used to reorder the NativeView layers. This + // method assumes that the widget's child layers which are owned by a view are + // already in the correct z-order relative to each other and does no + // reordering if there are no views with an associated NativeView. + virtual void ReorderNativeViews() = 0; + // Notifies the NativeWidget that a view was removed from the Widget's view // hierarchy. virtual void ViewRemoved(View* view) = 0; diff --git a/ui/views/widget/native_widget_win.cc b/ui/views/widget/native_widget_win.cc index 59abe0b..f7a242b 100644 --- a/ui/views/widget/native_widget_win.cc +++ b/ui/views/widget/native_widget_win.cc @@ -161,6 +161,9 @@ ui::Layer* NativeWidgetWin::GetLayer() { return NULL; } +void NativeWidgetWin::ReorderNativeViews() { +} + void NativeWidgetWin::ViewRemoved(View* view) { if (drop_target_.get()) drop_target_->ResetTargetViewIfEquals(view); diff --git a/ui/views/widget/native_widget_win.h b/ui/views/widget/native_widget_win.h index 85f1e84..a2c3f91 100644 --- a/ui/views/widget/native_widget_win.h +++ b/ui/views/widget/native_widget_win.h @@ -80,6 +80,7 @@ class VIEWS_EXPORT NativeWidgetWin : public internal::NativeWidgetPrivate, virtual const ui::Compositor* GetCompositor() const OVERRIDE; virtual ui::Compositor* GetCompositor() OVERRIDE; virtual ui::Layer* GetLayer() OVERRIDE; + virtual void ReorderNativeViews() OVERRIDE; virtual void ViewRemoved(View* view) OVERRIDE; virtual void SetNativeWindowProperty(const char* name, void* value) OVERRIDE; virtual void* GetNativeWindowProperty(const char* name) const OVERRIDE; diff --git a/ui/views/widget/widget.cc b/ui/views/widget/widget.cc index c78d90c..0be49ae 100644 --- a/ui/views/widget/widget.cc +++ b/ui/views/widget/widget.cc @@ -884,6 +884,10 @@ ui::Layer* Widget::GetLayer() { return native_widget_->GetLayer(); } +void Widget::ReorderNativeViews() { + native_widget_->ReorderNativeViews(); +} + void Widget::UpdateRootLayers() { // Calculate the layers requires traversing the tree, and since nearly any // mutation of the tree can trigger this call we delay until absolutely diff --git a/ui/views/widget/widget.h b/ui/views/widget/widget.h index 6246faa..2ab9da3 100644 --- a/ui/views/widget/widget.h +++ b/ui/views/widget/widget.h @@ -573,6 +573,15 @@ class VIEWS_EXPORT Widget : public internal::NativeWidgetDelegate, // Returns the widget's layer, if any. ui::Layer* GetLayer(); + // Reorders the widget's child NativeViews which are associated to the view + // tree (eg via a NativeViewHost) to match the z-order of the views in the + // view tree. The z-order of views with layers relative to views with + // associated NativeViews is used to reorder the NativeView layers. This + // method assumes that the widget's child layers which are owned by a view are + // already in the correct z-order relative to each other and does no + // reordering if there are no views with an associated NativeView. + void ReorderNativeViews(); + // Schedules an update to the root layers. The actual processing occurs when // GetRootLayers() is invoked. void UpdateRootLayers(); diff --git a/ui/views/widget/window_reorderer.cc b/ui/views/widget/window_reorderer.cc new file mode 100644 index 0000000..dcf51c8 --- /dev/null +++ b/ui/views/widget/window_reorderer.cc @@ -0,0 +1,201 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "ui/views/widget/window_reorderer.h" + +#include <map> +#include <vector> + +#include "ui/aura/window.h" +#include "ui/views/view.h" +#include "ui/views/view_constants_aura.h" + +namespace views { + +namespace { + +// Sets |hosted_windows| to a mapping of the views with an associated window to +// the window that they are associated to. Only views associated to a child of +// |parent_window| are returned. +void GetViewsWithAssociatedWindow( + const aura::Window& parent_window, + std::map<views::View*, aura::Window*>* hosted_windows) { + const std::vector<aura::Window*>& child_windows = parent_window.children(); + for (size_t i = 0; i < child_windows.size(); ++i) { + aura::Window* child = child_windows[i]; + View* host_view = child->GetProperty(kHostViewKey); + if (host_view) + (*hosted_windows)[host_view] = child; + } +} + +// Sets |order| to the list of views whose layer / associated window's layer +// is a child of |parent_layer|. |order| is sorted in ascending z-order of +// the views. +// |hosts| are the views with an associated window whose layer is a child of +// |parent_layer|. +void GetOrderOfViewsWithLayers( + views::View* view, + ui::Layer* parent_layer, + const std::map<views::View*, aura::Window*>& hosts, + std::vector<views::View*>* order) { + DCHECK(view); + DCHECK(parent_layer); + DCHECK(order); + if (view->layer() && view->layer()->parent() == parent_layer) { + order->push_back(view); + // |hosts| may contain a child of |view|. + } else if (hosts.find(view) != hosts.end()) { + order->push_back(view); + } + + for (int i = 0; i < view->child_count(); ++i) + GetOrderOfViewsWithLayers(view->child_at(i), parent_layer, hosts, order); +} + +} // namespace + +// Class which reorders windows as a result of the kHostViewKey property being +// set on the window. +class WindowReorderer::AssociationObserver : public aura::WindowObserver { + public: + explicit AssociationObserver(WindowReorderer* reorderer); + virtual ~AssociationObserver(); + + // Start/stop observing changes in the kHostViewKey property on |window|. + void StartObserving(aura::Window* window); + void StopObserving(aura::Window* window); + + private: + // aura::WindowObserver overrides: + virtual void OnWindowPropertyChanged(aura::Window* window, + const void* key, + intptr_t old) OVERRIDE; + virtual void OnWindowDestroying(aura::Window* window) OVERRIDE; + + // Not owned. + WindowReorderer* reorderer_; + + std::set<aura::Window*> windows_; + + DISALLOW_COPY_AND_ASSIGN(AssociationObserver); +}; + +WindowReorderer::AssociationObserver::AssociationObserver( + WindowReorderer* reorderer) + : reorderer_(reorderer) { +} + +WindowReorderer::AssociationObserver::~AssociationObserver() { + while (!windows_.empty()) + StopObserving(*windows_.begin()); +} + +void WindowReorderer::AssociationObserver::StartObserving( + aura::Window* window) { + windows_.insert(window); + window->AddObserver(this); +} + +void WindowReorderer::AssociationObserver::StopObserving( + aura::Window* window) { + windows_.erase(window); + window->RemoveObserver(this); +} + +void WindowReorderer::AssociationObserver::OnWindowPropertyChanged( + aura::Window* window, + const void* key, + intptr_t old) { + if (key == kHostViewKey) + reorderer_->ReorderChildWindows(); +} + +void WindowReorderer::AssociationObserver::OnWindowDestroying( + aura::Window* window) { + windows_.erase(window); + window->RemoveObserver(this); +} + +WindowReorderer::WindowReorderer(aura::Window* parent_window, + View* root_view) + : parent_window_(parent_window), + root_view_(root_view), + association_observer_(new AssociationObserver(this)) { + parent_window_->AddObserver(this); + const std::vector<aura::Window*>& windows = parent_window_->children(); + for (size_t i = 0; i < windows.size(); ++i) + association_observer_->StartObserving(windows[i]); + ReorderChildWindows(); +} + +WindowReorderer::~WindowReorderer() { + if (parent_window_) { + parent_window_->RemoveObserver(this); + // |association_observer_| stops observing any windows it is observing upon + // destruction. + } +} + +void WindowReorderer::ReorderChildWindows() { + if (!parent_window_) + return; + + std::map<View*, aura::Window*> hosted_windows; + GetViewsWithAssociatedWindow(*parent_window_, &hosted_windows); + + if (hosted_windows.empty()) { + // Exit early if there are no views with associated windows. + // View::ReorderLayers() should have already reordered the layers owned by + // views. + return; + } + + // Compute the desired z-order of the layers based on the order of the views + // with layers and views with associated windows in the view tree. + std::vector<View*> view_with_layer_order; + GetOrderOfViewsWithLayers(root_view_, parent_window_->layer(), hosted_windows, + &view_with_layer_order); + + // For the sake of simplicity, reorder both the layers owned by views and the + // layers of windows associated with a view. Iterate through + // |view_with_layer_order| backwards and stack windows at the bottom so that + // windows not associated to a view are stacked above windows with an + // associated view. + for (std::vector<View*>::reverse_iterator it = view_with_layer_order.rbegin(); + it != view_with_layer_order.rend(); ++it) { + View* view = *it; + ui::Layer* layer = view->layer(); + aura::Window* window = NULL; + + std::map<View*, aura::Window*>::iterator hosted_window_it = + hosted_windows.find(view); + if (hosted_window_it != hosted_windows.end()) { + window = hosted_window_it->second; + layer = window->layer(); + } + + DCHECK(layer); + if (window) + parent_window_->StackChildAtBottom(window); + parent_window_->layer()->StackAtBottom(layer); + } +} + +void WindowReorderer::OnWindowAdded(aura::Window* new_window) { + association_observer_->StartObserving(new_window); + ReorderChildWindows(); +} + +void WindowReorderer::OnWillRemoveWindow(aura::Window* window) { + association_observer_->StopObserving(window); +} + +void WindowReorderer::OnWindowDestroying(aura::Window* window) { + parent_window_->RemoveObserver(this); + parent_window_ = NULL; + association_observer_.reset(); +} + +} // namespace views diff --git a/ui/views/widget/window_reorderer.h b/ui/views/widget/window_reorderer.h new file mode 100644 index 0000000..b04e48f --- /dev/null +++ b/ui/views/widget/window_reorderer.h @@ -0,0 +1,59 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef UI_VIEWS_WIDGET_WINDOW_REORDERER_H_ +#define UI_VIEWS_WIDGET_WINDOW_REORDERER_H_ + +#include "base/compiler_specific.h" +#include "base/memory/scoped_ptr.h" +#include "ui/aura/window_observer.h" + +namespace aura { +class Window; +} + +namespace views { +class View; + +// Class which reorders the widget's child windows which have an associated view +// in the widget's view tree according the z-order of the views in the view +// tree. Windows not associated to a view are stacked above windows with an +// associated view. The child windows' layers are additionally reordered +// according to the z-order of the associated views relative to views with +// layers. +class WindowReorderer : public aura::WindowObserver { + public: + WindowReorderer(aura::Window* window, View* root_view); + virtual ~WindowReorderer(); + + // Explicitly reorder the children of |window_| (and their layers). This + // method should be called when the position of a view with an associated + // window changes in the view hierarchy. This method assumes that the + // child layers of |window_| which are owned by views are already in the + // correct z-order relative to each other and does no reordering if there + // are no views with an associated window. + void ReorderChildWindows(); + + private: + // aura::WindowObserver overrides: + virtual void OnWindowAdded(aura::Window* new_window) OVERRIDE; + virtual void OnWillRemoveWindow(aura::Window* window) OVERRIDE; + virtual void OnWindowDestroying(aura::Window* window) OVERRIDE; + + // The window and the root view of the native widget which owns the + // WindowReorderer. + aura::Window* parent_window_; + View* root_view_; + + // Reorders windows as a result of the kHostViewKey being set on a child of + // |parent_window_|. + class AssociationObserver; + scoped_ptr<AssociationObserver> association_observer_; + + DISALLOW_COPY_AND_ASSIGN(WindowReorderer); +}; + +} // namespace views + +#endif // UI_VIEWS_WIDGET_WINDOW_REORDERER_H_ diff --git a/ui/views/widget/window_reorderer_unittest.cc b/ui/views/widget/window_reorderer_unittest.cc new file mode 100644 index 0000000..1d9566c --- /dev/null +++ b/ui/views/widget/window_reorderer_unittest.cc @@ -0,0 +1,263 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "ui/aura/root_window.h" +#include "ui/aura/test/aura_test_base.h" +#include "ui/aura/test/test_windows.h" +#include "ui/aura/window.h" +#include "ui/compositor/layer.h" +#include "ui/compositor/test/test_layers.h" +#include "ui/views/view.h" +#include "ui/views/view_constants_aura.h" +#include "ui/views/widget/widget.h" + +namespace views { +namespace { + +// Creates a control widget with the passed in parameters. +// The caller takes ownership of the returned widget. +Widget* CreateControlWidget(aura::Window* parent, const gfx::Rect& bounds) { + Widget::InitParams params(Widget::InitParams::TYPE_CONTROL); + params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; + params.parent = parent; + params.bounds = bounds; + Widget* widget = new Widget(); + widget->Init(params); + return widget; +} + +// Sets the name of |window| and |window|'s layer to |name|. +void SetWindowAndLayerName(aura::Window* window, const std::string& name) { + window->SetName(name); + window->layer()->set_name(name); +} + +// Returns a string containing the name of each of the child windows (bottommost +// first) of |parent|. The format of the string is "name1 name2 name3 ...". +std::string ChildWindowNamesAsString(const aura::Window& parent) { + std::string names; + typedef std::vector<aura::Window*> Windows; + for (Windows::const_iterator it = parent.children().begin(); + it != parent.children().end(); ++it) { + if (!names.empty()) + names += " "; + names += (*it)->name(); + } + return names; +} + +typedef aura::test::AuraTestBase WindowReordererTest; + +// Test that views with layers and views with associated windows are reordered +// according to the view hierarchy. +TEST_F(WindowReordererTest, Basic) { + scoped_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); + + // 1) Test that layers for views and layers for windows associated to a host + // view are stacked below the layers for any windows not associated to a host + // view. + View* v = new View(); + v->SetPaintToLayer(true); + v->layer()->set_name("v"); + contents_view->AddChildView(v); + + scoped_ptr<Widget> w1(CreateControlWidget(parent_window, + gfx::Rect(0, 1, 100, 101))); + SetWindowAndLayerName(w1->GetNativeView(), "w1"); + w1->Show(); + scoped_ptr<Widget> w2(CreateControlWidget(parent_window, + gfx::Rect(0, 2, 100, 102))); + SetWindowAndLayerName(w2->GetNativeView(), "w2"); + w2->Show(); + + EXPECT_EQ("w1 w2", ChildWindowNamesAsString(*parent_window)); + EXPECT_EQ("v w1 w2", + ui::test::ChildLayerNamesAsString(*parent_window->layer())); + + View* host_view2 = new View(); + contents_view->AddChildView(host_view2); + w2->GetNativeView()->SetProperty(kHostViewKey, host_view2); + EXPECT_EQ("w2 w1", ChildWindowNamesAsString(*parent_window)); + EXPECT_EQ("v w2 w1", + ui::test::ChildLayerNamesAsString(*parent_window->layer())); + + View* host_view1 = new View(); + w1->GetNativeView()->SetProperty(kHostViewKey, host_view1); + contents_view->AddChildViewAt(host_view1, 0); + EXPECT_EQ("w1 w2", ChildWindowNamesAsString(*parent_window)); + EXPECT_EQ("w1 v w2", + ui::test::ChildLayerNamesAsString(*parent_window->layer())); + + // 2) Test the z-order of the windows and layers as a result of reordering the + // views. + contents_view->ReorderChildView(host_view1, -1); + EXPECT_EQ("w2 w1", ChildWindowNamesAsString(*parent_window)); + EXPECT_EQ("v w2 w1", + ui::test::ChildLayerNamesAsString(*parent_window->layer())); + + contents_view->ReorderChildView(host_view2, -1); + EXPECT_EQ("w1 w2", ChildWindowNamesAsString(*parent_window)); + EXPECT_EQ("v w1 w2", + ui::test::ChildLayerNamesAsString(*parent_window->layer())); + + // 3) Test the z-order of the windows and layers as a result of reordering the + // views in situations where the window order remains unchanged. + contents_view->ReorderChildView(v, -1); + EXPECT_EQ("w1 w2", ChildWindowNamesAsString(*parent_window)); + EXPECT_EQ("w1 w2 v", + ui::test::ChildLayerNamesAsString(*parent_window->layer())); + + contents_view->ReorderChildView(host_view2, -1); + EXPECT_EQ("w1 w2", ChildWindowNamesAsString(*parent_window)); + EXPECT_EQ("w1 v w2", + ui::test::ChildLayerNamesAsString(*parent_window->layer())); + + // Work around for bug in NativeWidgetAura. + // TODO: fix bug and remove this. + parent->Close(); +} + +// Test that different orderings of: +// - adding a window to a parent widget +// - adding a "host" view to a parent widget +// - associating the "host" view and window +// all correctly reorder the child windows and layers. +TEST_F(WindowReordererTest, Association) { + scoped_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); + + aura::Window* w1 = aura::test::CreateTestWindowWithId(0, + parent->GetNativeWindow()); + SetWindowAndLayerName(w1, "w1"); + + aura::Window* w2 = aura::test::CreateTestWindowWithId(0, NULL); + SetWindowAndLayerName(w2, "w2"); + + View* host_view2 = new View(); + + // 1) Test that parenting the window to the parent widget last results in a + // correct ordering of child windows and layers. + contents_view->AddChildView(host_view2); + w2->SetProperty(views::kHostViewKey, host_view2); + EXPECT_EQ("w1", ChildWindowNamesAsString(*parent_window)); + EXPECT_EQ("w1", + ui::test::ChildLayerNamesAsString(*parent_window->layer())); + + parent_window->AddChild(w2); + EXPECT_EQ("w2 w1", ChildWindowNamesAsString(*parent_window)); + EXPECT_EQ("w2 w1", + ui::test::ChildLayerNamesAsString(*parent_window->layer())); + + // 2) Test that associating the window and "host" view last results in a + // correct ordering of child windows and layers. + View* host_view1 = new View(); + contents_view->AddChildViewAt(host_view1, 0); + EXPECT_EQ("w2 w1", ChildWindowNamesAsString(*parent_window)); + EXPECT_EQ("w2 w1", + ui::test::ChildLayerNamesAsString(*parent_window->layer())); + + w1->SetProperty(views::kHostViewKey, host_view1); + EXPECT_EQ("w1 w2", ChildWindowNamesAsString(*parent_window)); + EXPECT_EQ("w1 w2", + ui::test::ChildLayerNamesAsString(*parent_window->layer())); + + // 3) Test that parenting the "host" view to the parent widget last results + // in a correct ordering of child windows and layers. + contents_view->RemoveChildView(host_view2); + contents_view->AddChildViewAt(host_view2, 0); + EXPECT_EQ("w2 w1", ChildWindowNamesAsString(*parent_window)); + EXPECT_EQ("w2 w1", + ui::test::ChildLayerNamesAsString(*parent_window->layer())); + + // Work around for bug in NativeWidgetAura. + // TODO: fix bug and remove this. + parent->Close(); +} + +// It is possible to associate a window to a view which has a parent layer +// (other than the widget layer). In this case, the parent layer of the host +// view and the parent layer of the associated window are different. Test that +// the layers and windows are properly reordered in this case. +TEST_F(WindowReordererTest, HostViewParentHasLayer) { + scoped_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); + + // Create the following view hierarchy. (*) denotes views which paint to a + // layer. + // + // contents_view + // +-- v1 + // +-- v11* + // +-- v12 (attached window) + // +-- v13* + // +--v2* + + View* v1 = new View(); + contents_view->AddChildView(v1); + + View* v11 = new View(); + v11->SetPaintToLayer(true); + v11->layer()->set_name("v11"); + v1->AddChildView(v11); + + scoped_ptr<Widget> w(CreateControlWidget(parent_window, + gfx::Rect(0, 1, 100, 101))); + SetWindowAndLayerName(w->GetNativeView(), "w"); + w->Show(); + + View* v12 = new View(); + v1->AddChildView(v12); + w->GetNativeView()->SetProperty(kHostViewKey, v12); + + View* v13 = new View(); + v13->SetPaintToLayer(true); + v13->layer()->set_name("v13"); + v1->AddChildView(v13); + + View* v2 = new View(); + v2->SetPaintToLayer(true); + v2->layer()->set_name("v2"); + contents_view->AddChildView(v2); + + // Test intial state. + EXPECT_EQ("w", ChildWindowNamesAsString(*parent_window)); + EXPECT_EQ("v11 w v13 v2", + ui::test::ChildLayerNamesAsString(*parent_window->layer())); + + // |w|'s layer should be stacked above |v1|'s layer. + v1->SetPaintToLayer(true); + v1->layer()->set_name("v1"); + EXPECT_EQ("w", ChildWindowNamesAsString(*parent_window)); + EXPECT_EQ("v1 w v2", + ui::test::ChildLayerNamesAsString(*parent_window->layer())); + + // Test moving the host view from one view with a layer to another. + v2->AddChildView(v12); + EXPECT_EQ("w", ChildWindowNamesAsString(*parent_window)); + EXPECT_EQ("v1 v2 w", + ui::test::ChildLayerNamesAsString(*parent_window->layer())); + + // Work around for bug in NativeWidgetAura. + // TODO: fix bug and remove this. + parent->Close(); +} + +} // namespace +} // namespace views |