summaryrefslogtreecommitdiffstats
path: root/views
diff options
context:
space:
mode:
authorsadrul@chromium.org <sadrul@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2011-10-12 14:55:22 +0000
committersadrul@chromium.org <sadrul@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2011-10-12 14:55:22 +0000
commit25ae9a18091e849f488630a9f29e7023e5add08f (patch)
treed2244a79abf3dc72824464e5685cc1ad46d25d8f /views
parentf435abd72034b50caa2ad5be6dce3d2728027e9b (diff)
downloadchromium_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.cc36
-rw-r--r--views/view.h6
-rw-r--r--views/view_unittest.cc199
-rw-r--r--views/widget/native_widget_aura.cc2
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) {