blob: 4a699860b01c12847b66ee986a8b91f5902039c5 [file] [log] [blame]
// Copyright 2015 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "base/bind.h"
#include "examples/ui/tile/tile_view.h"
#include "mojo/public/cpp/application/connect.h"
#include "mojo/services/geometry/cpp/geometry_util.h"
namespace examples {
namespace {
constexpr uint32_t kViewResourceIdBase = 100;
constexpr uint32_t kViewResourceIdSpacing = 100;
constexpr uint32_t kRootNodeId = mojo::gfx::composition::kSceneRootNodeId;
constexpr uint32_t kViewNodeIdBase = 100;
constexpr uint32_t kViewNodeIdSpacing = 100;
constexpr uint32_t kViewSceneNodeIdOffset = 1;
constexpr uint32_t kViewFallbackColorNodeIdOffset = 2;
constexpr uint32_t kViewFallbackDimLayerNodeIdOffset = 3;
constexpr uint32_t kViewFallbackDimSceneNodeIdOffset = 4;
} // namespace
TileParams::TileParams() {}
TileParams::~TileParams() {}
TileView::TileView(
mojo::ApplicationImpl* app_impl,
mojo::InterfaceRequest<mojo::ui::ViewOwner> view_owner_request,
const TileParams& params)
: BaseView(app_impl, view_owner_request.Pass(), "Tile"), params_(params) {
ConnectViews();
}
TileView::~TileView() {}
void TileView::ConnectViews() {
uint32_t child_key = 0;
for (const auto& url : params_.view_urls) {
// Start connecting to the view provider.
mojo::ui::ViewProviderPtr provider;
mojo::ConnectToService(app_impl()->shell(), url, mojo::GetProxy(&provider));
LOG(INFO) << "Connecting to view: child_key=" << child_key
<< ", url=" << url;
mojo::ui::ViewOwnerPtr child_view_owner;
provider->CreateView(mojo::GetProxy(&child_view_owner), nullptr, nullptr);
GetViewContainer()->AddChild(child_key, child_view_owner.Pass());
views_.emplace(std::make_pair(
child_key, std::unique_ptr<ViewData>(new ViewData(url, child_key))));
child_key++;
}
}
void TileView::OnChildAttached(uint32_t child_key,
mojo::ui::ViewInfoPtr child_view_info) {
auto it = views_.find(child_key);
DCHECK(it != views_.end());
ViewData* view_data = it->second.get();
view_data->view_info = child_view_info.Pass();
UpdateScene();
}
void TileView::OnChildUnavailable(uint32_t child_key) {
auto it = views_.find(child_key);
DCHECK(it != views_.end());
LOG(ERROR) << "View died unexpectedly: child_key=" << child_key
<< ", url=" << it->second->url;
std::unique_ptr<ViewData> view_data = std::move(it->second);
views_.erase(it);
GetViewContainer()->RemoveChild(child_key, nullptr);
}
void TileView::OnPropertiesChanged(uint32_t old_scene_version,
mojo::ui::ViewPropertiesPtr old_properties) {
if (!properties())
return;
// Layout all children in a row.
if (!views_.empty()) {
const mojo::Size& size = *properties()->view_layout->size;
const bool vertical =
(params_.orientation_mode == TileParams::OrientationMode::kVertical);
uint32_t index = 0;
uint32_t space = vertical ? size.height : size.width;
uint32_t base = space / views_.size();
uint32_t excess = space % views_.size();
uint32_t offset = 0;
for (auto it = views_.begin(); it != views_.end(); ++it, ++index) {
ViewData* view_data = it->second.get();
// Distribute any excess width among the leading children.
uint32_t extent = base;
if (excess) {
extent++;
excess--;
}
if (vertical) {
view_data->layout_bounds.x = 0;
view_data->layout_bounds.y = offset;
view_data->layout_bounds.width = size.width;
view_data->layout_bounds.height = extent;
} else {
view_data->layout_bounds.x = offset;
view_data->layout_bounds.y = 0;
view_data->layout_bounds.width = extent;
view_data->layout_bounds.height = size.height;
}
offset += extent;
auto view_properties = mojo::ui::ViewProperties::New();
view_properties->view_layout = mojo::ui::ViewLayout::New();
view_properties->view_layout->size = mojo::Size::New();
view_properties->view_layout->size->width =
view_data->layout_bounds.width;
view_properties->view_layout->size->height =
view_data->layout_bounds.height;
if (view_data->view_properties.Equals(view_properties))
continue; // no layout work to do
view_data->view_properties = view_properties.Clone();
view_data->scene_version++;
GetViewContainer()->SetChildProperties(
it->first, view_data->scene_version, view_properties.Pass());
}
}
UpdateScene();
}
void TileView::UpdateScene() {
// Update the scene.
// TODO: only send the resources once, be more incremental
auto update = mojo::gfx::composition::SceneUpdate::New();
update->clear_resources = true;
update->clear_nodes = true;
// Create the root node.
auto root_node = mojo::gfx::composition::Node::New();
// Add the children.
for (auto it = views_.cbegin(); it != views_.cend(); it++) {
const ViewData& view_data = *(it->second.get());
const uint32_t scene_resource_id =
kViewResourceIdBase + view_data.key * kViewResourceIdSpacing;
const uint32_t container_node_id =
kViewNodeIdBase + view_data.key * kViewNodeIdSpacing;
mojo::RectF extent;
extent.width = view_data.layout_bounds.width;
extent.height = view_data.layout_bounds.height;
// Create a container to represent the place where the child view
// will be presented. The children of the container provide
// fallback behavior in case the view is not available.
auto container_node = mojo::gfx::composition::Node::New();
container_node->content_clip = extent.Clone();
container_node->content_transform = mojo::Transform::New();
SetTranslationTransform(container_node->content_transform.get(),
view_data.layout_bounds.x,
view_data.layout_bounds.y, 0.f);
// If we have the view, add it to the scene.
if (view_data.view_info) {
auto scene_resource = mojo::gfx::composition::Resource::New();
scene_resource->set_scene(mojo::gfx::composition::SceneResource::New());
scene_resource->get_scene()->scene_token =
view_data.view_info->scene_token.Clone();
update->resources.insert(scene_resource_id, scene_resource.Pass());
const uint32_t scene_node_id = container_node_id + kViewSceneNodeIdOffset;
auto scene_node = mojo::gfx::composition::Node::New();
scene_node->op = mojo::gfx::composition::NodeOp::New();
scene_node->op->set_scene(mojo::gfx::composition::SceneNodeOp::New());
scene_node->op->get_scene()->scene_resource_id = scene_resource_id;
if (params_.version_mode == TileParams::VersionMode::kExact)
scene_node->op->get_scene()->scene_version = view_data.scene_version;
update->nodes.insert(scene_node_id, scene_node.Pass());
container_node->child_node_ids.push_back(scene_node_id);
}
if (params_.combinator_mode == TileParams::CombinatorMode::kPrune) {
container_node->combinator =
mojo::gfx::composition::Node::Combinator::PRUNE;
} else if (params_.combinator_mode ==
TileParams::CombinatorMode::kFallbackFlash) {
container_node->combinator =
mojo::gfx::composition::Node::Combinator::FALLBACK;
const uint32_t color_node_id =
container_node_id + kViewFallbackColorNodeIdOffset;
auto color_node = mojo::gfx::composition::Node::New();
color_node->op = mojo::gfx::composition::NodeOp::New();
color_node->op->set_rect(mojo::gfx::composition::RectNodeOp::New());
color_node->op->get_rect()->content_rect = extent.Clone();
color_node->op->get_rect()->color = mojo::gfx::composition::Color::New();
color_node->op->get_rect()->color->red = 255;
color_node->op->get_rect()->color->alpha = 255;
update->nodes.insert(color_node_id, color_node.Pass());
container_node->child_node_ids.push_back(color_node_id);
} else if (params_.combinator_mode ==
TileParams::CombinatorMode::kFallbackDim) {
container_node->combinator =
mojo::gfx::composition::Node::Combinator::FALLBACK;
const uint32_t dim_node_id =
container_node_id + kViewFallbackDimLayerNodeIdOffset;
auto dim_node = mojo::gfx::composition::Node::New();
dim_node->combinator = mojo::gfx::composition::Node::Combinator::PRUNE;
dim_node->op = mojo::gfx::composition::NodeOp::New();
dim_node->op->set_layer(mojo::gfx::composition::LayerNodeOp::New());
dim_node->op->get_layer()->layer_rect = extent.Clone();
dim_node->op->get_layer()->blend = mojo::gfx::composition::Blend::New();
dim_node->op->get_layer()->blend->alpha = 50;
if (view_data.view_info) {
const uint32_t scene_node_id =
container_node_id + kViewFallbackDimSceneNodeIdOffset;
auto scene_node = mojo::gfx::composition::Node::New();
scene_node->op = mojo::gfx::composition::NodeOp::New();
scene_node->op->set_scene(mojo::gfx::composition::SceneNodeOp::New());
scene_node->op->get_scene()->scene_resource_id = scene_resource_id;
update->nodes.insert(scene_node_id, scene_node.Pass());
dim_node->child_node_ids.push_back(scene_node_id);
}
update->nodes.insert(dim_node_id, dim_node.Pass());
container_node->child_node_ids.push_back(dim_node_id);
}
// Add the container.
update->nodes.insert(container_node_id, container_node.Pass());
root_node->child_node_ids.push_back(container_node_id);
}
// Add the root node.
update->nodes.insert(kRootNodeId, root_node.Pass());
scene()->Update(update.Pass());
// Publish the scene.
auto metadata = mojo::gfx::composition::SceneMetadata::New();
metadata->version = scene_version();
scene()->Publish(metadata.Pass());
}
TileView::ViewData::ViewData(const std::string& url, uint32_t key)
: url(url), key(key) {}
TileView::ViewData::~ViewData() {}
} // namespace examples