diff options
author | sadrul@chromium.org <sadrul@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-10-12 14:55:22 +0000 |
---|---|---|
committer | sadrul@chromium.org <sadrul@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-10-12 14:55:22 +0000 |
commit | 25ae9a18091e849f488630a9f29e7023e5add08f (patch) | |
tree | d2244a79abf3dc72824464e5685cc1ad46d25d8f /views | |
parent | f435abd72034b50caa2ad5be6dce3d2728027e9b (diff) | |
download | chromium_src-25ae9a18091e849f488630a9f29e7023e5add08f.zip chromium_src-25ae9a18091e849f488630a9f29e7023e5add08f.tar.gz chromium_src-25ae9a18091e849f488630a9f29e7023e5add08f.tar.bz2 |
views: Make sure layer visibility is updated properly.
If a View without a layer is hidden, and one of its descendant views has a layer, then the layer also needs to be hidden. This also needs to be reversed when such a View is redisplayed.
BUG=none
TEST=ViewLayerTreesInSync, VisibilityChildLayers
Review URL: http://codereview.chromium.org/8216027
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@105080 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'views')
-rw-r--r-- | views/view.cc | 36 | ||||
-rw-r--r-- | views/view.h | 6 | ||||
-rw-r--r-- | views/view_unittest.cc | 199 | ||||
-rw-r--r-- | views/widget/native_widget_aura.cc | 2 |
4 files changed, 239 insertions, 4 deletions
diff --git a/views/view.cc b/views/view.cc index d6a16eb..fc1f938 100644 --- a/views/view.cc +++ b/views/view.cc @@ -184,6 +184,12 @@ void View::AddChildViewAt(View* view, int index) { if (use_acceleration_when_possible) ReorderLayers(); + + // Make sure the visibility of the child layers are correct. + // If any of the parent View is hidden, then the layers of the subtree + // rooted at |this| should be hidden. Otherwise, all the child layers should + // inherit the visibility of the owner View. + UpdateLayerVisibility(); } void View::ReorderChildView(View* view, int index) { @@ -374,11 +380,10 @@ void View::SetVisible(bool visible) { SchedulePaint(); visible_ = visible; - if (layer()) - layer()->SetVisible(visible_); // This notifies all sub-views recursively. PropagateVisibilityNotifications(this, visible_); + UpdateLayerVisibility(); // If we are newly visible, schedule paint. if (visible_) @@ -1129,6 +1134,25 @@ void View::MoveLayerToParent(ui::Layer* parent_layer, } } +void View::UpdateLayerVisibility() { + if (!use_acceleration_when_possible) + return; + bool visible = IsVisible(); + for (const View* v = parent_; visible && v && !v->layer(); v = v->parent_) + visible = v->IsVisible(); + + UpdateChildLayerVisibility(visible); +} + +void View::UpdateChildLayerVisibility(bool ancestor_visible) { + if (layer()) { + layer()->SetVisible(ancestor_visible && IsVisible()); + } else { + for (int i = 0, count = child_count(); i < count; ++i) + child_at(i)->UpdateChildLayerVisibility(ancestor_visible && IsVisible()); + } +} + void View::UpdateChildLayerBounds(const gfx::Point& offset) { if (layer()) { layer_property_setter_->SetBounds(layer(), gfx::Rect(offset.x(), offset.y(), @@ -1467,6 +1491,7 @@ void View::DoRemoveChildView(View* view, UnregisterChildrenForVisibleBoundsNotification(view); view->PropagateRemoveNotifications(this); view->parent_ = NULL; + view->UpdateLayerVisibility(); if (delete_removed_view && view->parent_owned()) view_to_be_deleted.reset(view); @@ -1747,15 +1772,20 @@ bool View::ConvertPointFromAncestor(const View* ancestor, // Accelerated painting -------------------------------------------------------- void View::CreateLayer() { + // A new layer is being created for the view. So all the layers of the + // sub-tree can inherit the visibility of the corresponding view. + for (int i = 0, count = child_count(); i < count; ++i) + child_at(i)->UpdateChildLayerVisibility(true); + layer_.reset(new ui::Layer(NULL)); layer_->set_delegate(this); if (layer_property_setter_.get()) layer_property_setter_->Installed(layer()); else SetLayerPropertySetter(NULL); - layer_->SetVisible(IsVisible()); UpdateParentLayers(); + UpdateLayerVisibility(); // The new layer needs to be ordered in the layer tree according // to the view tree. Children of this layer were added in order diff --git a/views/view.h b/views/view.h index 5875cfb0..9d3bfbe 100644 --- a/views/view.h +++ b/views/view.h @@ -1247,6 +1247,12 @@ class VIEWS_EXPORT View : public ui::LayerDelegate, // |parent_layer|. void ReparentLayer(const gfx::Point& offset, 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 + // visibility of the child layers. + void UpdateLayerVisibility(); + void UpdateChildLayerVisibility(bool visible); + // Orphans the layers in this subtree that are parented to layers outside of // this subtree. void OrphanLayers(); diff --git a/views/view_unittest.cc b/views/view_unittest.cc index 2015658..45290cc 100644 --- a/views/view_unittest.cc +++ b/views/view_unittest.cc @@ -5,6 +5,7 @@ #include <map> #include "base/memory/scoped_ptr.h" +#include "base/rand_util.h" #include "base/string_util.h" #include "base/utf_string_conversions.h" #include "testing/gmock/include/gmock/gmock.h" @@ -54,6 +55,136 @@ bool LayerIsAncestor(const ui::Layer* ancestor, const ui::Layer* layer) { return layer == ancestor; } +// Convenience functions for walking a View tree. +const views::View* FirstView(const views::View* view) { + const views::View* v = view; + while (v->has_children()) + v = v->child_at(0); + return v; +} + +const views::View* NextView(const views::View* view) { + const views::View* v = view; + const views::View* parent = v->parent(); + if (!parent) + return NULL; + int next = parent->GetIndexOf(v) + 1; + if (next != parent->child_count()) + return FirstView(parent->child_at(next)); + return parent; +} + +// Convenience functions for walking a Layer tree. +const ui::Layer* FirstLayer(const ui::Layer* layer) { + const ui::Layer* l = layer; + while (l->children().size() > 0) + l = l->children()[0]; + return l; +} + +const ui::Layer* NextLayer(const ui::Layer* layer) { + const ui::Layer* parent = layer->parent(); + if (!parent) + return NULL; + const std::vector<ui::Layer*> children = parent->children(); + size_t index; + for (index = 0; index < children.size(); index++) { + if (children[index] == layer) + break; + } + size_t next = index + 1; + if (next < children.size()) + return FirstLayer(children[next]); + return parent; +} + +// Given the root nodes of a View tree and a Layer tree, makes sure the two +// trees are in sync. +bool ViewAndLayerTreeAreConsistent(const views::View* view, + const ui::Layer* layer) { + const views::View* v = FirstView(view); + const ui::Layer* l = FirstLayer(layer); + while (v && l) { + // Find the view with a layer. + while (v && !v->layer()) + v = NextView(v); + EXPECT_TRUE(v); + if (!v) + return false; + + // Check if the View tree and the Layer tree are in sync. + EXPECT_EQ(l, v->layer()); + if (v->layer() != l) + return false; + + // Check if the visibility states of the View and the Layer are in sync. + EXPECT_EQ(l->IsDrawn(), v->IsVisibleInRootView()); + if (v->IsVisibleInRootView() != l->IsDrawn()) { + for (const views::View* vv = v; vv; vv = vv->parent()) + LOG(ERROR) << "V: " << vv << " " << vv->IsVisible() << " " + << vv->IsVisibleInRootView() << " " << vv->layer(); + for (const ui::Layer* ll = l; ll; ll = ll->parent()) + LOG(ERROR) << "L: " << ll << " " << ll->IsDrawn(); + return false; + } + + // Check if the size of the View and the Layer are in sync. + EXPECT_EQ(l->bounds(), v->bounds()); + if (v->bounds() != l->bounds()) + return false; + + if (v == view || l == layer) + return v == view && l == layer; + + v = NextView(v); + l = NextLayer(l); + } + + return false; +} + +// Constructs a View tree with the specified depth. +void ConstructTree(views::View* view, int depth) { + if (depth == 0) + return; + int count = base::RandInt(1, 5); + for (int i = 0; i < count; i++) { + views::View* v = new views::View; + view->AddChildView(v); + if (base::RandDouble() > 0.5) + v->SetPaintToLayer(true); + if (base::RandDouble() < 0.2) + v->SetVisible(false); + + ConstructTree(v, depth - 1); + } +} + +void ScrambleTree(views::View* view) { + int count = view->child_count(); + if (count == 0) + return; + for (int i = 0; i < count; i++) { + ScrambleTree(view->child_at(i)); + } + + if (count > 1) { + int a = base::RandInt(0, count - 1); + int b = base::RandInt(0, count - 1); + + views::View* view_a = view->child_at(a); + views::View* view_b = view->child_at(b); + view->ReorderChildView(view_a, b); + view->ReorderChildView(view_b, a); + } + + if (!view->layer() && base::RandDouble() < 0.1) + view->SetPaintToLayer(true); + + if (base::RandDouble() < 0.1) + view->SetVisible(!view->IsVisible()); +} + } namespace views { @@ -2866,6 +2997,74 @@ TEST_F(ViewLayerTest, DontPaintChildrenWithLayers) { EXPECT_TRUE(content_view->painted()); } +// Tests that the visibility of child layers are updated correctly when a View's +// visibility changes. +TEST_F(ViewLayerTest, VisibilityChildLayers) { + View* v1 = new View; + v1->SetPaintToLayer(true); + widget()->SetContentsView(v1); + + View* v2 = new View; + v1->AddChildView(v2); + + View* v3 = new View; + v2->AddChildView(v3); + v3->SetVisible(false); + + View* v4 = new View; + v4->SetPaintToLayer(true); + v3->AddChildView(v4); + + EXPECT_TRUE(v1->layer()->IsDrawn()); + EXPECT_FALSE(v4->layer()->IsDrawn()); + + v2->SetVisible(false); + EXPECT_TRUE(v1->layer()->IsDrawn()); + EXPECT_FALSE(v4->layer()->IsDrawn()); + + v2->SetVisible(true); + EXPECT_TRUE(v1->layer()->IsDrawn()); + EXPECT_FALSE(v4->layer()->IsDrawn()); + + v2->SetVisible(false); + EXPECT_TRUE(v1->layer()->IsDrawn()); + EXPECT_FALSE(v4->layer()->IsDrawn()); + EXPECT_TRUE(ViewAndLayerTreeAreConsistent(v1, v1->layer())); + + v3->SetVisible(true); + EXPECT_TRUE(v1->layer()->IsDrawn()); + EXPECT_FALSE(v4->layer()->IsDrawn()); + EXPECT_TRUE(ViewAndLayerTreeAreConsistent(v1, v1->layer())); + + // Reparent |v3| to |v1|. + v1->AddChildView(v3); + EXPECT_TRUE(v1->layer()->IsDrawn()); + EXPECT_TRUE(v4->layer()->IsDrawn()); + EXPECT_TRUE(ViewAndLayerTreeAreConsistent(v1, v1->layer())); +} + +// This test creates a random View tree, and then randomly reorders child views, +// reparents views etc. Unrelated changes can appear to break this test. So +// marking this as FLAKY. +TEST_F(ViewLayerTest, FLAKY_ViewLayerTreesInSync) { + View* content = new View; + content->SetPaintToLayer(true); + widget()->SetContentsView(content); + widget()->Show(); + + ConstructTree(content, 5); + EXPECT_TRUE(ViewAndLayerTreeAreConsistent(content, content->layer())); + + ScrambleTree(content); + EXPECT_TRUE(ViewAndLayerTreeAreConsistent(content, content->layer())); + + ScrambleTree(content); + EXPECT_TRUE(ViewAndLayerTreeAreConsistent(content, content->layer())); + + ScrambleTree(content); + EXPECT_TRUE(ViewAndLayerTreeAreConsistent(content, content->layer())); +} + #endif // VIEWS_COMPOSITOR } // namespace views diff --git a/views/widget/native_widget_aura.cc b/views/widget/native_widget_aura.cc index bfd603f..6877a91 100644 --- a/views/widget/native_widget_aura.cc +++ b/views/widget/native_widget_aura.cc @@ -169,7 +169,7 @@ void NativeWidgetAura::ReorderLayers() { } void NativeWidgetAura::ViewRemoved(View* view) { - NOTIMPLEMENTED(); +// NOTIMPLEMENTED(); } void NativeWidgetAura::SetNativeWindowProperty(const char* name, void* value) { |