From c582c85a1a8cc638c7e5b75299ed9ff813c95780 Mon Sep 17 00:00:00 2001 From: Nova Date: Tue, 20 Feb 2024 23:12:23 -0500 Subject: [PATCH] feat: save/restore state --- Cargo.lock | 20 ++++++----- Cargo.toml | 1 + app_grid/src/main.rs | 8 +++-- hexagon_launcher/Cargo.toml | 1 + hexagon_launcher/src/app.rs | 32 +++++++++++------ hexagon_launcher/src/main.rs | 66 +++++++++++++++++++++++++++++------- protostar/src/application.rs | 5 +-- single/src/single.rs | 2 +- sirius/Cargo.toml | 1 + sirius/src/main.rs | 29 ++++++++++++---- 10 files changed, 119 insertions(+), 46 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index df03d5f..324987f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -846,6 +846,7 @@ dependencies = [ "manifest-dir-macros", "mint", "protostar", + "serde", "stardust-xr-fusion", "stardust-xr-molecules", "tokio", @@ -1828,18 +1829,18 @@ checksum = "b0293b4b29daaf487284529cc2f5675b8e57c61f70167ba415a463651fd6a918" [[package]] name = "serde" -version = "1.0.196" +version = "1.0.197" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "870026e60fa08c69f064aa766c10f10b1d62db9ccd4d0abb206472bee0ce3b32" +checksum = "3fb1c873e1b9b056a4dc4c0c198b24c3ffa059243875552b2bd0933b1aee4ce2" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.196" +version = "1.0.197" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33c85360c95e7d137454dc81d9a4ed2b8efd8fbe19cee57357b32b9771fccb67" +checksum = "7eb0b34b42edc17f6b7cac84a52a1c5f0e1bb2227e997ca9011ea3dd34e8610b" dependencies = [ "proc-macro2", "quote", @@ -1974,6 +1975,7 @@ dependencies = [ "manifest-dir-macros", "mint", "protostar", + "serde", "stardust-xr-fusion", "stardust-xr-molecules", "tokio", @@ -2016,7 +2018,7 @@ checksum = "2f2b15926089e5526bb2dd738a2eb0e59034356e06eb71e1cd912358c0e62c4d" [[package]] name = "stardust-xr" version = "0.44.0" -source = "git+https://github.com/StardustXR/core.git#6904244bda0346a9689358d5cd1b241108423656" +source = "git+https://github.com/StardustXR/core.git#66f759f44e34caa12d1b76a8f058352b8cdeabe4" dependencies = [ "cluFlock", "color-rs", @@ -2036,7 +2038,7 @@ dependencies = [ [[package]] name = "stardust-xr-fusion" version = "0.44.0" -source = "git+https://github.com/StardustXR/core.git#6904244bda0346a9689358d5cd1b241108423656" +source = "git+https://github.com/StardustXR/core.git#66f759f44e34caa12d1b76a8f058352b8cdeabe4" dependencies = [ "color-eyre", "color-rs", @@ -2056,7 +2058,7 @@ dependencies = [ [[package]] name = "stardust-xr-fusion-codegen" version = "0.1.0" -source = "git+https://github.com/StardustXR/core.git#6904244bda0346a9689358d5cd1b241108423656" +source = "git+https://github.com/StardustXR/core.git#66f759f44e34caa12d1b76a8f058352b8cdeabe4" dependencies = [ "convert_case", "mint", @@ -2069,7 +2071,7 @@ dependencies = [ [[package]] name = "stardust-xr-molecules" version = "0.44.0" -source = "git+https://github.com/StardustXR/molecules.git#c861e5665536e45262510048f90afd4eab694132" +source = "git+https://github.com/StardustXR/molecules.git#dffa22c302e730776876940f4dac39a526d601bc" dependencies = [ "color-rs", "glam 0.25.0", @@ -2087,7 +2089,7 @@ dependencies = [ [[package]] name = "stardust-xr-schemas" version = "1.5.3" -source = "git+https://github.com/StardustXR/core.git#6904244bda0346a9689358d5cd1b241108423656" +source = "git+https://github.com/StardustXR/core.git#66f759f44e34caa12d1b76a8f058352b8cdeabe4" dependencies = [ "flatbuffers", "flexbuffers", diff --git a/Cargo.toml b/Cargo.toml index 3e42ddf..ee1fae6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,6 +3,7 @@ members = ["app_grid", "hexagon_launcher", "protostar", "single", "sirius"] [workspace.dependencies] tokio = { version = "1.32.0", features = ["rt", "tokio-macros", "sync"] } +serde = "1.0.197" [workspace.dependencies.stardust-xr-fusion] git = "https://github.com/StardustXR/core.git" diff --git a/app_grid/src/main.rs b/app_grid/src/main.rs index 909283e..ca74326 100644 --- a/app_grid/src/main.rs +++ b/app_grid/src/main.rs @@ -46,6 +46,7 @@ async fn main() -> Result<()> { } struct AppGrid { + root: Spatial, apps: Vec, //style: TextStyle, } @@ -70,7 +71,10 @@ impl AppGrid { .ok() }) .collect::>(); - AppGrid { apps } + AppGrid { + root: client.get_root().alias(), + apps, + } } } impl RootHandler for AppGrid { @@ -80,7 +84,7 @@ impl RootHandler for AppGrid { } } fn save_state(&mut self) -> ClientState { - ClientState::default() + ClientState::from_root(&self.root) } } diff --git a/hexagon_launcher/Cargo.toml b/hexagon_launcher/Cargo.toml index bb44a72..191c71e 100644 --- a/hexagon_launcher/Cargo.toml +++ b/hexagon_launcher/Cargo.toml @@ -14,5 +14,6 @@ mint = "0.5.9" tween = "2.0.1" tracing-subscriber = "0.3.17" tokio = { workspace = true } +serde = { workspace = true } stardust-xr-fusion = { workspace = true } stardust-xr-molecules = { workspace = true } diff --git a/hexagon_launcher/src/app.rs b/hexagon_launcher/src/app.rs index acdba9e..fca2640 100644 --- a/hexagon_launcher/src/app.rs +++ b/hexagon_launcher/src/app.rs @@ -20,7 +20,7 @@ use stardust_xr_molecules::{Grabbable, GrabbableSettings}; use std::f32::consts::PI; use tween::{QuartInOut, Tweener}; -use crate::{ACTIVATION_DISTANCE, APP_SIZE, DEFAULT_HEX_COLOR}; +use crate::{State, ACTIVATION_DISTANCE, APP_SIZE, DEFAULT_HEX_COLOR}; // Model handling fn model_from_icon(parent: &Spatial, icon: &Icon) -> Result { @@ -65,13 +65,13 @@ pub struct App { grabbable_shrink: Option>, grabbable_grow: Option>, grabbable_move: Option>, - currently_shown: bool, } impl App { pub fn create_from_desktop_file( parent: &Spatial, position: impl Into>, desktop_file: DesktopFile, + state: &State, ) -> Result { let position = position.into(); let field = BoxField::create(parent, Transform::identity(), [APP_SIZE; 3])?; @@ -87,6 +87,9 @@ impl App { ..Default::default() }, )?; + if !state.unfurled { + grabbable.set_enabled(false)?; + } grabbable.content_parent().set_spatial_parent(parent)?; field.set_spatial_parent(grabbable.content_parent())?; let icon = icon @@ -101,6 +104,9 @@ impl App { &ResourceID::new_namespaced("protostar", "hexagon/hexagon"), )?) })?; + if !state.unfurled { + icon.set_enabled(false)?; + } let label_style = TextStyle { character_height: APP_SIZE * 2.0, @@ -127,6 +133,12 @@ impl App { ) .ok() }); + if !state.unfurled { + if let Some(label) = label.as_ref() { + label.set_enabled(false)?; + } + } + Ok(App { parent: parent.alias(), position, @@ -138,27 +150,25 @@ impl App { grabbable_shrink: None, grabbable_grow: None, grabbable_move: None, - currently_shown: true, }) } pub fn content_parent(&self) -> &Spatial { self.grabbable.content_parent() } - pub fn toggle(&mut self) { - self.grabbable.set_enabled(!self.currently_shown).unwrap(); - if self.currently_shown { - self.grabbable_move = Some(Tweener::quart_in_out(1.0, 0.0001, 0.25)); //TODO make the scale a parameter - } else { + pub fn apply_state(&mut self, state: &State) { + self.grabbable.set_enabled(state.unfurled).unwrap(); + if state.unfurled { self.icon.set_enabled(true).unwrap(); if let Some(label) = self.label.as_ref() { label.set_enabled(true).unwrap() } self.grabbable_move = Some(Tweener::quart_in_out(0.0001, 1.0, 0.25)); + } else { + self.grabbable_move = Some(Tweener::quart_in_out(1.0, 0.0001, 0.25)); //TODO make the scale a parameter } - self.currently_shown = !self.currently_shown; } - pub fn frame(&mut self, info: FrameInfo) { + pub fn frame(&mut self, info: FrameInfo, state: &State) { let _ = self.grabbable.update(&info); if let Some(grabbable_move) = &mut self.grabbable_move { @@ -193,7 +203,7 @@ impl App { .content_parent() .set_spatial_parent(&self.parent) .unwrap(); - if self.currently_shown { + if state.unfurled { self.grabbable_grow = Some(Tweener::quart_in_out(0.0001, 1.0, 0.25)); self.grabbable.cancel_angular_velocity(); self.grabbable.cancel_linear_velocity(); diff --git a/hexagon_launcher/src/main.rs b/hexagon_launcher/src/main.rs index 96e4f5e..146d3e0 100644 --- a/hexagon_launcher/src/main.rs +++ b/hexagon_launcher/src/main.rs @@ -8,16 +8,17 @@ use glam::Quat; use hex::{HEX_CENTER, HEX_DIRECTION_VECTORS}; use manifest_dir_macros::directory_relative_path; use protostar::xdg::{get_desktop_files, parse_desktop_file, DesktopFile}; +use serde::{Deserialize, Serialize}; use stardust_xr_fusion::{ client::{Client, ClientState, FrameInfo, RootHandler}, - core::values::ResourceID, + core::{schemas::flex::flexbuffers, values::ResourceID}, drawable::{MaterialParameter, Model, ModelPartAspect}, fields::BoxField, - node::NodeError, - spatial::{SpatialAspect, Transform}, + node::{NodeError, NodeType}, + spatial::{Spatial, SpatialAspect, Transform}, }; use stardust_xr_molecules::{touch_plane::TouchPlane, Grabbable, GrabbableSettings, PointerMode}; -use std::f32::consts::PI; +use std::{f32::consts::PI, time::Duration}; const APP_SIZE: f32 = 0.06; const PADDING: f32 = 0.005; @@ -38,7 +39,7 @@ async fn main() -> Result<()> { let (client, event_loop) = Client::connect_with_async_loop().await?; client.set_base_prefixes(&[directory_relative_path!("res")]); - let _root = client.wrap_root(AppHexGrid::new(&client))?; + let _root = client.wrap_root(AppHexGrid::new(&client).await)?; tokio::select! { _ = tokio::signal::ctrl_c() => (), @@ -47,13 +48,27 @@ async fn main() -> Result<()> { Ok(()) } +#[derive(Debug, Default, Serialize, Deserialize)] +pub struct State { + unfurled: bool, +} + struct AppHexGrid { + movable_root: Spatial, apps: Vec, button: Button, + state: State, } impl AppHexGrid { - fn new(client: &Client) -> Self { - let button = Button::new(client).unwrap(); + async fn new(client: &Client) -> Self { + let state = flexbuffers::from_slice(&client.state().data).unwrap_or_default(); + + let movable_root = + Spatial::create(client.get_root(), Transform::identity(), false).unwrap(); + + let button = Button::new(client, &client.state()).unwrap(); + tokio::time::sleep(Duration::from_millis(10)).await; // give it a bit of time to send the messages properly + let mut desktop_files: Vec = get_desktop_files() .filter_map(|d| parse_desktop_file(d).ok()) .filter(|d| !d.no_display) @@ -78,6 +93,7 @@ impl AppHexGrid { button.grabbable.content_parent(), hex.get_coords(), desktop_files.pop().unwrap(), + &state, ) .unwrap(), ); @@ -86,7 +102,12 @@ impl AppHexGrid { } radius += 1; } - AppHexGrid { apps, button } + AppHexGrid { + movable_root, + apps, + button, + state, + } } } impl RootHandler for AppHexGrid { @@ -99,8 +120,9 @@ impl RootHandler for AppHexGrid { .unwrap() .set_material_parameter("color", MaterialParameter::Color(BTN_SELECTED_COLOR)) .unwrap(); + self.state.unfurled = !self.state.unfurled; for app in &mut self.apps { - app.toggle(); + app.apply_state(&self.state); } } else if self.button.touch_plane.touch_stopped() { self.button @@ -111,12 +133,27 @@ impl RootHandler for AppHexGrid { .unwrap(); } for app in &mut self.apps { - app.frame(info); + app.frame(info, &self.state); } } fn save_state(&mut self) -> ClientState { - ClientState::default() + self.movable_root + .set_relative_transform( + self.button.grabbable.content_parent(), + Transform::from_translation([0.0; 3]), + ) + .unwrap(); + ClientState { + data: flexbuffers::to_vec(&self.state).unwrap(), + root: self.movable_root.alias(), + spatial_anchors: [( + "content_parent".to_string(), + self.button.grabbable.content_parent().alias(), + )] + .into_iter() + .collect(), + } } } @@ -126,7 +163,7 @@ struct Button { model: Model, } impl Button { - fn new(client: &Client) -> Result { + fn new(client: &Client, state: &ClientState) -> Result { let field = BoxField::create(client.get_root(), Transform::identity(), [APP_SIZE; 3])?; let grabbable = Grabbable::create( client.get_root(), @@ -161,6 +198,11 @@ impl Button { model .model_part("Hex")? .set_material_parameter("color", MaterialParameter::Color(BTN_COLOR))?; + if let Some(content_parent) = state.spatial_anchors.get("content_parent") { + grabbable + .content_parent() + .set_relative_transform(content_parent, Transform::identity())?; + } Ok(Button { touch_plane, grabbable, diff --git a/protostar/src/application.rs b/protostar/src/application.rs index 7176570..45d7079 100644 --- a/protostar/src/application.rs +++ b/protostar/src/application.rs @@ -55,10 +55,7 @@ impl Application { .ok_or(NodeError::DoesNotExist)?; tokio::task::spawn(async move { let Ok(startup_token) = client - .state_token(&ClientState { - root: Some(launch_space.alias()), - ..Default::default() - }) + .state_token(&ClientState::from_root(&launch_space)) .await else { return; diff --git a/single/src/single.rs b/single/src/single.rs index 4c2f96e..7f3a68b 100644 --- a/single/src/single.rs +++ b/single/src/single.rs @@ -243,6 +243,6 @@ impl RootHandler for Single { } fn save_state(&mut self) -> ClientState { - ClientState::default() + ClientState::from_root(self.content_parent()) } } diff --git a/sirius/Cargo.toml b/sirius/Cargo.toml index 528ca31..c9fe8bd 100644 --- a/sirius/Cargo.toml +++ b/sirius/Cargo.toml @@ -15,5 +15,6 @@ tween = "2.0.1" tracing-subscriber = "0.3.17" walkdir = "2.4.0" tokio = { workspace = true } +serde = { workspace = true } stardust-xr-fusion = { workspace = true } stardust-xr-molecules = { workspace = true } diff --git a/sirius/src/main.rs b/sirius/src/main.rs index 4ee0868..ad1bf58 100644 --- a/sirius/src/main.rs +++ b/sirius/src/main.rs @@ -8,9 +8,10 @@ use protostar::{ application::Application, xdg::{parse_desktop_file, DesktopFile, Icon, IconType}, }; +use serde::{Deserialize, Serialize}; use stardust_xr_fusion::{ client::{Client, ClientState, FrameInfo, RootHandler}, - core::values::ResourceID, + core::{schemas::flex::flexbuffers, values::ResourceID}, drawable::{ MaterialParameter, Model, ModelPartAspect, Text, TextBounds, TextFit, TextStyle, XAlign, YAlign, @@ -59,11 +60,16 @@ async fn main() -> Result<()> { Ok(()) } +#[derive(Debug, Serialize, Deserialize)] +struct State { + visible: bool, +} + struct Sirius { touch_plane: TouchPlane, model: Model, clients: Vec, - visibility: bool, + state: State, grabbable: Grabbable, } impl Sirius { @@ -113,13 +119,13 @@ impl Sirius { &ResourceID::new_namespaced("protostar", "button"), )?; field.set_spatial_parent(grabbable.content_parent())?; - let visibility = false; + let state = State { visible: false }; Ok(Sirius { touch_plane, model, clients, - visibility, + state, grabbable, }) } @@ -141,8 +147,8 @@ impl RootHandler for Sirius { self.touch_plane.update(); if self.touch_plane.touch_started() { println!("Touch started"); - self.visibility = !self.visibility; - match self.visibility { + self.state.visible = !self.state.visible; + match self.state.visible { true => { for (pos, star) in self.clients.iter().enumerate() { let mut starpos = (pos as f32 + 1.0) / 10.0; @@ -210,7 +216,16 @@ impl RootHandler for Sirius { } fn save_state(&mut self) -> ClientState { - ClientState::default() + ClientState { + data: flexbuffers::to_vec(&self.state).unwrap(), + root: self.grabbable.content_parent().alias(), + spatial_anchors: [( + "content_parent".to_string(), + self.grabbable.content_parent().alias(), + )] + .into_iter() + .collect(), + } } }