| // 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)) |
| } |