Index: ui/views/view.cc |
diff --git a/ui/views/view.cc b/ui/views/view.cc |
index bbd12407706424d28ef082e5baeca695dc9354da..598070686a67bdb02e4980e45a5cff4ed02c7584 100644 |
--- a/ui/views/view.cc |
+++ b/ui/views/view.cc |
@@ -158,6 +158,7 @@ View::View() |
enabled_(true), |
notify_enter_exit_on_child_(false), |
registered_for_visible_bounds_notification_(false), |
+ root_bounds_dirty_(true), |
clip_insets_(0, 0, 0, 0), |
needs_layout_(true), |
flip_canvas_on_paint_for_rtl_ui_(false), |
@@ -234,6 +235,9 @@ void View::AddChildViewAt(View* view, int index) { |
view->parent_ = this; |
children_.insert(children_.begin() + index, view); |
+ // Instruct the view to recompute its root bounds on next Paint(). |
+ view->SetRootBoundsDirty(true); |
+ |
views::Widget* widget = GetWidget(); |
if (widget) { |
const ui::NativeTheme* new_theme = view->GetNativeTheme(); |
@@ -512,6 +516,31 @@ void View::SetTransform(const gfx::Transform& transform) { |
} |
void View::SetPaintToLayer(bool paint_to_layer) { |
+ if (paint_to_layer_ == paint_to_layer) |
+ return; |
+ |
+ // If this is a change in state we will also need to update bounds trees. |
+ if (paint_to_layer) { |
+ // Gaining a layer means becoming a paint root. We must remove ourselves |
+ // from our old paint root, if we had one. Traverse up view tree to find old |
+ // paint root. |
+ View* old_paint_root = parent_; |
+ while (old_paint_root && !old_paint_root->IsPaintRoot()) |
+ old_paint_root = old_paint_root->parent_; |
+ |
+ // Remove our and our children's bounds from the old tree. This will also |
+ // mark all of our bounds as dirty. |
+ if (old_paint_root && old_paint_root->bounds_tree_) |
+ RemoveRootBounds(old_paint_root->bounds_tree_.get()); |
+ |
+ } else { |
+ // Losing a layer means we are no longer a paint root, so delete our |
+ // bounds tree and mark ourselves as dirty for future insertion into our |
+ // new paint root's bounds tree. |
+ bounds_tree_.reset(); |
+ SetRootBoundsDirty(true); |
+ } |
+ |
paint_to_layer_ = paint_to_layer; |
if (paint_to_layer_ && !layer()) { |
CreateLayer(); |
@@ -772,32 +801,65 @@ void View::SchedulePaintInRect(const gfx::Rect& rect) { |
} |
} |
-void View::Paint(gfx::Canvas* canvas) { |
- TRACE_EVENT1("views", "View::Paint", "class", GetClassName()); |
+void View::Paint(gfx::Canvas* canvas, const CullSet& cull_set) { |
+ // The cull_set may allow us to skip painting without canvas construction or |
+ // even canvas rect intersection. |
+ if (cull_set.ShouldPaint(this)) { |
+ TRACE_EVENT1("views", "View::Paint", "class", GetClassName()); |
- gfx::ScopedCanvas scoped_canvas(canvas); |
+ gfx::ScopedCanvas scoped_canvas(canvas); |
- // Paint this View and its children, setting the clip rect to the bounds |
- // of this View and translating the origin to the local bounds' top left |
- // point. |
- // |
- // Note that the X (or left) position we pass to ClipRectInt takes into |
- // consideration whether or not the view uses a right-to-left layout so that |
- // we paint our view in its mirrored position if need be. |
- gfx::Rect clip_rect = bounds(); |
- clip_rect.Inset(clip_insets_); |
- if (parent_) |
- clip_rect.set_x(parent_->GetMirroredXForRect(clip_rect)); |
- canvas->ClipRect(clip_rect); |
- if (canvas->IsClipEmpty()) |
- return; |
+ // Paint this View and its children, setting the clip rect to the bounds |
+ // of this View and translating the origin to the local bounds' top left |
+ // point. |
+ // |
+ // Note that the X (or left) position we pass to ClipRectInt takes into |
+ // consideration whether or not the view uses a right-to-left layout so that |
+ // we paint our view in its mirrored position if need be. |
+ gfx::Rect clip_rect = bounds(); |
+ clip_rect.Inset(clip_insets_); |
+ if (parent_) |
+ clip_rect.set_x(parent_->GetMirroredXForRect(clip_rect)); |
+ canvas->ClipRect(clip_rect); |
+ if (canvas->IsClipEmpty()) |
+ return; |
+ |
+ // Non-empty clip, translate the graphics such that 0,0 corresponds to where |
+ // this view is located (related to its parent). |
+ canvas->Translate(GetMirroredPosition().OffsetFromOrigin()); |
+ canvas->Transform(GetTransform()); |
- // Non-empty clip, translate the graphics such that 0,0 corresponds to |
- // where this view is located (related to its parent). |
- canvas->Translate(GetMirroredPosition().OffsetFromOrigin()); |
- canvas->Transform(GetTransform()); |
+ // If we are a paint root, we need to construct our own CullSet object for |
+ // propagation to our children. |
+ if (IsPaintRoot()) { |
+ if (!bounds_tree_) |
+ bounds_tree_.reset(new gfx::RTree(2, 5)); |
- PaintCommon(canvas); |
+ // Recompute our bounds tree as needed. |
+ UpdateRootBounds(bounds_tree_.get(), gfx::Vector2d()); |
+ |
+ // Grab the clip rect from the supplied canvas to use as the query rect. |
+ gfx::Rect canvas_bounds; |
+ if (!canvas->GetClipBounds(&canvas_bounds)) { |
+ NOTREACHED() << "Failed to get clip bounds from the canvas!"; |
+ return; |
+ } |
+ |
+ // Now query our bounds_tree_ for a set of damaged views that intersect |
+ // our canvas bounds. |
+ scoped_ptr<base::hash_set<intptr_t> > damaged_views( |
+ new base::hash_set<intptr_t>()); |
+ bounds_tree_->Query(canvas_bounds, damaged_views.get()); |
+ // Construct a CullSet to wrap the damaged views set, it will delete it |
+ // for us on scope exit. |
+ CullSet paint_root_cull_set(damaged_views.Pass()); |
+ // Paint all descendents using our new cull set. |
+ PaintCommon(canvas, paint_root_cull_set); |
+ } else { |
+ // Not a paint root, so we can proceed as normal. |
+ PaintCommon(canvas, cull_set); |
+ } |
+ } |
} |
void View::set_background(Background* b) { |
@@ -1389,11 +1451,11 @@ void View::NativeViewHierarchyChanged() { |
// Painting -------------------------------------------------------------------- |
-void View::PaintChildren(gfx::Canvas* canvas) { |
+void View::PaintChildren(gfx::Canvas* canvas, const CullSet& cull_set) { |
TRACE_EVENT1("views", "View::PaintChildren", "class", GetClassName()); |
for (int i = 0, count = child_count(); i < count; ++i) |
if (!child_at(i)->layer()) |
- child_at(i)->Paint(canvas); |
+ child_at(i)->Paint(canvas, cull_set); |
} |
void View::OnPaint(gfx::Canvas* canvas) { |
@@ -1420,6 +1482,10 @@ void View::OnPaintBorder(gfx::Canvas* canvas) { |
} |
} |
+bool View::IsPaintRoot() { |
+ return paint_to_layer_ || !parent_; |
+} |
+ |
// Accelerated Painting -------------------------------------------------------- |
void View::SetFillsBoundsOpaquely(bool fills_bounds_opaquely) { |
@@ -1502,7 +1568,7 @@ void View::UpdateChildLayerBounds(const gfx::Vector2d& offset) { |
void View::OnPaintLayer(gfx::Canvas* canvas) { |
if (!layer() || !layer()->fills_bounds_opaquely()) |
canvas->DrawColor(SK_ColorBLACK, SkXfermode::kClear_Mode); |
- PaintCommon(canvas); |
+ PaintCommon(canvas, CullSet()); |
} |
void View::OnDeviceScaleFactorChanged(float device_scale_factor) { |
@@ -1750,7 +1816,6 @@ std::string View::DoPrintViewGraph(bool first, View* view_with_children) { |
return result; |
} |
- |
#endif |
//////////////////////////////////////////////////////////////////////////////// |
@@ -1787,7 +1852,7 @@ void View::SchedulePaintBoundsChanged(SchedulePaintType type) { |
} |
} |
-void View::PaintCommon(gfx::Canvas* canvas) { |
+void View::PaintCommon(gfx::Canvas* canvas, const CullSet& cull_set) { |
if (!visible_) |
return; |
@@ -1806,7 +1871,7 @@ void View::PaintCommon(gfx::Canvas* canvas) { |
OnPaint(canvas); |
} |
- PaintChildren(canvas); |
+ PaintChildren(canvas, cull_set); |
} |
// Tree operations ------------------------------------------------------------- |
@@ -1837,6 +1902,13 @@ void View::DoRemoveChildView(View* view, |
view->SchedulePaint(); |
GetWidget()->NotifyWillRemoveView(view); |
} |
+ |
+ // Remove the bounds of this child and any of its descendants from our |
+ // paint root bounds tree. |
+ gfx::RTree* bounds_tree = GetBoundsTreeFromPaintRoot(); |
+ if (bounds_tree) |
+ view->RemoveRootBounds(bounds_tree); |
+ |
view->PropagateRemoveNotifications(this, new_parent); |
view->parent_ = NULL; |
view->UpdateLayerVisibility(); |
@@ -1928,6 +2000,12 @@ void View::VisibilityChangedImpl(View* starting_from, bool is_visible) { |
} |
void View::BoundsChanged(const gfx::Rect& previous_bounds) { |
+ // Mark our bounds as dirty for the paint root, also see if we need to |
+ // recompute our children's bounds due to origin change. |
+ bool origin_changed = |
+ previous_bounds.OffsetFromOrigin() != bounds_.OffsetFromOrigin(); |
+ SetRootBoundsDirty(origin_changed); |
+ |
if (visible_) { |
// Paint the new bounds. |
SchedulePaintBoundsChanged( |
@@ -2024,6 +2102,69 @@ void View::SetLayerBounds(const gfx::Rect& bounds) { |
layer()->SetBounds(bounds); |
} |
+void View::SetRootBoundsDirty(bool origin_changed) { |
+ root_bounds_dirty_ = true; |
+ |
+ if (origin_changed) { |
+ // Inform our children that their root bounds are dirty, as their relative |
+ // coordinates in paint root space have changed since ours have changed. |
+ for (Views::const_iterator i(children_.begin()); i != children_.end(); |
+ ++i) { |
+ if (!(*i)->IsPaintRoot()) |
+ (*i)->SetRootBoundsDirty(origin_changed); |
+ } |
+ } |
+} |
+ |
+void View::UpdateRootBounds(gfx::RTree* tree, const gfx::Vector2d& offset) { |
+ // No need to recompute bounds if we haven't flagged ours as dirty. |
+ TRACE_EVENT1("views", "View::UpdateRootBounds", "class", GetClassName()); |
+ |
+ // Add our own offset to the provided offset, for our own bounds update and |
+ // for propagation to our children if needed. |
+ gfx::Vector2d view_offset = offset + bounds_.OffsetFromOrigin(); |
+ |
+ // If our bounds have changed we must re-insert our new bounds to the tree. |
+ if (root_bounds_dirty_) { |
+ root_bounds_dirty_ = false; |
+ gfx::Rect bounds( |
+ view_offset.x(), view_offset.y(), bounds_.width(), bounds_.height()); |
+ tree->Insert(bounds, reinterpret_cast<intptr_t>(this)); |
+ } |
+ |
+ // Update our children's bounds if needed. |
+ for (Views::const_iterator i(children_.begin()); i != children_.end(); ++i) { |
+ // We don't descend in to layer views for bounds recomputation, as they |
+ // manage their own RTree as paint roots. |
+ if (!(*i)->IsPaintRoot()) |
+ (*i)->UpdateRootBounds(tree, view_offset); |
+ } |
+} |
+ |
+void View::RemoveRootBounds(gfx::RTree* tree) { |
+ tree->Remove(reinterpret_cast<intptr_t>(this)); |
+ |
+ root_bounds_dirty_ = true; |
+ |
+ for (Views::const_iterator i(children_.begin()); i != children_.end(); ++i) { |
+ if (!(*i)->IsPaintRoot()) |
+ (*i)->RemoveRootBounds(tree); |
+ } |
+} |
+ |
+gfx::RTree* View::GetBoundsTreeFromPaintRoot() { |
+ gfx::RTree* bounds_tree = bounds_tree_.get(); |
+ View* paint_root = this; |
+ while (!bounds_tree && !paint_root->IsPaintRoot()) { |
+ // Assumption is that if IsPaintRoot() is false then parent_ is valid. |
+ DCHECK(paint_root); |
+ paint_root = paint_root->parent_; |
+ bounds_tree = paint_root->bounds_tree_.get(); |
+ } |
+ |
+ return bounds_tree; |
+} |
+ |
// Transformations ------------------------------------------------------------- |
bool View::GetTransformRelativeTo(const View* ancestor, |