Chromium Code Reviews| OLD | NEW |
|---|---|
| (Empty) | |
| 1 // Copyright 2013 The Chromium Authors. All rights reserved. | |
| 2 // Use of this source code is governed by a BSD-style license that can be | |
| 3 // found in the LICENSE file. | |
| 4 | |
| 5 #include "examples/ui/tile/tile_view.h" | |
| 6 #include "mojo/services/surfaces/cpp/surfaces_utils.h" | |
| 7 #include "mojo/services/surfaces/interfaces/quads.mojom.h" | |
| 8 | |
| 9 namespace examples { | |
| 10 | |
| 11 TileView::TileView(mojo::ApplicationImpl* app_impl, | |
| 12 const std::vector<std::string>& view_urls, | |
| 13 const mojo::ui::ViewProvider::CreateViewCallback& callback) | |
| 14 : app_impl_(app_impl), | |
| 15 view_urls_(view_urls), | |
| 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 } | |
| 28 | |
| 29 TileView::~TileView() {} | |
| 30 | |
| 31 void TileView::OnSurfaceIdNamespaceAvailable(uint32_t id_namespace) { | |
| 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; | |
| 45 for (auto url : view_urls_) { | |
|
qsr
2015/10/27 09:01:27
const auto& ?
jeffbrown
2015/10/27 22:54:24
Done.
| |
| 46 // Start connecting to the view provider. | |
| 47 mojo::ui::ViewProviderPtr provider; | |
| 48 app_impl_->ConnectToService(url, &provider); | |
| 49 provider.set_connection_error_handler( | |
| 50 base::Bind(&TileView::OnChildConnectionError, base::Unretained(this), | |
| 51 child_key, url)); | |
| 52 | |
| 53 // Create the view. | |
| 54 // We include the provider reference in the callback so that the | |
| 55 // binding will be kept alive until the callback completes. | |
| 56 LOG(INFO) << "Connecting to view: child_key=" << child_key | |
| 57 << ", url=" << url; | |
| 58 provider->CreateView( | |
| 59 nullptr, nullptr, | |
| 60 base::Bind(&TileView::OnChildCreated, base::Unretained(this), child_key, | |
| 61 url, base::Passed(provider.Pass()))); | |
| 62 child_key++; | |
| 63 } | |
| 64 } | |
| 65 | |
| 66 void TileView::OnChildConnectionError(uint32_t child_key, | |
| 67 const std::string& url) { | |
| 68 LOG(ERROR) << "Could not connect to view: child_key=" << child_key | |
| 69 << ", url=" << url; | |
| 70 } | |
| 71 | |
| 72 void TileView::OnChildCreated(uint32_t child_key, | |
| 73 const std::string& url, | |
| 74 mojo::ui::ViewProviderPtr provider, | |
| 75 mojo::ui::ViewTokenPtr token) { | |
| 76 DCHECK(views_.find(child_key) == views_.end()); | |
| 77 LOG(INFO) << "View created: child_key=" << child_key << ", url=" << url; | |
| 78 | |
| 79 view_host_->AddChild(child_key, token.Pass()); | |
| 80 views_.emplace( | |
| 81 std::make_pair(child_key, std::unique_ptr<ViewData>(new ViewData(url)))); | |
|
jamesr
2015/10/27 22:58:27
there's a school of thought that C++ code using sm
jeffbrown
2015/10/27 23:24:19
Feels weird to depend on an EDK header here so I'l
| |
| 82 | |
| 83 // Don't need the view provider anymore now that we have created the view | |
| 84 // so stop watching for errors. | |
| 85 provider.set_connection_error_handler(base::Closure()); | |
|
qsr
2015/10/27 09:01:27
Why is this needed? The provider will be deleted a
jeffbrown
2015/10/27 22:54:24
Good point, this is redundant.
| |
| 86 } | |
| 87 | |
| 88 void TileView::OnChildUnavailable(uint32_t child_key, | |
| 89 const OnChildUnavailableCallback& callback) { | |
| 90 auto it = views_.find(child_key); | |
| 91 DCHECK(it != views_.end()); | |
| 92 LOG(ERROR) << "View died unexpectedly: child_key=" << child_key | |
| 93 << ", url=" << it->second->url(); | |
| 94 | |
| 95 std::unique_ptr<ViewData> view_data = std::move(it->second); | |
| 96 views_.erase(it); | |
| 97 | |
| 98 view_host_->RemoveChild(child_key); | |
| 99 | |
| 100 if (view_data->layout_pending) { | |
| 101 DCHECK(pending_child_layout_count_); | |
| 102 pending_child_layout_count_--; | |
| 103 FinishLayout(); | |
| 104 } | |
| 105 | |
| 106 callback.Run(); | |
| 107 } | |
| 108 | |
| 109 void TileView::OnLayout(mojo::ui::ViewLayoutParamsPtr layout_params, | |
| 110 mojo::Array<uint32_t> children_needing_layout, | |
| 111 const OnLayoutCallback& callback) { | |
| 112 // Create a new surface the first time or if the size has changed. | |
| 113 mojo::Size new_size; | |
| 114 new_size.width = layout_params->constraints->max_width; | |
| 115 new_size.height = layout_params->constraints->max_height; | |
| 116 if (!surface_id_ || !size_.Equals(new_size)) { | |
| 117 if (!surface_id_) { | |
| 118 surface_id_ = mojo::SurfaceId::New(); | |
| 119 surface_id_->id_namespace = surface_id_namespace_; | |
| 120 } else { | |
| 121 surfaces_->DestroySurface(surface_id_->local); | |
| 122 } | |
| 123 surface_id_->local++; | |
| 124 size_ = new_size; | |
| 125 surfaces_->CreateSurface(surface_id_->local); | |
| 126 } | |
| 127 | |
| 128 // Wipe out cached layout information for children needing layout. | |
| 129 for (uint32_t child_key : children_needing_layout) { | |
| 130 auto view_it = views_.find(child_key); | |
| 131 if (view_it != views_.end()) | |
| 132 view_it->second->layout_info.reset(); | |
| 133 } | |
| 134 | |
| 135 // Layout all children in a row. | |
| 136 if (!views_.empty()) { | |
| 137 uint32_t index = 0; | |
| 138 uint32_t base_width = new_size.width / views_.size(); | |
| 139 uint32_t excess_width = new_size.width % views_.size(); | |
| 140 uint32_t x = 0; | |
| 141 for (auto it = views_.begin(); it != views_.end(); ++it, ++index) { | |
| 142 ViewData* view_data = it->second.get(); | |
| 143 DCHECK(!view_data->layout_pending); | |
| 144 | |
| 145 // Distribute any excess width among the leading children. | |
| 146 uint32_t child_width = base_width; | |
| 147 if (excess_width) { | |
| 148 child_width++; | |
| 149 excess_width--; | |
| 150 } | |
| 151 uint32_t child_height = new_size.height; | |
| 152 uint32_t child_x = x; | |
| 153 x += child_width; | |
| 154 | |
| 155 view_data->layout_bounds.x = child_x; | |
| 156 view_data->layout_bounds.y = 0; | |
| 157 view_data->layout_bounds.width = child_width; | |
| 158 view_data->layout_bounds.height = child_height; | |
| 159 | |
| 160 mojo::ui::ViewLayoutParamsPtr params = mojo::ui::ViewLayoutParams::New(); | |
| 161 params->constraints = mojo::ui::BoxConstraints::New(); | |
| 162 params->constraints->min_width = child_width; | |
| 163 params->constraints->max_width = child_width; | |
| 164 params->constraints->min_height = child_height; | |
| 165 params->constraints->max_height = child_height; | |
| 166 params->device_pixel_ratio = layout_params->device_pixel_ratio; | |
| 167 | |
| 168 if (view_data->layout_info && view_data->layout_params.Equals(params)) | |
| 169 continue; // no layout work to do | |
| 170 | |
| 171 pending_child_layout_count_++; | |
| 172 view_data->layout_pending = true; | |
| 173 view_data->layout_params = params.Clone(); | |
| 174 view_data->layout_info.reset(); | |
| 175 | |
| 176 view_host_->LayoutChild(it->first, params.Pass(), | |
| 177 base::Bind(&TileView::OnChildLayoutFinished, | |
| 178 base::Unretained(this), it->first)); | |
| 179 } | |
| 180 } | |
| 181 | |
| 182 // Store the callback until layout of all children is finished. | |
| 183 pending_layout_callback_ = callback; | |
| 184 FinishLayout(); | |
| 185 } | |
| 186 | |
| 187 void TileView::OnChildLayoutFinished( | |
| 188 uint32_t child_key, | |
| 189 mojo::ui::ViewLayoutInfoPtr child_layout_info) { | |
| 190 auto it = views_.find(child_key); | |
| 191 if (it != views_.end()) { | |
| 192 ViewData* view_data = it->second.get(); | |
| 193 DCHECK(view_data->layout_pending); | |
| 194 DCHECK(pending_child_layout_count_); | |
| 195 pending_child_layout_count_--; | |
| 196 view_data->layout_pending = false; | |
| 197 view_data->layout_info = child_layout_info.Pass(); | |
| 198 FinishLayout(); | |
| 199 } | |
| 200 } | |
| 201 | |
| 202 void TileView::FinishLayout() { | |
| 203 if (frame_pending_ || pending_layout_callback_.is_null()) | |
| 204 return; | |
| 205 | |
| 206 // Wait until all children have laid out. | |
| 207 // TODO(jeffbrown): There should be a timeout on this. | |
| 208 if (pending_child_layout_count_) | |
| 209 return; | |
| 210 | |
| 211 // Produce a new frame. | |
| 212 mojo::FramePtr frame = mojo::Frame::New(); | |
| 213 frame->resources.resize(0u); | |
| 214 | |
| 215 mojo::Rect bounds; | |
| 216 bounds.width = size_.width; | |
| 217 bounds.height = size_.height; | |
| 218 mojo::PassPtr pass = mojo::CreateDefaultPass(1, bounds); | |
| 219 pass->shared_quad_states.resize(0u); | |
| 220 pass->quads.resize(0u); | |
| 221 | |
| 222 for (auto it = views_.cbegin(); it != views_.cend(); it++) { | |
| 223 const ViewData& view_data = *(it->second.get()); | |
| 224 | |
| 225 mojo::QuadPtr quad = mojo::Quad::New(); | |
| 226 quad->rect = view_data.layout_bounds.Clone(); | |
| 227 quad->rect->x = 0; | |
| 228 quad->rect->y = 0; | |
| 229 quad->opaque_rect = quad->rect.Clone(); | |
| 230 quad->visible_rect = quad->rect.Clone(); | |
| 231 quad->shared_quad_state_index = pass->shared_quad_states.size(); | |
| 232 | |
| 233 mojo::Size size; | |
| 234 size.width = view_data.layout_bounds.width; | |
| 235 size.height = view_data.layout_bounds.height; | |
| 236 | |
| 237 mojo::SharedQuadStatePtr quad_state = mojo::CreateDefaultSQS(size); | |
| 238 quad_state->content_to_target_transform->matrix[3] = | |
| 239 view_data.layout_bounds.x; | |
| 240 pass->shared_quad_states.push_back(quad_state.Pass()); | |
| 241 | |
| 242 if (it->second->layout_info) { | |
| 243 quad->material = mojo::Material::SURFACE_CONTENT; | |
| 244 quad->surface_quad_state = mojo::SurfaceQuadState::New(); | |
| 245 quad->surface_quad_state->surface = | |
| 246 view_data.layout_info->surface_id.Clone(); | |
| 247 } else { | |
| 248 quad->material = mojo::Material::SOLID_COLOR; | |
| 249 quad->solid_color_quad_state = mojo::SolidColorQuadState::New(); | |
| 250 quad->solid_color_quad_state->color = mojo::Color::New(); | |
| 251 quad->solid_color_quad_state->color->rgba = 0xffff00ff; | |
| 252 } | |
| 253 | |
| 254 pass->quads.push_back(quad.Pass()); | |
| 255 } | |
| 256 | |
| 257 frame->passes.push_back(pass.Pass()); | |
| 258 | |
| 259 frame_pending_ = true; | |
| 260 surfaces_->SubmitFrame( | |
| 261 surface_id_->local, frame.Pass(), | |
| 262 base::Bind(&TileView::OnFrameSubmitted, base::Unretained(this))); | |
| 263 | |
| 264 // Submit the new layout information. | |
| 265 mojo::ui::ViewLayoutInfoPtr info = mojo::ui::ViewLayoutInfo::New(); | |
| 266 info->size = size_.Clone(); | |
| 267 info->surface_id = surface_id_->Clone(); | |
| 268 pending_layout_callback_.Run(info.Pass()); | |
| 269 pending_layout_callback_.reset(); | |
| 270 } | |
| 271 | |
| 272 void TileView::OnFrameSubmitted() { | |
| 273 DCHECK(frame_pending_); | |
| 274 | |
| 275 frame_pending_ = false; | |
| 276 FinishLayout(); | |
| 277 } | |
| 278 | |
| 279 TileView::ViewData::ViewData(const std::string& url) | |
| 280 : layout_pending(false), url_(url) {} | |
| 281 | |
| 282 TileView::ViewData::~ViewData() {} | |
| 283 | |
| 284 } // namespace examples | |
| OLD | NEW |