| // 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::InterfaceHandle<mojo::ApplicationConnector> app_connector, |
| mojo::InterfaceRequest<mojo::ui::ViewOwner> view_owner_request, |
| const TileParams& params) |
| : BaseView(app_connector.Pass(), 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_connector(), 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); |
| |
| 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 |