From 963cdcbfde5a4545b3f08677a2428fd32fcfb172 Mon Sep 17 00:00:00 2001 From: Nicola Guerrera Date: Sun, 14 May 2023 22:02:18 +0200 Subject: [PATCH] Patched app_grid --- examples/app_grid.rs | 180 +++++++++++++++++++++++++++++++++++++++---- 1 file changed, 167 insertions(+), 13 deletions(-) diff --git a/examples/app_grid.rs b/examples/app_grid.rs index 36e55e6..7336a51 100644 --- a/examples/app_grid.rs +++ b/examples/app_grid.rs @@ -1,18 +1,28 @@ use color_eyre::eyre::Result; +use glam::Quat; use manifest_dir_macros::directory_relative_path; use mint::Vector3; use protostar::{ + application::Application, protostar::ProtoStar, - xdg::{get_desktop_files, parse_desktop_file, DesktopFile}, + xdg::{get_desktop_files, parse_desktop_file, DesktopFile, Icon, IconType}, }; use stardust_xr_fusion::{ client::{Client, FrameInfo, RootHandler}, + core::values::Transform, + drawable::{Alignment, Bounds, MaterialParameter, Model, ResourceID, Text, TextFit, TextStyle}, + fields::BoxField, + node::NodeType, spatial::Spatial, }; +use stardust_xr_molecules::{GrabData, Grabbable}; +use std::f32::consts::PI; +use tween::{QuartInOut, Tweener}; const APP_LIMIT: usize = 300; const APP_SIZE: f32 = 0.05; const GRID_PADDING: f32 = 0.01; +const ACTIVATION_DISTANCE: f32 = 0.5; #[tokio::main(flavor = "current_thread")] async fn main() -> Result<()> { @@ -46,7 +56,7 @@ impl AppGrid { .enumerate() .filter(|(i, _)| *i <= APP_LIMIT) .filter_map(|(i, a)| { - App::new( + App::create_from_desktop_file( client.get_root(), [ (i % 10) as f32 * (APP_SIZE + GRID_PADDING), @@ -56,6 +66,7 @@ impl AppGrid { a, //style, ) + .ok() }) .collect::>(); AppGrid { apps } @@ -68,28 +79,171 @@ impl RootHandler for AppGrid { } } } -struct App { - _desktop_file: DesktopFile, - protostar: ProtoStar, + +fn model_from_icon(parent: &Spatial, icon: &Icon) -> Result { + return match &icon.icon_type { + IconType::Png => { + let t = Transform::from_rotation_scale( + Quat::from_rotation_x(PI / 2.0) * Quat::from_rotation_y(PI), + [APP_SIZE * 0.5; 3], + ); + + let model = Model::create( + parent, + t, + &ResourceID::new_namespaced("protostar", "hexagon/hexagon"), + )?; + model + .model_part("Hex")? + .set_material_parameter("color", MaterialParameter::Color([0.0, 1.0, 1.0, 1.0]))?; + model.model_part("Icon")?.set_material_parameter( + "diffuse", + MaterialParameter::Texture(ResourceID::Direct(icon.path.clone())), + )?; + Ok(model) + } + IconType::Gltf => Ok(Model::create( + parent, + Transform::from_scale([0.05; 3]), + &ResourceID::new_direct(icon.path.clone())?, + )?), + _ => panic!("Invalid Icon Type"), + }; } +pub struct App { + application: Application, + parent: Spatial, + position: Vector3, + grabbable: Grabbable, + _field: BoxField, + icon: Model, + label: Option, + grabbable_move: Option>, +} impl App { - fn new( + pub fn create_from_desktop_file( parent: &Spatial, position: impl Into>, desktop_file: DesktopFile, - ) -> Option { + ) -> Result { let position = position.into(); - let protostar = - ProtoStar::create_from_desktop_file(parent, position, desktop_file.clone()).ok()?; - Some(App { - _desktop_file: desktop_file, - protostar, + let field = BoxField::create(parent, Transform::default(), [APP_SIZE; 3])?; + let application = Application::create(&parent.client()?, desktop_file)?; + let icon = application.icon(128, false); + let grabbable = Grabbable::create( + parent, + Transform::from_position(position), + &field, + GrabData { + max_distance: 0.01, + frame_cancel_threshold: 50, + ..Default::default() + }, + )?; + grabbable.content_parent().set_spatial_parent(parent)?; + field.set_spatial_parent(grabbable.content_parent())?; + let icon = icon + .map(|i| model_from_icon(grabbable.content_parent(), &i)) + .unwrap_or_else(|| { + Ok(Model::create( + grabbable.content_parent(), + Transform::from_rotation_scale( + Quat::from_rotation_x(PI / 2.0) * Quat::from_rotation_y(PI), + [APP_SIZE * 0.5; 3], + ), + &ResourceID::new_namespaced("protostar", "hexagon/hexagon"), + )?) + })?; + + let label_style = TextStyle { + character_height: APP_SIZE * 2.0, + bounds: Some(Bounds { + bounds: [1.0; 2].into(), + fit: TextFit::Wrap, + bounds_align: Alignment::XCenter | Alignment::YCenter, + }), + text_align: Alignment::Center.into(), + ..Default::default() + }; + let label = application.name().and_then(|name| { + Text::create( + &icon, + Transform::from_position_rotation( + [0.0, 0.1, -(APP_SIZE * 4.0)], + Quat::from_rotation_x(PI * 0.5), + ), + name, + label_style, + ) + .ok() + }); + Ok(App { + parent: parent.alias(), + position, + grabbable, + _field: field, + label, + application, + icon, + grabbable_move: None, }) } + pub fn content_parent(&self) -> &Spatial { + self.grabbable.content_parent() + } } impl RootHandler for App { fn frame(&mut self, info: FrameInfo) { - self.protostar.frame(info); + let _ = self.grabbable.update(&info); + + if let Some(grabbable_move) = &mut self.grabbable_move { + if !grabbable_move.is_finished() { + let scale = grabbable_move.move_by(info.delta); + self.grabbable + .content_parent() + .set_position( + Some(&self.parent), + [ + self.position.x * scale, + self.position.y * scale, + self.position.z * scale, + ], + ) + .unwrap(); + } else { + if grabbable_move.final_value() == 0.0001 { + self.icon.set_enabled(false).unwrap(); + self.label.as_ref().map(|l| l.set_enabled(false).unwrap()); + } + self.grabbable_move = None; + } + } else if self.grabbable.valid() && self.grabbable.grab_action().actor_stopped() { + self.grabbable.cancel_angular_velocity(); + self.grabbable.cancel_linear_velocity(); + self.grabbable + .content_parent() + .set_position(Some(&self.parent), self.position) + .unwrap(); + + let Ok(distance_future) = self.grabbable + .content_parent() + .get_position_rotation_scale(&self.parent) + else {return}; + + let application = self.application.clone(); + let space = self.content_parent().alias(); + + //TODO: split the executable string for the args + tokio::task::spawn(async move { + let distance_vector = distance_future.await.ok().unwrap().0; + let distance = ((distance_vector.x.powi(2) + distance_vector.y.powi(2)).sqrt() + + distance_vector.z.powi(2)) + .sqrt(); + if dbg!(distance) > ACTIVATION_DISTANCE { + let _ = application.launch(&space); + } + }); + } } }