Added text
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
use color_eyre::eyre::Result;
|
||||
use glam::Quat;
|
||||
use manifest_dir_macros::directory_relative_path;
|
||||
use mint::Vector3;
|
||||
use protostar::{
|
||||
@@ -7,10 +8,10 @@ use protostar::{
|
||||
};
|
||||
use stardust_xr_fusion::{
|
||||
client::{Client, FrameInfo, RootHandler},
|
||||
spatial::Spatial,
|
||||
spatial::Spatial, drawable::{Text, TextStyle, Bounds, TextFit, Alignment}, core::values::Transform,
|
||||
};
|
||||
|
||||
const APP_LIMIT: usize = 50;
|
||||
const APP_LIMIT: usize = 100;
|
||||
const APP_SIZE: f32 = 0.05;
|
||||
const GRID_PADDING: f32 = 0.01;
|
||||
|
||||
@@ -35,8 +36,10 @@ async fn main() -> Result<()> {
|
||||
|
||||
struct AppGrid {
|
||||
apps: Vec<App>,
|
||||
//style: TextStyle,
|
||||
}
|
||||
impl AppGrid {
|
||||
|
||||
fn new(client: &Client) -> Self {
|
||||
let apps = get_desktop_files()
|
||||
.into_iter()
|
||||
@@ -53,6 +56,7 @@ impl AppGrid {
|
||||
0.0,
|
||||
],
|
||||
a,
|
||||
//style,
|
||||
)
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
@@ -67,44 +71,46 @@ impl RootHandler for AppGrid {
|
||||
}
|
||||
}
|
||||
struct App {
|
||||
// _text: Text,
|
||||
_text: Text,
|
||||
_desktop_file: DesktopFile,
|
||||
protostar: ProtoStar,
|
||||
}
|
||||
|
||||
impl App {
|
||||
fn new(
|
||||
parent: &Spatial,
|
||||
position: impl Into<Vector3<f32>>,
|
||||
desktop_file: DesktopFile,
|
||||
//style: TextStyle,
|
||||
) -> Option<Self> {
|
||||
let position = position.into();
|
||||
|
||||
let style = TextStyle {
|
||||
character_height: APP_SIZE * 0.1,
|
||||
bounds: Some(Bounds {
|
||||
bounds: [APP_SIZE; 2].into(),
|
||||
fit: TextFit::Wrap,
|
||||
bounds_align: Alignment::XCenter | Alignment::YCenter,
|
||||
}),
|
||||
text_align: Alignment::XCenter | Alignment::YCenter,
|
||||
..Default::default()
|
||||
};
|
||||
let protostar = ProtoStar::create_from_desktop_file(parent, desktop_file.clone()).ok()?;
|
||||
// let text = Text::create(
|
||||
// protostar.content_parent(),
|
||||
// Transform::from_position_rotation(
|
||||
// [0.0, 0.0, APP_SIZE / 2.0],
|
||||
// Quat::from_rotation_y(PI),
|
||||
// ),
|
||||
// desktop_file.name.as_deref().unwrap_or("Unknown"),
|
||||
// TextStyle {
|
||||
// character_height: APP_SIZE * 0.1,
|
||||
// bounds: Some(Bounds {
|
||||
// bounds: [APP_SIZE; 2].into(),
|
||||
// fit: TextFit::Wrap,
|
||||
// bounds_align: Alignment::XCenter | Alignment::YCenter,
|
||||
// }),
|
||||
// text_align: Alignment::XCenter | Alignment::YCenter,
|
||||
// ..Default::default()
|
||||
// },
|
||||
// )
|
||||
// .unwrap();
|
||||
let text = Text::create(
|
||||
protostar.content_parent(),
|
||||
Transform::from_position_rotation(
|
||||
[0.0, 0.0, APP_SIZE / 2.0],
|
||||
Quat::from_rotation_y(3.14),
|
||||
),
|
||||
desktop_file.name.as_deref().unwrap_or("Unknown"),
|
||||
style,
|
||||
)
|
||||
.unwrap();
|
||||
protostar
|
||||
.content_parent()
|
||||
.set_position(None, position)
|
||||
.unwrap();
|
||||
Some(App {
|
||||
// _text: text,
|
||||
_text: text,
|
||||
_desktop_file: desktop_file,
|
||||
protostar,
|
||||
})
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use crate::xdg::{DesktopFile, Icon, RawIconType};
|
||||
use crate::xdg::{DesktopFile, Icon, IconType};
|
||||
use color_eyre::eyre::{eyre, Result};
|
||||
use glam::Quat;
|
||||
use mint::Vector3;
|
||||
@@ -19,7 +19,7 @@ use ustr::ustr;
|
||||
|
||||
fn model_from_icon(parent: &Spatial, icon: &Icon) -> Result<Model> {
|
||||
return match &icon.icon_type {
|
||||
RawIconType::Png(path) => {
|
||||
IconType::Png(path) => {
|
||||
let model = Model::create(
|
||||
parent,
|
||||
Transform::from_rotation(Quat::from_rotation_y(PI)),
|
||||
@@ -32,7 +32,7 @@ fn model_from_icon(parent: &Spatial, icon: &Icon) -> Result<Model> {
|
||||
)?;
|
||||
Ok(model)
|
||||
}
|
||||
RawIconType::Gltf(path) => Ok(Model::create(
|
||||
IconType::Gltf(path) => Ok(Model::create(
|
||||
parent,
|
||||
Transform::from_scale([0.05; 3]),
|
||||
&ResourceID::new_direct(path)?,
|
||||
@@ -52,19 +52,29 @@ pub struct ProtoStar {
|
||||
impl ProtoStar {
|
||||
pub fn create_from_desktop_file(parent: &Spatial, desktop_file: DesktopFile) -> Result<Self> {
|
||||
// dbg!(&desktop_file);
|
||||
dbg!(&desktop_file);
|
||||
let mut raw_icons = dbg!(desktop_file.get_raw_icons());
|
||||
let last_icon = raw_icons.pop();
|
||||
let icon = raw_icons
|
||||
let raw_icons = desktop_file.get_raw_icons();
|
||||
let mut icon = raw_icons
|
||||
.clone()
|
||||
.into_iter()
|
||||
.find(|i| match i.icon_type {
|
||||
RawIconType::Png(_) => false,
|
||||
RawIconType::Svg(_) => false,
|
||||
RawIconType::Gltf(_) => true,
|
||||
IconType::Gltf(_) => true,
|
||||
_ => false,
|
||||
})
|
||||
.or(last_icon)
|
||||
.map(|i| i.process(128).ok())
|
||||
.ok_or_else(|| eyre!("No compatible icons found"))?;
|
||||
.or(
|
||||
raw_icons
|
||||
.into_iter()
|
||||
.max_by_key(|i| i.size)
|
||||
);
|
||||
|
||||
match icon{
|
||||
Some(i) => {
|
||||
icon = match i.process(128) {
|
||||
Ok(i) => Some(i),
|
||||
_ => None,
|
||||
}},
|
||||
None => {},
|
||||
}
|
||||
|
||||
Self::new_raw(
|
||||
parent,
|
||||
icon,
|
||||
|
||||
37
src/xdg.rs
37
src/xdg.rs
@@ -9,7 +9,6 @@ use std::path::{Path, PathBuf};
|
||||
use std::str::FromStr;
|
||||
use std::{env, fs};
|
||||
use walkdir::WalkDir;
|
||||
use sha2::{Sha224, Digest};
|
||||
use linicon;
|
||||
|
||||
fn get_data_dirs() -> Vec<PathBuf> {
|
||||
@@ -44,7 +43,6 @@ pub fn get_desktop_files() -> Vec<PathBuf> {
|
||||
let desktop_extension = OsString::from_str("desktop").unwrap();
|
||||
// Get the list of directories to search
|
||||
let app_dirs = get_app_dirs();
|
||||
dbg!(&app_dirs);
|
||||
app_dirs
|
||||
.into_iter()
|
||||
.flat_map(|dir| {
|
||||
@@ -180,8 +178,13 @@ impl DesktopFile {
|
||||
}
|
||||
}
|
||||
|
||||
let mut icons_iter= linicon::lookup_icon(icon_name);
|
||||
let mut icons_iter= linicon::lookup_icon(icon_name).use_fallback_themes(false).peekable();
|
||||
|
||||
if icons_iter.peek().is_none(){
|
||||
//dbg!("No icons found in current theme");
|
||||
icons_iter= linicon::lookup_icon(icon_name).peekable();
|
||||
}
|
||||
|
||||
let sized_png : Vec<Icon> = icons_iter
|
||||
.filter_map(|i| i.ok())
|
||||
.filter(|i| i.icon_type != linicon::IconType::XMP) //TODO: support XMP
|
||||
@@ -191,14 +194,14 @@ impl DesktopFile {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
#[derive(Debug, PartialEq, Eq, Clone)]
|
||||
pub struct Icon {
|
||||
pub icon_type: RawIconType,
|
||||
pub icon_type: IconType,
|
||||
pub size: u16,
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
pub enum RawIconType {
|
||||
#[derive(Debug, PartialEq, Eq, Clone)]
|
||||
pub enum IconType {
|
||||
Png(PathBuf),
|
||||
Svg(PathBuf),
|
||||
Gltf(PathBuf),
|
||||
@@ -206,9 +209,9 @@ pub enum RawIconType {
|
||||
impl Icon {
|
||||
pub fn from_path(path: PathBuf, size: u16) -> Option<Icon>{
|
||||
let icon_type = match path.extension().and_then(|ext| ext.to_str()) {
|
||||
Some("png") => Some(RawIconType::Png(path)),
|
||||
Some("svg") => Some(RawIconType::Svg(path)),
|
||||
Some("glb") | Some("gltf") => Some(RawIconType::Gltf(path)),
|
||||
Some("png") => Some(IconType::Png(path)),
|
||||
Some("svg") => Some(IconType::Svg(path)),
|
||||
Some("glb") | Some("gltf") => Some(IconType::Gltf(path)),
|
||||
_ => {return None},
|
||||
}.unwrap();
|
||||
return Some(Icon{icon_type,size})
|
||||
@@ -216,7 +219,7 @@ impl Icon {
|
||||
|
||||
pub fn process(self, size: u16) -> Result<Icon, std::io::Error> {
|
||||
match self.icon_type {
|
||||
RawIconType::Svg(path) => Ok(Icon::from_path(get_png_from_svg(&path, size)?,size).unwrap()),
|
||||
IconType::Svg(path) => Ok(Icon::from_path(get_png_from_svg(&path, size)?,size).unwrap()),
|
||||
_ => Ok(self),
|
||||
}
|
||||
}
|
||||
@@ -244,8 +247,9 @@ fn test_get_icon_path() {
|
||||
|
||||
pub fn get_png_from_svg(svg_path: impl AsRef<Path>, size: u16,) -> Result<PathBuf, std::io::Error> {
|
||||
let svg_path = fs::canonicalize(svg_path)?;
|
||||
let svg_data = fs::read(svg_path.as_path())?;
|
||||
let tree = Tree::from_data(
|
||||
fs::read(svg_path.as_path())?.as_slice(),
|
||||
svg_data.as_slice(),
|
||||
&resvg::usvg::Options::default(),
|
||||
)
|
||||
.map_err(|_| ErrorKind::InvalidData)?;
|
||||
@@ -262,16 +266,9 @@ pub fn get_png_from_svg(svg_path: impl AsRef<Path>, size: u16,) -> Result<PathBu
|
||||
let image_cache_dir = cache_dir.join("protostar_icon_cache");
|
||||
|
||||
create_dir_all(&image_cache_dir).expect("Could not create image cache directory");
|
||||
|
||||
//TODO: come up with a better way to cache images system
|
||||
let mut hasher = Sha224::new();
|
||||
let mut svg_file = fs::File::open(&svg_path)?;
|
||||
io::copy(&mut svg_file, &mut hasher)?;
|
||||
let hash_bytes = hasher.finalize();
|
||||
|
||||
let png_path = image_cache_dir
|
||||
.join(format!("{}-{:02x}",svg_path.with_extension("").file_name().unwrap().to_str().unwrap(), hash_bytes))
|
||||
.with_extension("png");
|
||||
.join(format!("{}-{}.png",svg_path.file_name().unwrap().to_str().unwrap(), svg_data.len()));
|
||||
|
||||
if png_path.exists() {
|
||||
return Ok(png_path)
|
||||
|
||||
Reference in New Issue
Block a user