blob: d5567c5a30e0d6fb8172747fc486866289301318 [file] [log] [blame]
/*
* 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_