blob: 140bfc8dc818a91c77a3069fb2ad403101cb43a3 [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 anyhow::Error;
use carnelian::{
make_app_assistant, set_node_color, App, AppAssistant, Color, Coord, Size, ViewAssistant,
ViewAssistantContext, ViewAssistantPtr, ViewKey,
};
use fidl_fuchsia_math::RectF;
use fidl_fuchsia_ui_app::ViewProviderMarker;
use fidl_fuchsia_ui_gfx::{BoundingBox, Vec3, ViewProperties};
use fuchsia_component::client::{launch, launcher, App as LaunchedApp};
use fuchsia_scenic::{EntityNode, Rectangle, SessionPtr, ShapeNode, ViewHolder, ViewTokenPair};
use itertools::Itertools;
use std::collections::BTreeMap;
const BACKGROUND_Z: f32 = 0.0;
const EMBED_Z: f32 = BACKGROUND_Z - 0.1;
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;
}
#[derive(Default)]
struct EmbeddingAppAssistant;
impl AppAssistant for EmbeddingAppAssistant {
fn setup(&mut self) -> Result<(), Error> {
Ok(())
}
fn create_view_assistant(
&mut self,
_: ViewKey,
session: &SessionPtr,
) -> Result<ViewAssistantPtr, Error> {
let app = launch(
&launcher()?,
"fuchsia-pkg://fuchsia.com/spinning_square_rs#meta/spinning_square_rs.cmx".to_string(),
None,
)?;
Ok(Box::new(EmbeddingViewAssistant {
background_node: ShapeNode::new(session.clone()),
size: Size::zero(),
app,
views: BTreeMap::new(),
}))
}
}
struct ViewData {
bounds: Option<RectF>,
host_node: EntityNode,
host_view_holder: ViewHolder,
}
impl ViewData {
pub fn new(host_node: EntityNode, host_view_holder: ViewHolder) -> ViewData {
ViewData { bounds: None, host_node: host_node, host_view_holder: host_view_holder }
}
}
struct EmbeddingViewAssistant {
background_node: ShapeNode,
size: Size,
#[allow(unused)]
app: LaunchedApp,
views: BTreeMap<u32, ViewData>,
}
impl EmbeddingViewAssistant {
fn create_and_setup_view(&mut self, context: &ViewAssistantContext<'_>) -> Result<(), Error> {
let token_pair = ViewTokenPair::new()?;
let view_provider = self.app.connect_to_service::<ViewProviderMarker>()?;
view_provider.create_view(token_pair.view_token.value, None, None)?;
let holder_node = EntityNode::new(context.session().clone());
let view_holder = ViewHolder::new(
context.session().clone(),
token_pair.view_holder_token,
Some(String::from("Carnelian Embedded View")),
);
holder_node.attach(&view_holder);
context.root_node().add_child(&holder_node);
let view_data = ViewData::new(holder_node, view_holder);
self.views.insert(view_data.host_view_holder.id(), view_data);
Ok(())
}
pub fn layout(&mut self) {
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.size.height / rows as Coord).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.size.width / tiles_in_row as Coord).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 view_properties = ViewProperties {
bounding_box: BoundingBox {
min: Vec3 { x: 0.0, y: 0.0, z: 0.0 },
max: Vec3 { x: tile_bounds.width, y: tile_bounds.height, z: 0.0 },
},
inset_from_min: Vec3 { x: 0.0, y: 0.0, z: 0.0 },
inset_from_max: Vec3 { x: 0.0, y: 0.0, z: 0.0 },
focus_change: true,
downward_input: false,
};
view.host_view_holder.set_view_properties(view_properties);
view.host_node.set_translation(tile_bounds.x, tile_bounds.y, EMBED_Z);
view.bounds = Some(tile_bounds);
}
}
}
}
}
impl ViewAssistant for EmbeddingViewAssistant {
fn setup(&mut self, context: &ViewAssistantContext<'_>) -> Result<(), Error> {
set_node_color(
context.session(),
&self.background_node,
&Color { r: 0x00, g: 0xc0, b: 0x00, a: 0xff },
);
context.root_node().add_child(&self.background_node);
for _ in 1..5 {
self.create_and_setup_view(&context)?;
}
Ok(())
}
fn update(&mut self, context: &ViewAssistantContext<'_>) -> Result<(), Error> {
self.size = context.size;
let center_x = self.size.width * 0.5;
let center_y = self.size.height * 0.5;
self.background_node.set_shape(&Rectangle::new(
context.session().clone(),
self.size.width,
self.size.height,
));
self.background_node.set_translation(center_x, center_y, BACKGROUND_Z);
self.layout();
Ok(())
}
}
fn main() -> Result<(), Error> {
App::run(make_app_assistant::<EmbeddingAppAssistant>())
}