blob: 35eb69754f035ce2b408284c694d785cf98a632f [file] [log] [blame]
// Copyright 2018 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
use carnelian::{App, AppAssistant, ViewAssistant, ViewAssistantContext, ViewAssistantPtr};
use failure::Error;
use fidl::encoding::OutOfLine;
use fidl::endpoints::create_endpoints;
use fidl_fuchsia_math::{InsetF, RectF, SizeF};
use fidl_fuchsia_ui_gfx::{self as gfx, ColorRgba};
use fidl_fuchsia_ui_viewsv1::ViewProviderMarker;
use fidl_fuchsia_ui_viewsv1::{CustomFocusBehavior, ViewLayout, ViewProperties};
use fidl_fuchsia_ui_viewsv1token::ViewOwnerMarker;
use fuchsia_app::client::{App as LaunchedApp, Launcher};
use fuchsia_scenic::{EntityNode, ImportNode, Material, Rectangle, SessionPtr, ShapeNode};
use itertools::Itertools;
use parking_lot::Mutex;
use std::collections::BTreeMap;
use std::{any::Any, cell::RefCell};
fn inset(rect: &mut RectF, border: f32) {
let inset = border.min(rect.width / 0.3).min(rect.height / 0.3);
rect.x += inset;
rect.y += inset;
let inset_width = inset * 2.0;
rect.width = rect.width - inset_width;
rect.height = rect.height - inset_width;
}
struct EmbeddingAppAssistant {}
impl AppAssistant for EmbeddingAppAssistant {
fn setup(&mut self) -> Result<(), Error> {
Ok(())
}
fn create_view_assistant(&mut self, session: &SessionPtr) -> Result<ViewAssistantPtr, Error> {
let app = Launcher::new()?.launch(
"fuchsia-pkg://fuchsia.com/spinning_square_rs#meta/spinning_square_rs.cmx".to_string(),
None,
)?;
Ok(Mutex::new(RefCell::new(Box::new(EmbeddingViewAssistant {
background_node: ShapeNode::new(session.clone()),
width: 0.0,
height: 0.0,
app,
views: BTreeMap::new(),
}))))
}
}
#[allow(unused)]
struct ViewData {
key: u32,
bounds: Option<RectF>,
host_node: EntityNode,
}
impl ViewData {
pub fn new(key: u32, host_node: EntityNode) -> ViewData {
ViewData {
key: key,
bounds: None,
host_node: host_node,
}
}
}
struct EmbeddingViewAssistant {
background_node: ShapeNode,
width: f32,
height: f32,
#[allow(unused)]
app: LaunchedApp,
views: BTreeMap<u32, ViewData>,
}
impl EmbeddingViewAssistant {
fn create_and_setup_view(
&mut self, key: u32, session: &SessionPtr,
view_container: &fidl_fuchsia_ui_viewsv1::ViewContainerProxy, import_node: &ImportNode,
) -> Result<(), Error> {
let view_provider = self.app.connect_to_service(ViewProviderMarker)?;
let (view_owner_client, view_owner_server) = create_endpoints::<ViewOwnerMarker>()?;
view_provider.create_view(view_owner_server, None)?;
let host_node = EntityNode::new(session.clone());
let host_import_token = host_node.export_as_request();
import_node.add_child(&host_node);
let view_data = ViewData::new(key, host_node);
self.views.insert(key, view_data);
view_container.add_child(key, view_owner_client, host_import_token)?;
Ok(())
}
pub fn layout(&mut self, view_container: &fidl_fuchsia_ui_viewsv1::ViewContainerProxy) {
if !self.views.is_empty() {
let num_tiles = self.views.len();
let columns = (num_tiles as f32).sqrt().ceil() as usize;
let rows = (columns + num_tiles - 1) / columns;
let tile_height = (self.height / rows as f32).floor();
for (row_index, view_chunk) in self
.views
.iter_mut()
.chunks(columns)
.into_iter()
.enumerate()
{
let tiles_in_row = if row_index == rows - 1 && (num_tiles % columns) != 0 {
num_tiles % columns
} else {
columns
};
let tile_width = (self.width / tiles_in_row as f32).floor();
for (column_index, (_key, view)) in view_chunk.enumerate() {
let mut tile_bounds = RectF {
height: tile_height,
width: tile_width,
x: column_index as f32 * tile_width,
y: row_index as f32 * tile_height,
};
inset(&mut tile_bounds, 5.0);
let mut view_properties = ViewProperties {
custom_focus_behavior: Some(Box::new(CustomFocusBehavior {
allow_focus: true,
})),
view_layout: Some(Box::new(ViewLayout {
inset: InsetF {
bottom: 0.0,
left: 0.0,
right: 0.0,
top: 0.0,
},
size: SizeF {
width: tile_bounds.width,
height: tile_bounds.height,
},
})),
};
view_container
.set_child_properties(view.key, Some(OutOfLine(&mut view_properties)))
.unwrap();
view.host_node
.set_translation(tile_bounds.x, tile_bounds.y, 0.0);
view.bounds = Some(tile_bounds);
}
}
}
}
}
impl ViewAssistant for EmbeddingViewAssistant {
fn setup(&mut self, context: &ViewAssistantContext) -> Result<(), Error> {
context
.import_node
.resource()
.set_event_mask(gfx::METRICS_EVENT_MASK);
context.import_node.add_child(&self.background_node);
let material = Material::new(context.session.clone());
material.set_color(ColorRgba {
red: 0x00,
green: 0xc0,
blue: 0x00,
alpha: 0xff,
});
self.background_node.set_material(&material);
for n in 1..5 {
self.create_and_setup_view(
n,
context.session,
context.view_container,
context.import_node,
)?;
}
Ok(())
}
fn update(&mut self, context: &ViewAssistantContext) -> Result<(), Error> {
self.width = context.width;
self.height = context.height;
let center_x = self.width * 0.5;
let center_y = self.height * 0.5;
self.background_node.set_shape(&Rectangle::new(
context.session.clone(),
self.width,
self.height,
));
self.background_node
.set_translation(center_x, center_y, 0.0);
self.layout(context.view_container);
Ok(())
}
fn handle_message(&mut self, _message: &Any) {
// If spinning square had any custom messages they
// would be handled here.
}
}
fn main() -> Result<(), Error> {
let assistant = EmbeddingAppAssistant {};
App::run(Box::new(assistant))
}