Chromium Code Reviews| Index: examples/ui/tile/tile_view.cc |
| diff --git a/examples/ui/tile/tile_view.cc b/examples/ui/tile/tile_view.cc |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..6fffdd1cccd108270d99a4b3ee008b91c0787e34 |
| --- /dev/null |
| +++ b/examples/ui/tile/tile_view.cc |
| @@ -0,0 +1,264 @@ |
| +// Copyright 2013 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 <algorithm> |
| + |
| +#include "examples/ui/tile/tile_view.h" |
| +#include "mojo/services/surfaces/cpp/surfaces_utils.h" |
| +#include "mojo/services/surfaces/interfaces/quads.mojom.h" |
| + |
| +namespace examples { |
| + |
| +TileView::TileView(mojo::ApplicationImpl* app_impl, |
| + const std::vector<std::string>& view_urls, |
| + const mojo::ui::ViewProvider::CreateViewCallback& callback) |
| + : app_impl_(app_impl), |
| + view_urls_(view_urls), |
| + callback_(callback), |
| + binding_(this), |
| + surface_id_namespace_(0), |
| + frame_pending_(false), |
| + weak_ptr_factory_(this) { |
| + app_impl_->ConnectToService("mojo:surfaces_service", &surfaces_); |
| + app_impl_->ConnectToService("mojo:view_manager_service", &view_manager_); |
| + |
| + surfaces_->GetIdNamespace(base::Bind(&TileView::OnSurfaceIdNamespaceAvailable, |
| + base::Unretained(this))); |
| +} |
| + |
| +TileView::~TileView() {} |
| + |
| +void TileView::OnSurfaceIdNamespaceAvailable(uint32_t id_namespace) { |
| + surface_id_namespace_ = id_namespace; |
| + InitView(); |
| +} |
| + |
| +void TileView::InitView() { |
| + // Register the view. |
| + mojo::ui::ViewPtr view; |
| + binding_.Bind(mojo::GetProxy(&view)); |
| + view_manager_->RegisterView(view.Pass(), mojo::GetProxy(&view_host_), |
| + callback_); |
| + |
| + // Connect to all child views. |
| + 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_) {
|
| + const std::string& url = *it; |
| + 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
|
| + |
| + // Start connecting to the view provider. |
| + mojo::ui::ViewProviderPtr provider; |
| + app_impl_->ConnectToService(url, &provider); |
| + provider.set_connection_error_handler(base::Bind( |
| + &TileView::OnChildConnectionError, base::Unretained(this), key, url)); |
| + |
| + // Create the view. |
| + // We include the provider reference in the callback so that the |
| + // binding will be kept alive until the callback completes. |
| + LOG(INFO) << "Connecting to view: key=" << key << ", url=" << url; |
| + provider->CreateView( |
| + 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.
|
| + mojo::ServiceProviderPtr(), |
|
abarth
2015/10/24 06:51:12
ditto
|
| + base::Bind(&TileView::OnChildCreated, base::Unretained(this), key, url, |
| + base::Passed(provider.Pass()))); |
| + } |
| +} |
| + |
| +void TileView::OnChildConnectionError(uint32_t child_key, |
| + const std::string& url) { |
| + LOG(ERROR) << "Could not connect to view: key=" << child_key |
| + << ", url=" << url; |
| +} |
| + |
| +void TileView::OnChildCreated(uint32_t child_key, |
| + const std::string& url, |
| + mojo::ui::ViewProviderPtr provider, |
| + mojo::ui::ViewTokenPtr token) { |
| + DCHECK(views_.find(child_key) == views_.end()); |
| + |
| + LOG(INFO) << "View created: key=" << child_key << ", url=" << url; |
| + view_host_->AddChild(child_key, token.Pass()); |
| + views_.emplace( |
| + 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
|
| + |
| + // Don't need the view provider anymore now that we have created the view |
| + // so stop watching for errors. |
| + provider.set_connection_error_handler(base::Closure()); |
| +} |
| + |
| +void TileView::OnChildUnavailable(uint32_t child_key, |
| + const OnChildUnavailableCallback& callback) { |
| + auto it = views_.find(child_key); |
| + DCHECK(it != views_.end()); |
| + |
| + LOG(ERROR) << "View died unexpectedly: key=" << child_key |
| + << ", url=" << it->second->url(); |
| + view_host_->RemoveChild(child_key); |
| + views_.erase(it); |
| + FinishLayout(); |
| + |
| + callback.Run(); |
| +} |
| + |
| +void TileView::OnLayout(mojo::ui::ViewLayoutParamsPtr layout_params, |
| + mojo::Array<uint32_t> children_needing_layout, |
| + const OnLayoutCallback& callback) { |
| + // Create a new surface the first time or if the size has changed. |
| + mojo::Size new_size; |
| + new_size.width = layout_params->constraints->max_width; |
| + new_size.height = layout_params->constraints->max_height; |
| + if (!surface_id_ || !size_.Equals(new_size)) { |
| + if (!surface_id_) { |
| + surface_id_ = mojo::SurfaceId::New(); |
| + surface_id_->id_namespace = surface_id_namespace_; |
| + } else { |
| + surfaces_->DestroySurface(surface_id_->local); |
| + } |
| + surface_id_->local++; |
| + size_ = new_size; |
| + surfaces_->CreateSurface(surface_id_->local); |
| + } |
|
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
|
| + |
| + // Wipe out cached layout information for children needing layout. |
| + for (auto it = children_needing_layout.begin(); |
| + it != children_needing_layout.end(); ++it) { |
|
abarth
2015/10/24 06:51:12
for (uint32_t id : children_needing_layout) {
|
| + auto view_it = views_.find(*it); |
| + if (view_it != views_.end()) |
| + view_it->second->layout_info.reset(); |
| + } |
| + |
| + // Layout all children in a row. |
| + uint32_t index = 0; |
| + for (auto it = views_.begin(); it != views_.end(); ++it, ++index) { |
| + ViewData* view_data = it->second.get(); |
| + DCHECK(!view_data->layout_pending); |
| + |
| + 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!
|
| + uint32_t child_height = new_size.height; |
| + |
| + mojo::ui::ViewLayoutParamsPtr params = mojo::ui::ViewLayoutParams::New(); |
| + params->constraints = mojo::ui::BoxConstraints::New(); |
| + params->constraints->min_width = child_width; |
| + params->constraints->max_width = child_width; |
| + params->constraints->min_height = child_height; |
| + params->constraints->max_height = child_height; |
| + 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
|
| + |
| + if (view_data->layout_info.get() && view_data->layout_params.Equals(params)) |
| + 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
|
| + |
| + view_data->layout_pending = true; |
| + view_data->layout_params = params.Clone(); |
| + view_data->layout_info.reset(); |
| + view_data->layout_bounds.x = child_width * index; |
| + view_data->layout_bounds.y = 0; |
| + view_data->layout_bounds.width = child_width; |
| + view_data->layout_bounds.height = child_height; |
| + |
| + view_host_->LayoutChild(it->first, params.Pass(), false /*provide_size*/, |
| + base::Bind(&TileView::OnChildLayoutFinished, |
| + base::Unretained(this), it->first)); |
| + } |
| + |
| + // Store the callback until layout of all children is finished. |
| + pending_layout_callback_ = callback; |
| + FinishLayout(); |
| +} |
| + |
| +void TileView::OnChildLayoutFinished( |
| + uint32_t child_key, |
| + mojo::ui::ViewLayoutInfoPtr child_layout_info) { |
| + auto it = views_.find(child_key); |
| + if (it != views_.end()) { |
| + it->second->layout_pending = false; |
| + it->second->layout_info = child_layout_info.Pass(); |
| + FinishLayout(); |
| + } |
| +} |
| + |
| +void TileView::FinishLayout() { |
| + if (frame_pending_ || pending_layout_callback_.is_null()) |
| + return; |
| + |
| + // Wait until all children have laid out. |
| + // TODO(jeffbrown): There should be a timeout on this. |
| + 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
|
| + [](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
|
| + return pair.second->layout_pending; |
| + })) |
| + return; |
| + |
| + // 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
|
| + mojo::FramePtr frame = mojo::Frame::New(); |
| + 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
|
| + |
| + mojo::Rect bounds; |
| + bounds.width = size_.width; |
| + bounds.height = size_.height; |
| + mojo::PassPtr pass = mojo::CreateDefaultPass(1, bounds); |
| + pass->shared_quad_states.resize(0u); |
| + pass->quads.resize(0u); |
| + |
| + for (auto it = views_.begin(); it != views_.end(); it++) { |
|
jamesr
2015/10/26 20:31:21
'const auto& it' ?
|
| + 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.
|
| + |
| + mojo::QuadPtr quad = mojo::Quad::New(); |
| + quad->rect = view_data->layout_bounds.Clone(); |
| + quad->rect->x = 0; |
| + quad->rect->y = 0; |
| + quad->opaque_rect = quad->rect.Clone(); |
| + quad->visible_rect = quad->rect.Clone(); |
| + quad->shared_quad_state_index = pass->shared_quad_states.size(); |
| + |
| + mojo::Size size; |
| + size.width = view_data->layout_bounds.width; |
| + size.height = view_data->layout_bounds.height; |
| + |
| + mojo::SharedQuadStatePtr quad_state = mojo::CreateDefaultSQS(size); |
| + quad_state->content_to_target_transform->matrix[3] = |
| + view_data->layout_bounds.x; |
| + pass->shared_quad_states.push_back(quad_state.Pass()); |
| + |
| + if (it->second->layout_info.get()) { |
| + quad->material = mojo::Material::SURFACE_CONTENT; |
| + quad->surface_quad_state = mojo::SurfaceQuadState::New(); |
| + quad->surface_quad_state->surface = |
| + view_data->layout_info->surface_id.Clone(); |
| + } else { |
| + quad->material = mojo::Material::SOLID_COLOR; |
| + quad->solid_color_quad_state = mojo::SolidColorQuadState::New(); |
| + quad->solid_color_quad_state->color = mojo::Color::New(); |
| + 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
|
| + } |
| + |
| + pass->quads.push_back(quad.Pass()); |
| + } |
| + |
| + frame->passes.push_back(pass.Pass()); |
| + |
| + frame_pending_ = true; |
| + surfaces_->SubmitFrame( |
| + surface_id_->local, frame.Pass(), |
| + base::Bind(&TileView::OnFrameSubmitted, base::Unretained(this))); |
| + |
| + // Submit the new layout information. |
| + mojo::ui::ViewLayoutInfoPtr info = mojo::ui::ViewLayoutInfo::New(); |
| + info->size = size_.Clone(); |
| + info->surface_id = surface_id_->Clone(); |
| + pending_layout_callback_.Run(info.Pass()); |
| + pending_layout_callback_.reset(); |
| +} |
| + |
| +void TileView::OnFrameSubmitted() { |
| + DCHECK(frame_pending_); |
| + |
| + frame_pending_ = false; |
| + FinishLayout(); |
| +} |
| + |
| +TileView::ViewData::ViewData(const std::string& url) |
| + : layout_pending(false), url_(url) {} |
| + |
| +TileView::ViewData::~ViewData() {} |
| + |
| +} // namespace examples |