OLD | NEW |
1 // Copyright 2015 The Chromium Authors. All rights reserved. | 1 // Copyright 2015 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
| 5 #include "base/bind.h" |
5 #include "examples/ui/tile/tile_view.h" | 6 #include "examples/ui/tile/tile_view.h" |
6 #include "mojo/services/surfaces/cpp/surfaces_utils.h" | 7 #include "mojo/services/geometry/cpp/geometry_util.h" |
7 #include "mojo/services/surfaces/interfaces/quads.mojom.h" | |
8 | 8 |
9 namespace examples { | 9 namespace examples { |
10 | 10 |
| 11 namespace { |
| 12 constexpr uint32_t kViewResourceIdBase = 100; |
| 13 constexpr uint32_t kViewResourceIdSpacing = 100; |
| 14 |
| 15 constexpr uint32_t kRootNodeId = mojo::gfx::composition::kSceneRootNodeId; |
| 16 constexpr uint32_t kViewNodeIdBase = 100; |
| 17 constexpr uint32_t kViewNodeIdSpacing = 100; |
| 18 constexpr uint32_t kViewSceneNodeIdOffset = 1; |
| 19 constexpr uint32_t kViewFallbackNodeIdOffset = 2; |
| 20 } // namespace |
| 21 |
11 TileView::TileView(mojo::ApplicationImpl* app_impl, | 22 TileView::TileView(mojo::ApplicationImpl* app_impl, |
12 const std::vector<std::string>& view_urls, | 23 const std::vector<std::string>& view_urls, |
13 const mojo::ui::ViewProvider::CreateViewCallback& callback) | 24 const mojo::ui::ViewProvider::CreateViewCallback& callback) |
14 : app_impl_(app_impl), | 25 : BaseView(app_impl, "Tile", callback), view_urls_(view_urls) { |
15 view_urls_(view_urls), | 26 ConnectViews(); |
16 callback_(callback), | |
17 binding_(this), | |
18 surface_id_namespace_(0), | |
19 pending_child_layout_count_(0), | |
20 frame_pending_(false), | |
21 weak_ptr_factory_(this) { | |
22 app_impl_->ConnectToService("mojo:surfaces_service", &surfaces_); | |
23 app_impl_->ConnectToService("mojo:view_manager_service", &view_manager_); | |
24 | |
25 surfaces_->GetIdNamespace(base::Bind(&TileView::OnSurfaceIdNamespaceAvailable, | |
26 base::Unretained(this))); | |
27 } | 27 } |
28 | 28 |
29 TileView::~TileView() {} | 29 TileView::~TileView() {} |
30 | 30 |
31 void TileView::OnSurfaceIdNamespaceAvailable(uint32_t id_namespace) { | 31 void TileView::ConnectViews() { |
32 surface_id_namespace_ = id_namespace; | |
33 InitView(); | |
34 } | |
35 | |
36 void TileView::InitView() { | |
37 // Register the view. | |
38 mojo::ui::ViewPtr view; | |
39 binding_.Bind(mojo::GetProxy(&view)); | |
40 view_manager_->RegisterView(view.Pass(), mojo::GetProxy(&view_host_), | |
41 callback_); | |
42 | |
43 // Connect to all child views. | |
44 uint32_t child_key = 0; | 32 uint32_t child_key = 0; |
45 for (const auto& url : view_urls_) { | 33 for (const auto& url : view_urls_) { |
46 // Start connecting to the view provider. | 34 // Start connecting to the view provider. |
47 mojo::ui::ViewProviderPtr provider; | 35 mojo::ui::ViewProviderPtr provider; |
48 app_impl_->ConnectToService(url, &provider); | 36 app_impl()->ConnectToService(url, &provider); |
49 provider.set_connection_error_handler( | 37 provider.set_connection_error_handler( |
50 base::Bind(&TileView::OnChildConnectionError, base::Unretained(this), | 38 base::Bind(&TileView::OnChildConnectionError, base::Unretained(this), |
51 child_key, url)); | 39 child_key, url)); |
52 | 40 |
53 // Create the view. | 41 // Create the view. |
54 // We include the provider reference in the callback so that the | 42 // We include the provider reference in the callback so that the |
55 // binding will be kept alive until the callback completes. | 43 // binding will be kept alive until the callback completes. |
56 LOG(INFO) << "Connecting to view: child_key=" << child_key | 44 LOG(INFO) << "Connecting to view: child_key=" << child_key |
57 << ", url=" << url; | 45 << ", url=" << url; |
58 provider->CreateView( | 46 provider->CreateView( |
(...skipping 10 matching lines...) Expand all Loading... |
69 << ", url=" << url; | 57 << ", url=" << url; |
70 } | 58 } |
71 | 59 |
72 void TileView::OnChildCreated(uint32_t child_key, | 60 void TileView::OnChildCreated(uint32_t child_key, |
73 const std::string& url, | 61 const std::string& url, |
74 mojo::ui::ViewProviderPtr provider, | 62 mojo::ui::ViewProviderPtr provider, |
75 mojo::ui::ViewTokenPtr token) { | 63 mojo::ui::ViewTokenPtr token) { |
76 DCHECK(views_.find(child_key) == views_.end()); | 64 DCHECK(views_.find(child_key) == views_.end()); |
77 LOG(INFO) << "View created: child_key=" << child_key << ", url=" << url; | 65 LOG(INFO) << "View created: child_key=" << child_key << ", url=" << url; |
78 | 66 |
79 view_host_->AddChild(child_key, token.Pass()); | 67 view_host()->AddChild(child_key, token.Pass()); |
80 views_.emplace( | 68 views_.emplace(std::make_pair( |
81 std::make_pair(child_key, std::unique_ptr<ViewData>(new ViewData(url)))); | 69 child_key, std::unique_ptr<ViewData>(new ViewData(url, child_key)))); |
82 | 70 |
83 // Note that the view provider will be destroyed once this function | 71 // Note that the view provider will be destroyed once this function |
84 // returns which is fine now that we are done creating the view. | 72 // returns which is fine now that we are done creating the view. |
85 } | 73 } |
86 | 74 |
87 void TileView::OnChildUnavailable(uint32_t child_key, | 75 void TileView::OnChildUnavailable(uint32_t child_key, |
88 const OnChildUnavailableCallback& callback) { | 76 const OnChildUnavailableCallback& callback) { |
89 auto it = views_.find(child_key); | 77 auto it = views_.find(child_key); |
90 DCHECK(it != views_.end()); | 78 DCHECK(it != views_.end()); |
91 LOG(ERROR) << "View died unexpectedly: child_key=" << child_key | 79 LOG(ERROR) << "View died unexpectedly: child_key=" << child_key |
92 << ", url=" << it->second->url; | 80 << ", url=" << it->second->url; |
93 | 81 |
94 std::unique_ptr<ViewData> view_data = std::move(it->second); | 82 std::unique_ptr<ViewData> view_data = std::move(it->second); |
95 views_.erase(it); | 83 views_.erase(it); |
96 | 84 |
97 view_host_->RemoveChild(child_key); | 85 view_host()->RemoveChild(child_key); |
98 | 86 |
99 if (view_data->layout_pending) { | 87 if (view_data->layout_pending) { |
100 DCHECK(pending_child_layout_count_); | 88 DCHECK(pending_child_layout_count_); |
101 pending_child_layout_count_--; | 89 pending_child_layout_count_--; |
102 FinishLayout(); | 90 FinishLayout(); |
103 } | 91 } |
104 | 92 |
105 callback.Run(); | 93 callback.Run(); |
106 } | 94 } |
107 | 95 |
108 void TileView::OnLayout(mojo::ui::ViewLayoutParamsPtr layout_params, | 96 void TileView::OnLayout(mojo::ui::ViewLayoutParamsPtr layout_params, |
109 mojo::Array<uint32_t> children_needing_layout, | 97 mojo::Array<uint32_t> children_needing_layout, |
110 const OnLayoutCallback& callback) { | 98 const OnLayoutCallback& callback) { |
111 // Create a new surface the first time or if the size has changed. | 99 size_.width = layout_params->constraints->max_width; |
112 mojo::Size new_size; | 100 size_.height = layout_params->constraints->max_height; |
113 new_size.width = layout_params->constraints->max_width; | |
114 new_size.height = layout_params->constraints->max_height; | |
115 if (!surface_id_ || !size_.Equals(new_size)) { | |
116 if (!surface_id_) { | |
117 surface_id_ = mojo::SurfaceId::New(); | |
118 surface_id_->id_namespace = surface_id_namespace_; | |
119 } else { | |
120 surfaces_->DestroySurface(surface_id_->local); | |
121 } | |
122 surface_id_->local++; | |
123 size_ = new_size; | |
124 surfaces_->CreateSurface(surface_id_->local); | |
125 } | |
126 | 101 |
127 // Wipe out cached layout information for children needing layout. | 102 // Wipe out cached layout information for children needing layout. |
128 for (uint32_t child_key : children_needing_layout) { | 103 for (uint32_t child_key : children_needing_layout) { |
129 auto view_it = views_.find(child_key); | 104 auto view_it = views_.find(child_key); |
130 if (view_it != views_.end()) | 105 if (view_it != views_.end()) |
131 view_it->second->layout_info.reset(); | 106 view_it->second->layout_info.reset(); |
132 } | 107 } |
133 | 108 |
134 // Layout all children in a row. | 109 // Layout all children in a row. |
135 if (!views_.empty()) { | 110 if (!views_.empty()) { |
136 uint32_t index = 0; | 111 uint32_t index = 0; |
137 uint32_t base_width = new_size.width / views_.size(); | 112 uint32_t base_width = size_.width / views_.size(); |
138 uint32_t excess_width = new_size.width % views_.size(); | 113 uint32_t excess_width = size_.width % views_.size(); |
139 uint32_t x = 0; | 114 uint32_t x = 0; |
140 for (auto it = views_.begin(); it != views_.end(); ++it, ++index) { | 115 for (auto it = views_.begin(); it != views_.end(); ++it, ++index) { |
141 ViewData* view_data = it->second.get(); | 116 ViewData* view_data = it->second.get(); |
142 DCHECK(!view_data->layout_pending); | 117 DCHECK(!view_data->layout_pending); |
143 | 118 |
144 // Distribute any excess width among the leading children. | 119 // Distribute any excess width among the leading children. |
145 uint32_t child_width = base_width; | 120 uint32_t child_width = base_width; |
146 if (excess_width) { | 121 if (excess_width) { |
147 child_width++; | 122 child_width++; |
148 excess_width--; | 123 excess_width--; |
149 } | 124 } |
150 uint32_t child_height = new_size.height; | 125 uint32_t child_height = size_.height; |
151 uint32_t child_x = x; | 126 uint32_t child_x = x; |
152 x += child_width; | 127 x += child_width; |
153 | 128 |
154 view_data->layout_bounds.x = child_x; | 129 view_data->layout_bounds.x = child_x; |
155 view_data->layout_bounds.y = 0; | 130 view_data->layout_bounds.y = 0; |
156 view_data->layout_bounds.width = child_width; | 131 view_data->layout_bounds.width = child_width; |
157 view_data->layout_bounds.height = child_height; | 132 view_data->layout_bounds.height = child_height; |
158 | 133 |
159 mojo::ui::ViewLayoutParamsPtr params = mojo::ui::ViewLayoutParams::New(); | 134 mojo::ui::ViewLayoutParamsPtr params = mojo::ui::ViewLayoutParams::New(); |
160 params->constraints = mojo::ui::BoxConstraints::New(); | 135 params->constraints = mojo::ui::BoxConstraints::New(); |
161 params->constraints->min_width = child_width; | 136 params->constraints->min_width = child_width; |
162 params->constraints->max_width = child_width; | 137 params->constraints->max_width = child_width; |
163 params->constraints->min_height = child_height; | 138 params->constraints->min_height = child_height; |
164 params->constraints->max_height = child_height; | 139 params->constraints->max_height = child_height; |
165 params->device_pixel_ratio = layout_params->device_pixel_ratio; | 140 params->device_pixel_ratio = layout_params->device_pixel_ratio; |
166 | 141 |
167 if (view_data->layout_info && view_data->layout_params.Equals(params)) | 142 if (view_data->layout_info && view_data->layout_params.Equals(params)) |
168 continue; // no layout work to do | 143 continue; // no layout work to do |
169 | 144 |
170 pending_child_layout_count_++; | 145 pending_child_layout_count_++; |
171 view_data->layout_pending = true; | 146 view_data->layout_pending = true; |
172 view_data->layout_params = params.Clone(); | 147 view_data->layout_params = params.Clone(); |
173 view_data->layout_info.reset(); | 148 view_data->layout_info.reset(); |
174 | 149 |
175 view_host_->LayoutChild(it->first, params.Pass(), | 150 view_host()->LayoutChild(it->first, params.Pass(), |
176 base::Bind(&TileView::OnChildLayoutFinished, | 151 base::Bind(&TileView::OnChildLayoutFinished, |
177 base::Unretained(this), it->first)); | 152 base::Unretained(this), it->first)); |
178 } | 153 } |
179 } | 154 } |
180 | 155 |
181 // Store the callback until layout of all children is finished. | 156 // Store the callback until layout of all children is finished. |
182 pending_layout_callback_ = callback; | 157 pending_layout_callback_ = callback; |
183 FinishLayout(); | 158 FinishLayout(); |
184 } | 159 } |
185 | 160 |
186 void TileView::OnChildLayoutFinished( | 161 void TileView::OnChildLayoutFinished( |
187 uint32_t child_key, | 162 uint32_t child_key, |
188 mojo::ui::ViewLayoutInfoPtr child_layout_info) { | 163 mojo::ui::ViewLayoutInfoPtr child_layout_info) { |
189 auto it = views_.find(child_key); | 164 auto it = views_.find(child_key); |
190 if (it != views_.end()) { | 165 if (it != views_.end()) { |
191 ViewData* view_data = it->second.get(); | 166 ViewData* view_data = it->second.get(); |
192 DCHECK(view_data->layout_pending); | 167 DCHECK(view_data->layout_pending); |
193 DCHECK(pending_child_layout_count_); | 168 DCHECK(pending_child_layout_count_); |
194 pending_child_layout_count_--; | 169 pending_child_layout_count_--; |
195 view_data->layout_pending = false; | 170 view_data->layout_pending = false; |
196 view_data->layout_info = child_layout_info.Pass(); | 171 view_data->layout_info = child_layout_info.Pass(); |
197 FinishLayout(); | 172 FinishLayout(); |
198 } | 173 } |
199 } | 174 } |
200 | 175 |
201 void TileView::FinishLayout() { | 176 void TileView::FinishLayout() { |
202 if (frame_pending_ || pending_layout_callback_.is_null()) | 177 if (pending_layout_callback_.is_null()) |
203 return; | 178 return; |
204 | 179 |
205 // Wait until all children have laid out. | 180 // Wait until all children have laid out. |
206 // TODO(jeffbrown): There should be a timeout on this. | 181 // TODO(jeffbrown): There should be a timeout on this. |
207 if (pending_child_layout_count_) | 182 if (pending_child_layout_count_) |
208 return; | 183 return; |
209 | 184 |
210 // Produce a new frame. | 185 // Update the scene. |
211 mojo::FramePtr frame = mojo::Frame::New(); | 186 // TODO: only send the resources once, be more incremental |
212 frame->resources.resize(0u); | 187 auto update = mojo::gfx::composition::SceneUpdate::New(); |
213 | 188 |
214 mojo::Rect bounds; | 189 // Create the root node. |
215 bounds.width = size_.width; | 190 auto root_node = mojo::gfx::composition::Node::New(); |
216 bounds.height = size_.height; | |
217 mojo::PassPtr pass = mojo::CreateDefaultPass(1, bounds); | |
218 pass->shared_quad_states.resize(0u); | |
219 pass->quads.resize(0u); | |
220 | 191 |
| 192 // Add the children. |
221 for (auto it = views_.cbegin(); it != views_.cend(); it++) { | 193 for (auto it = views_.cbegin(); it != views_.cend(); it++) { |
222 const ViewData& view_data = *(it->second.get()); | 194 const ViewData& view_data = *(it->second.get()); |
| 195 const uint32_t scene_resource_id = |
| 196 kViewResourceIdBase + view_data.key * kViewResourceIdSpacing; |
| 197 const uint32_t container_node_id = |
| 198 kViewNodeIdBase + view_data.key * kViewNodeIdSpacing; |
| 199 const uint32_t scene_node_id = container_node_id + kViewSceneNodeIdOffset; |
| 200 const uint32_t fallback_node_id = |
| 201 container_node_id + kViewFallbackNodeIdOffset; |
223 | 202 |
224 mojo::QuadPtr quad = mojo::Quad::New(); | 203 mojo::Rect extent; |
225 quad->rect = view_data.layout_bounds.Clone(); | 204 extent.width = view_data.layout_bounds.width; |
226 quad->rect->x = 0; | 205 extent.height = view_data.layout_bounds.height; |
227 quad->rect->y = 0; | |
228 quad->opaque_rect = quad->rect.Clone(); | |
229 quad->visible_rect = quad->rect.Clone(); | |
230 quad->shared_quad_state_index = pass->shared_quad_states.size(); | |
231 | 206 |
232 mojo::Size size; | 207 // Create a container to represent the place where the child view |
233 size.width = view_data.layout_bounds.width; | 208 // will be presented. The children of the container provide |
234 size.height = view_data.layout_bounds.height; | 209 // fallback behavior in case the view is not available. |
| 210 auto container_node = mojo::gfx::composition::Node::New(); |
| 211 container_node->content_clip = extent.Clone(); |
| 212 container_node->content_transform = mojo::Transform::New(); |
| 213 SetTranslationTransform(container_node->content_transform.get(), |
| 214 view_data.layout_bounds.x, |
| 215 view_data.layout_bounds.y, 0.f); |
| 216 container_node->combinator = |
| 217 mojo::gfx::composition::Node::Combinator::FALLBACK; |
235 | 218 |
236 mojo::SharedQuadStatePtr quad_state = mojo::CreateDefaultSQS(size); | 219 // If we have the view, add it to the scene. |
237 quad_state->content_to_target_transform->matrix[3] = | 220 if (view_data.layout_info) { |
238 view_data.layout_bounds.x; | 221 auto scene_resource = mojo::gfx::composition::Resource::New(); |
239 pass->shared_quad_states.push_back(quad_state.Pass()); | 222 scene_resource->set_scene(mojo::gfx::composition::SceneResource::New()); |
| 223 scene_resource->get_scene()->scene_token = |
| 224 view_data.layout_info->scene_token.Clone(); |
| 225 update->resources.insert(scene_resource_id, scene_resource.Pass()); |
240 | 226 |
241 if (it->second->layout_info) { | 227 auto scene_node = mojo::gfx::composition::Node::New(); |
242 quad->material = mojo::Material::SURFACE_CONTENT; | 228 scene_node->op = mojo::gfx::composition::NodeOp::New(); |
243 quad->surface_quad_state = mojo::SurfaceQuadState::New(); | 229 scene_node->op->set_scene(mojo::gfx::composition::SceneNodeOp::New()); |
244 quad->surface_quad_state->surface = | 230 scene_node->op->get_scene()->scene_resource_id = scene_resource_id; |
245 view_data.layout_info->surface_id.Clone(); | 231 update->nodes.insert(scene_node_id, scene_node.Pass()); |
| 232 container_node->child_node_ids.push_back(scene_node_id); |
246 } else { | 233 } else { |
247 quad->material = mojo::Material::SOLID_COLOR; | 234 update->resources.insert(fallback_node_id, nullptr); |
248 quad->solid_color_quad_state = mojo::SolidColorQuadState::New(); | 235 update->nodes.insert(scene_node_id, nullptr); |
249 quad->solid_color_quad_state->color = mojo::Color::New(); | |
250 quad->solid_color_quad_state->color->rgba = 0xffff00ff; | |
251 } | 236 } |
252 | 237 |
253 pass->quads.push_back(quad.Pass()); | 238 // Add the fallback content. |
| 239 auto fallback_node = mojo::gfx::composition::Node::New(); |
| 240 fallback_node->op = mojo::gfx::composition::NodeOp::New(); |
| 241 fallback_node->op->set_rect(mojo::gfx::composition::RectNodeOp::New()); |
| 242 fallback_node->op->get_rect()->content_rect = extent.Clone(); |
| 243 fallback_node->op->get_rect()->color = mojo::gfx::composition::Color::New(); |
| 244 fallback_node->op->get_rect()->color->red = 255; |
| 245 fallback_node->op->get_rect()->color->alpha = 255; |
| 246 update->nodes.insert(fallback_node_id, fallback_node.Pass()); |
| 247 container_node->child_node_ids.push_back(fallback_node_id); |
| 248 |
| 249 // Add the container. |
| 250 update->nodes.insert(container_node_id, container_node.Pass()); |
| 251 root_node->child_node_ids.push_back(container_node_id); |
254 } | 252 } |
255 | 253 |
256 frame->passes.push_back(pass.Pass()); | 254 // Add the root node. |
| 255 update->nodes.insert(kRootNodeId, root_node.Pass()); |
257 | 256 |
258 frame_pending_ = true; | 257 // Publish the scene. |
259 surfaces_->SubmitFrame( | 258 scene()->Update(update.Pass()); |
260 surface_id_->local, frame.Pass(), | 259 scene()->Publish(nullptr); |
261 base::Bind(&TileView::OnFrameSubmitted, base::Unretained(this))); | |
262 | 260 |
263 // Submit the new layout information. | 261 // Submit the new layout information. |
264 mojo::ui::ViewLayoutInfoPtr info = mojo::ui::ViewLayoutInfo::New(); | 262 auto info = mojo::ui::ViewLayoutResult::New(); |
265 info->size = size_.Clone(); | 263 info->size = size_.Clone(); |
266 info->surface_id = surface_id_->Clone(); | |
267 pending_layout_callback_.Run(info.Pass()); | 264 pending_layout_callback_.Run(info.Pass()); |
268 pending_layout_callback_.reset(); | 265 pending_layout_callback_.reset(); |
269 } | 266 } |
270 | 267 |
271 void TileView::OnFrameSubmitted() { | 268 TileView::ViewData::ViewData(const std::string& url, uint32_t key) |
272 DCHECK(frame_pending_); | 269 : url(url), key(key), layout_pending(false) {} |
273 | |
274 frame_pending_ = false; | |
275 FinishLayout(); | |
276 } | |
277 | |
278 TileView::ViewData::ViewData(const std::string& url) | |
279 : url(url), layout_pending(false) {} | |
280 | 270 |
281 TileView::ViewData::~ViewData() {} | 271 TileView::ViewData::~ViewData() {} |
282 | 272 |
283 } // namespace examples | 273 } // namespace examples |
OLD | NEW |