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 <algorithm> | |
| 6 | |
| 7 #include "examples/ui/tile/tile_view.h" | |
| 8 #include "mojo/services/surfaces/cpp/surfaces_utils.h" | |
| 9 #include "mojo/services/surfaces/interfaces/quads.mojom.h" | |
| 10 | |
| 11 namespace examples { | |
| 12 | |
| 13 TileView::TileView(mojo::ApplicationImpl* app_impl, | |
| 14 const std::vector<std::string>& view_urls, | |
| 15 const mojo::ui::ViewProvider::CreateViewCallback& callback) | |
| 16 : app_impl_(app_impl), | |
| 17 view_urls_(view_urls), | |
| 18 callback_(callback), | |
| 19 binding_(this), | |
| 20 surface_id_namespace_(0), | |
| 21 frame_pending_(false), | |
| 22 weak_ptr_factory_(this) { | |
| 23 app_impl_->ConnectToService("mojo:surfaces_service", &surfaces_); | |
| 24 app_impl_->ConnectToService("mojo:view_manager_service", &view_manager_); | |
| 25 | |
| 26 surfaces_->GetIdNamespace(base::Bind(&TileView::OnSurfaceIdNamespaceAvailable, | |
| 27 base::Unretained(this))); | |
| 28 } | |
| 29 | |
| 30 TileView::~TileView() {} | |
| 31 | |
| 32 void TileView::OnSurfaceIdNamespaceAvailable(uint32_t id_namespace) { | |
| 33 surface_id_namespace_ = id_namespace; | |
| 34 InitView(); | |
| 35 } | |
| 36 | |
| 37 void TileView::InitView() { | |
| 38 // Register the view. | |
| 39 mojo::ui::ViewPtr view; | |
| 40 binding_.Bind(mojo::GetProxy(&view)); | |
| 41 view_manager_->RegisterView(view.Pass(), mojo::GetProxy(&view_host_), | |
| 42 callback_); | |
| 43 | |
| 44 // Connect to all child views. | |
| 45 for (auto it = view_urls_.begin(); it != view_urls_.end(); it++) { | |
|
abarth
2015/10/24 06:51:12
for (const std::string& url : view_urls_) {
| |
| 46 const std::string& url = *it; | |
| 47 const uint32_t key = it - view_urls_.begin(); | |
|
abarth
2015/10/24 06:51:12
Oh, I see... I probably would have just used the
jeffbrown
2015/10/27 03:13:08
Honestly I just didn't know the for iterator synta
| |
| 48 | |
| 49 // Start connecting to the view provider. | |
| 50 mojo::ui::ViewProviderPtr provider; | |
| 51 app_impl_->ConnectToService(url, &provider); | |
| 52 provider.set_connection_error_handler(base::Bind( | |
| 53 &TileView::OnChildConnectionError, base::Unretained(this), key, url)); | |
| 54 | |
| 55 // Create the view. | |
| 56 // We include the provider reference in the callback so that the | |
| 57 // binding will be kept alive until the callback completes. | |
| 58 LOG(INFO) << "Connecting to view: key=" << key << ", url=" << url; | |
| 59 provider->CreateView( | |
| 60 mojo::InterfaceRequest<mojo::ServiceProvider>(), | |
|
abarth
2015/10/24 06:51:12
there's no implicit conversion from |nullptr| ?
jamesr
2015/10/26 20:31:21
there is. provider->CreateView(nullptr, nullptr, .
jeffbrown
2015/10/27 03:13:08
Done.
| |
| 61 mojo::ServiceProviderPtr(), | |
|
abarth
2015/10/24 06:51:12
ditto
| |
| 62 base::Bind(&TileView::OnChildCreated, base::Unretained(this), key, url, | |
| 63 base::Passed(provider.Pass()))); | |
| 64 } | |
| 65 } | |
| 66 | |
| 67 void TileView::OnChildConnectionError(uint32_t child_key, | |
| 68 const std::string& url) { | |
| 69 LOG(ERROR) << "Could not connect to view: key=" << child_key | |
| 70 << ", url=" << url; | |
| 71 } | |
| 72 | |
| 73 void TileView::OnChildCreated(uint32_t child_key, | |
| 74 const std::string& url, | |
| 75 mojo::ui::ViewProviderPtr provider, | |
| 76 mojo::ui::ViewTokenPtr token) { | |
| 77 DCHECK(views_.find(child_key) == views_.end()); | |
| 78 | |
| 79 LOG(INFO) << "View created: key=" << child_key << ", url=" << url; | |
| 80 view_host_->AddChild(child_key, token.Pass()); | |
| 81 views_.emplace( | |
| 82 std::make_pair(child_key, std::unique_ptr<ViewData>(new ViewData(url)))); | |
|
abarth
2015/10/24 06:51:12
We don't have std::make_unique<ViewData>(url) but
jamesr
2015/10/26 20:31:21
there's this:
https://github.com/domokit/mojo/blo
jeffbrown
2015/10/27 03:13:08
Hardly seems worth it here. I think I'll just lea
| |
| 83 | |
| 84 // Don't need the view provider anymore now that we have created the view | |
| 85 // so stop watching for errors. | |
| 86 provider.set_connection_error_handler(base::Closure()); | |
| 87 } | |
| 88 | |
| 89 void TileView::OnChildUnavailable(uint32_t child_key, | |
| 90 const OnChildUnavailableCallback& callback) { | |
| 91 auto it = views_.find(child_key); | |
| 92 DCHECK(it != views_.end()); | |
| 93 | |
| 94 LOG(ERROR) << "View died unexpectedly: key=" << child_key | |
| 95 << ", url=" << it->second->url(); | |
| 96 view_host_->RemoveChild(child_key); | |
| 97 views_.erase(it); | |
| 98 FinishLayout(); | |
| 99 | |
| 100 callback.Run(); | |
| 101 } | |
| 102 | |
| 103 void TileView::OnLayout(mojo::ui::ViewLayoutParamsPtr layout_params, | |
| 104 mojo::Array<uint32_t> children_needing_layout, | |
| 105 const OnLayoutCallback& callback) { | |
| 106 // Create a new surface the first time or if the size has changed. | |
| 107 mojo::Size new_size; | |
| 108 new_size.width = layout_params->constraints->max_width; | |
| 109 new_size.height = layout_params->constraints->max_height; | |
| 110 if (!surface_id_ || !size_.Equals(new_size)) { | |
| 111 if (!surface_id_) { | |
| 112 surface_id_ = mojo::SurfaceId::New(); | |
| 113 surface_id_->id_namespace = surface_id_namespace_; | |
| 114 } else { | |
| 115 surfaces_->DestroySurface(surface_id_->local); | |
| 116 } | |
| 117 surface_id_->local++; | |
| 118 size_ = new_size; | |
| 119 surfaces_->CreateSurface(surface_id_->local); | |
| 120 } | |
|
abarth
2015/10/24 06:51:12
This stanza has come up several times now...
jeffbrown
2015/10/27 03:13:08
Yup. I find it helpful to be explicit in example
| |
| 121 | |
| 122 // Wipe out cached layout information for children needing layout. | |
| 123 for (auto it = children_needing_layout.begin(); | |
| 124 it != children_needing_layout.end(); ++it) { | |
|
abarth
2015/10/24 06:51:12
for (uint32_t id : children_needing_layout) {
| |
| 125 auto view_it = views_.find(*it); | |
| 126 if (view_it != views_.end()) | |
| 127 view_it->second->layout_info.reset(); | |
| 128 } | |
| 129 | |
| 130 // Layout all children in a row. | |
| 131 uint32_t index = 0; | |
| 132 for (auto it = views_.begin(); it != views_.end(); ++it, ++index) { | |
| 133 ViewData* view_data = it->second.get(); | |
| 134 DCHECK(!view_data->layout_pending); | |
| 135 | |
| 136 uint32_t child_width = new_size.width / views_.size(); | |
|
abarth
2015/10/24 06:51:12
If new_size.width doesn't divide evenly by views_.
jeffbrown
2015/10/27 03:13:08
Good point!
| |
| 137 uint32_t child_height = new_size.height; | |
| 138 | |
| 139 mojo::ui::ViewLayoutParamsPtr params = mojo::ui::ViewLayoutParams::New(); | |
| 140 params->constraints = mojo::ui::BoxConstraints::New(); | |
| 141 params->constraints->min_width = child_width; | |
| 142 params->constraints->max_width = child_width; | |
| 143 params->constraints->min_height = child_height; | |
| 144 params->constraints->max_height = child_height; | |
| 145 params->device_pixel_ratio = layout_params->device_pixel_ratio; | |
|
abarth
2015/10/24 06:51:12
This would be more future-proof if you made |param
jeffbrown
2015/10/27 03:13:08
Discussed in person. I'll see about pushing more
| |
| 146 | |
| 147 if (view_data->layout_info.get() && view_data->layout_params.Equals(params)) | |
| 148 continue; // no work to do | |
|
abarth
2015/10/24 06:51:12
This seems dubious. What if one of our grandchild
jeffbrown
2015/10/27 03:13:08
This is just an optimization to avoid an extra rou
| |
| 149 | |
| 150 view_data->layout_pending = true; | |
| 151 view_data->layout_params = params.Clone(); | |
| 152 view_data->layout_info.reset(); | |
| 153 view_data->layout_bounds.x = child_width * index; | |
| 154 view_data->layout_bounds.y = 0; | |
| 155 view_data->layout_bounds.width = child_width; | |
| 156 view_data->layout_bounds.height = child_height; | |
| 157 | |
| 158 view_host_->LayoutChild(it->first, params.Pass(), false /*provide_size*/, | |
| 159 base::Bind(&TileView::OnChildLayoutFinished, | |
| 160 base::Unretained(this), it->first)); | |
| 161 } | |
| 162 | |
| 163 // Store the callback until layout of all children is finished. | |
| 164 pending_layout_callback_ = callback; | |
| 165 FinishLayout(); | |
| 166 } | |
| 167 | |
| 168 void TileView::OnChildLayoutFinished( | |
| 169 uint32_t child_key, | |
| 170 mojo::ui::ViewLayoutInfoPtr child_layout_info) { | |
| 171 auto it = views_.find(child_key); | |
| 172 if (it != views_.end()) { | |
| 173 it->second->layout_pending = false; | |
| 174 it->second->layout_info = child_layout_info.Pass(); | |
| 175 FinishLayout(); | |
| 176 } | |
| 177 } | |
| 178 | |
| 179 void TileView::FinishLayout() { | |
| 180 if (frame_pending_ || pending_layout_callback_.is_null()) | |
| 181 return; | |
| 182 | |
| 183 // Wait until all children have laid out. | |
| 184 // TODO(jeffbrown): There should be a timeout on this. | |
| 185 if (std::any_of(views_.begin(), views_.end(), | |
|
jamesr
2015/10/26 20:31:21
this scales linearly with views, which might be a
jeffbrown
2015/10/27 03:13:08
True. This is simpler (state can't get out of syn
| |
| 186 [](const decltype(views_)::value_type& pair) { | |
|
jamesr
2015/10/26 20:31:21
this is an odd use of decltype, imo. this isn't ge
jeffbrown
2015/10/27 03:13:08
Hmm. I think the intention is pretty clear. I do
| |
| 187 return pair.second->layout_pending; | |
| 188 })) | |
| 189 return; | |
| 190 | |
| 191 // Produce a new frame. | |
|
abarth
2015/10/24 06:51:12
This system would be cleaner if the parent allocat
jeffbrown
2015/10/27 03:13:08
It might be. Depends on whether a new surface is
| |
| 192 mojo::FramePtr frame = mojo::Frame::New(); | |
| 193 frame->resources.resize(0u); | |
|
jamesr
2015/10/26 20:31:21
i *hate* that mojo arrays are nullable - the fact
jeffbrown
2015/10/27 03:13:08
Yup. It's nuts. And if I omit this line then the
| |
| 194 | |
| 195 mojo::Rect bounds; | |
| 196 bounds.width = size_.width; | |
| 197 bounds.height = size_.height; | |
| 198 mojo::PassPtr pass = mojo::CreateDefaultPass(1, bounds); | |
| 199 pass->shared_quad_states.resize(0u); | |
| 200 pass->quads.resize(0u); | |
| 201 | |
| 202 for (auto it = views_.begin(); it != views_.end(); it++) { | |
|
jamesr
2015/10/26 20:31:21
'const auto& it' ?
| |
| 203 const ViewData* view_data = it->second.get(); | |
|
jamesr
2015/10/26 20:31:21
const ViewData& view_data ? it's not useful to nul
jeffbrown
2015/10/27 03:13:08
Sure, why not.
| |
| 204 | |
| 205 mojo::QuadPtr quad = mojo::Quad::New(); | |
| 206 quad->rect = view_data->layout_bounds.Clone(); | |
| 207 quad->rect->x = 0; | |
| 208 quad->rect->y = 0; | |
| 209 quad->opaque_rect = quad->rect.Clone(); | |
| 210 quad->visible_rect = quad->rect.Clone(); | |
| 211 quad->shared_quad_state_index = pass->shared_quad_states.size(); | |
| 212 | |
| 213 mojo::Size size; | |
| 214 size.width = view_data->layout_bounds.width; | |
| 215 size.height = view_data->layout_bounds.height; | |
| 216 | |
| 217 mojo::SharedQuadStatePtr quad_state = mojo::CreateDefaultSQS(size); | |
| 218 quad_state->content_to_target_transform->matrix[3] = | |
| 219 view_data->layout_bounds.x; | |
| 220 pass->shared_quad_states.push_back(quad_state.Pass()); | |
| 221 | |
| 222 if (it->second->layout_info.get()) { | |
| 223 quad->material = mojo::Material::SURFACE_CONTENT; | |
| 224 quad->surface_quad_state = mojo::SurfaceQuadState::New(); | |
| 225 quad->surface_quad_state->surface = | |
| 226 view_data->layout_info->surface_id.Clone(); | |
| 227 } else { | |
| 228 quad->material = mojo::Material::SOLID_COLOR; | |
| 229 quad->solid_color_quad_state = mojo::SolidColorQuadState::New(); | |
| 230 quad->solid_color_quad_state->color = mojo::Color::New(); | |
| 231 quad->solid_color_quad_state->color->rgba = 0xffff00ff; | |
|
jamesr
2015/10/26 20:31:21
i think we have some named colors somewhere (not t
| |
| 232 } | |
| 233 | |
| 234 pass->quads.push_back(quad.Pass()); | |
| 235 } | |
| 236 | |
| 237 frame->passes.push_back(pass.Pass()); | |
| 238 | |
| 239 frame_pending_ = true; | |
| 240 surfaces_->SubmitFrame( | |
| 241 surface_id_->local, frame.Pass(), | |
| 242 base::Bind(&TileView::OnFrameSubmitted, base::Unretained(this))); | |
| 243 | |
| 244 // Submit the new layout information. | |
| 245 mojo::ui::ViewLayoutInfoPtr info = mojo::ui::ViewLayoutInfo::New(); | |
| 246 info->size = size_.Clone(); | |
| 247 info->surface_id = surface_id_->Clone(); | |
| 248 pending_layout_callback_.Run(info.Pass()); | |
| 249 pending_layout_callback_.reset(); | |
| 250 } | |
| 251 | |
| 252 void TileView::OnFrameSubmitted() { | |
| 253 DCHECK(frame_pending_); | |
| 254 | |
| 255 frame_pending_ = false; | |
| 256 FinishLayout(); | |
| 257 } | |
| 258 | |
| 259 TileView::ViewData::ViewData(const std::string& url) | |
| 260 : layout_pending(false), url_(url) {} | |
| 261 | |
| 262 TileView::ViewData::~ViewData() {} | |
| 263 | |
| 264 } // namespace examples | |
| OLD | NEW |