| /* |
| * Copyright 2016 Google Inc. All Rights Reserved. |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| #ifndef COBALT_RENDER_TREE_CHILD_ITERATOR_H_ |
| #define COBALT_RENDER_TREE_CHILD_ITERATOR_H_ |
| |
| #include "cobalt/render_tree/composition_node.h" |
| #include "cobalt/render_tree/filter_node.h" |
| #include "cobalt/render_tree/image_node.h" |
| #include "cobalt/render_tree/matrix_transform_node.h" |
| #include "cobalt/render_tree/node.h" |
| #include "cobalt/render_tree/punch_through_video_node.h" |
| #include "cobalt/render_tree/rect_node.h" |
| #include "cobalt/render_tree/rect_shadow_node.h" |
| #include "cobalt/render_tree/text_node.h" |
| |
| namespace cobalt { |
| namespace render_tree { |
| |
| class ImageNode; |
| class PunchThroughVideoNode; |
| class RectNode; |
| class RectShadowNode; |
| class TextNode; |
| |
| // The ChildIterator template provides a way to iterate over the children of |
| // a node without concern for what type of node it is (though since it is a |
| // template, the compiler must know the type). Thus, render_tree::NodeVisitor |
| // subclasses can forward visits to a ChildIterator if they want to traverse |
| // the tree without writing traversal code for specific render tree node types. |
| // Additionally, ReplaceCurrent() can be used to modify the list of children |
| // at a given node, allowing render tree modifications to be made by code that |
| // does not care about what type of render tree nodes it is dealing with. |
| // Note that child-related methods like GetCurrent(), Next(), and |
| // ReplaceCurrent() are only enabled for nodes that have children. This can |
| // be checked via |ChildIterator<T>::has_children|. |
| // |
| // Example usage: |
| // |
| // void Visit(CompositionNode* node) { VisitNode(node); } |
| // void Visit(ImageNode* node) { VisitNode(node); } |
| // ... |
| // |
| // template <typename T> |
| // typename base::enable_if<!ChildIterator<T>::has_children>::type |
| // VisitNode(T* node) { |
| // ProcessNode(); |
| // } |
| // |
| // template <typename T> |
| // typename base::enable_if<ChildIterator<T>::has_children>::type VisitNode( |
| // T* node) { |
| // ProcessNode(); |
| // |
| // // Visit children |
| // ChildIterator<T> child_iterator(node); |
| // while (Node* child = child_iterator.GetCurrent()) { |
| // Visit(child); |
| // child_iterator.Next(); |
| // } |
| // } |
| // |
| // void ProcessNode(Node* node) { |
| // // Called on every node |
| // ... |
| // } |
| // |
| |
| template <typename T, typename = void> |
| class ChildIterator { |
| public: |
| // This default class is used for all nodes that have no children. |
| static const bool has_children = false; |
| }; |
| |
| enum ChildIteratorDirection { |
| // Decides whether we start at the first or last child. |
| kChildIteratorDirectionForwards, |
| kChildIteratorDirectionBackwards, |
| }; |
| |
| // CompositionNodes may have multiple children, so we setup ChildIterator |
| // to iterate through each of them. |
| template <> |
| class ChildIterator<CompositionNode> { |
| public: |
| static const bool has_children = true; |
| |
| explicit ChildIterator( |
| CompositionNode* composition, |
| ChildIteratorDirection direction = kChildIteratorDirectionForwards) |
| : composition_node_(composition), |
| children_(composition_node_->data().children()), |
| child_number_(0), |
| direction_(direction) {} |
| |
| Node* GetCurrent() const { |
| return done() ? NULL : children_[child_index()].get(); |
| } |
| void Next() { ++child_number_; } |
| |
| void ReplaceCurrent(const scoped_refptr<Node>& new_child) { |
| DCHECK(!done()); |
| if (!modified_children_builder_) { |
| // If we haven't replaced any children yet, we start by constructing a new |
| // modifiable version of this CompositionNode's data so that we can |
| // modify its children. |
| modified_children_builder_ = composition_node_->data(); |
| } |
| // Update the modified children list with the newly replaced child. |
| *modified_children_builder_->GetChild(static_cast<int>(child_index())) = |
| new_child; |
| } |
| CompositionNode::Builder::Moved TakeReplacedChildrenBuilder() { |
| return modified_children_builder_->Pass(); |
| } |
| |
| private: |
| size_t child_index() const { |
| DCHECK(!done()); |
| return direction_ == kChildIteratorDirectionForwards |
| ? child_number_ |
| : children_.size() - child_number_ - 1; |
| } |
| bool done() const { return child_number_ >= children_.size(); } |
| |
| // The node whose children we are iterating through. |
| CompositionNode* composition_node_; |
| // A quick reference to the node's children. |
| const CompositionNode::Children& children_; |
| // The current child index we are at with our iteration. |
| size_t child_number_; |
| |
| ChildIteratorDirection direction_; |
| |
| // The builder to use if we are modifying the children. If constructed, it |
| // starts as a copy of the origin CompositionNode's data. |
| base::optional<CompositionNode::Builder> modified_children_builder_; |
| }; |
| |
| // FilterNodes can have up to 1 child. |
| template <> |
| class ChildIterator<FilterNode> { |
| public: |
| static const bool has_children = true; |
| |
| explicit ChildIterator( |
| FilterNode* filter, |
| ChildIteratorDirection direction = kChildIteratorDirectionForwards) |
| : filter_node_(filter), source_(filter->data().source.get()) { |
| UNREFERENCED_PARAMETER(direction); |
| } |
| |
| Node* GetCurrent() const { return source_; } |
| void Next() { source_ = NULL; } |
| |
| void ReplaceCurrent(const scoped_refptr<Node>& new_child) { |
| DCHECK(GetCurrent()); |
| if (!modified_children_builder_) { |
| modified_children_builder_ = filter_node_->data(); |
| } |
| modified_children_builder_->source = new_child; |
| } |
| FilterNode::Builder TakeReplacedChildrenBuilder() { |
| return *modified_children_builder_; |
| } |
| |
| private: |
| FilterNode* filter_node_; |
| Node* source_; |
| |
| base::optional<FilterNode::Builder> modified_children_builder_; |
| }; |
| |
| // MatrixTransformNodes can have up to 1 child. |
| template <> |
| class ChildIterator<MatrixTransformNode> { |
| public: |
| static const bool has_children = true; |
| |
| explicit ChildIterator( |
| MatrixTransformNode* matrix_transform, |
| ChildIteratorDirection direction = kChildIteratorDirectionForwards) |
| : matrix_transform_node_(matrix_transform), |
| source_(matrix_transform->data().source.get()) { |
| UNREFERENCED_PARAMETER(direction); |
| } |
| |
| Node* GetCurrent() const { return source_; } |
| void Next() { source_ = NULL; } |
| |
| void ReplaceCurrent(const scoped_refptr<Node>& new_child) { |
| DCHECK(GetCurrent()); |
| if (!modified_children_builder_) { |
| modified_children_builder_ = matrix_transform_node_->data(); |
| } |
| modified_children_builder_->source = new_child; |
| } |
| MatrixTransformNode::Builder TakeReplacedChildrenBuilder() { |
| return *modified_children_builder_; |
| } |
| |
| private: |
| MatrixTransformNode* matrix_transform_node_; |
| Node* source_; |
| |
| base::optional<MatrixTransformNode::Builder> modified_children_builder_; |
| }; |
| |
| } // namespace render_tree |
| } // namespace cobalt |
| |
| #endif // COBALT_RENDER_TREE_CHILD_ITERATOR_H_ |