mirror of
https://gitlab.com/gabmus/envision.git
synced 2025-04-20 11:35:48 +00:00
feat: initial ui prototype; valve index default profile; app config file
This commit is contained in:
parent
f44a25d679
commit
e6a839ae19
8 changed files with 2977 additions and 2 deletions
2715
Cargo.lock
generated
2715
Cargo.lock
generated
File diff suppressed because it is too large
Load diff
|
@ -6,6 +6,10 @@ edition = "2021"
|
|||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
iced = "0.9.0"
|
||||
iced_aw = { version = "0.5.2", default-features = false, features = [
|
||||
"tab_bar", "tabs"
|
||||
] }
|
||||
serde = { version = "1.0.163", features = [
|
||||
"derive"
|
||||
] }
|
||||
|
|
67
src/config.rs
Normal file
67
src/config.rs
Normal file
|
@ -0,0 +1,67 @@
|
|||
use crate::{
|
||||
constants::CMD_NAME,
|
||||
file_utils::{get_config_dir, get_writer},
|
||||
};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::{fs::File, io::BufReader};
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub struct Config {
|
||||
pub selected_profile_name: String,
|
||||
pub debug_view_enabled: bool,
|
||||
}
|
||||
|
||||
fn config_file_path() -> String {
|
||||
format!(
|
||||
"{config}/{name}.json",
|
||||
config = get_config_dir(),
|
||||
name = CMD_NAME
|
||||
)
|
||||
}
|
||||
|
||||
fn default_config() -> Config {
|
||||
Config {
|
||||
// TODO: handle first start with no profile selected
|
||||
selected_profile_name: "Demo profile".to_string(),
|
||||
debug_view_enabled: false,
|
||||
}
|
||||
}
|
||||
|
||||
fn get_config_from_path(path_s: String) -> Config {
|
||||
match File::open(path_s) {
|
||||
Ok(file) => {
|
||||
let reader = BufReader::new(file);
|
||||
match serde_json::from_reader(reader) {
|
||||
Ok(config) => config,
|
||||
Err(_) => default_config(),
|
||||
}
|
||||
}
|
||||
Err(_) => default_config(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_config() -> Config {
|
||||
get_config_from_path(config_file_path())
|
||||
}
|
||||
|
||||
fn save_config_to_path(config: &Config, path_s: &String) -> Result<(), serde_json::Error> {
|
||||
let writer = get_writer(path_s);
|
||||
serde_json::to_writer_pretty(writer, config)
|
||||
}
|
||||
|
||||
pub fn save_config(config: &Config) -> Result<(), serde_json::Error> {
|
||||
save_config_to_path(config, &config_file_path())
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::get_config_from_path;
|
||||
|
||||
#[test]
|
||||
fn will_load_default_if_config_does_not_exist() {
|
||||
assert_eq!(
|
||||
get_config_from_path("/non/existing/file.json".into()).debug_view_enabled,
|
||||
false
|
||||
)
|
||||
}
|
||||
}
|
10
src/main.rs
10
src/main.rs
|
@ -1,6 +1,14 @@
|
|||
use iced::{Application, Settings};
|
||||
use ui::main_win::MainWin;
|
||||
|
||||
pub mod profile;
|
||||
pub mod runner;
|
||||
pub mod ui;
|
||||
pub mod config;
|
||||
pub mod constants;
|
||||
pub mod file_utils;
|
||||
pub mod profiles;
|
||||
|
||||
fn main() {
|
||||
println!("Hello, world!");
|
||||
MainWin::run(Settings::default());
|
||||
}
|
||||
|
|
3
src/profiles/mod.rs
Normal file
3
src/profiles/mod.rs
Normal file
|
@ -0,0 +1,3 @@
|
|||
|
||||
pub mod valve_index;
|
||||
|
23
src/profiles/valve_index.rs
Normal file
23
src/profiles/valve_index.rs
Normal file
|
@ -0,0 +1,23 @@
|
|||
use crate::{constants::APP_NAME, file_utils::get_data_dir, profile::Profile};
|
||||
use std::collections::HashMap;
|
||||
|
||||
pub fn valve_index_profile() -> Profile {
|
||||
let data_dir = get_data_dir();
|
||||
let mut environment: HashMap<String, String> = HashMap::new();
|
||||
environment.insert("XRT_COMPOSITOR_SCALE_PERCENTAGE".into(), "140".into());
|
||||
environment.insert("XRT_COMPOSITOR_COMPUTE".into(), "1".into());
|
||||
environment.insert("SURVIVE_GLOBALSCENESOLVER".into(), "0".into());
|
||||
environment.insert("SURVIVE_TIMECODE_OFFSET_MS".into(), "-6.94".into());
|
||||
Profile {
|
||||
name: format!("Valve Index - {name} Default", name = APP_NAME),
|
||||
monado_path: format!("{data}/monado", data = data_dir),
|
||||
openovr_path: format!("{data}/openovr", data = data_dir),
|
||||
libsurvive_path: Some(format!("{data}/libsurvive", data = data_dir)),
|
||||
basalt_path: None,
|
||||
mercury_path: None,
|
||||
libsurvive_enabled: true,
|
||||
basalt_enabled: false,
|
||||
mercury_enabled: false,
|
||||
environment,
|
||||
}
|
||||
}
|
154
src/ui/main_win.rs
Normal file
154
src/ui/main_win.rs
Normal file
|
@ -0,0 +1,154 @@
|
|||
use crate::{
|
||||
config::{get_config, save_config, Config},
|
||||
constants::APP_NAME,
|
||||
profile::{load_profile, Profile},
|
||||
};
|
||||
use iced::{
|
||||
executor,
|
||||
widget::{button, checkbox, column, pick_list, row, scrollable, text, text_input},
|
||||
Alignment, Application, Command, Element, Length, Padding, Theme,
|
||||
};
|
||||
|
||||
pub struct MainWin {
|
||||
profiles: Vec<Profile>,
|
||||
monado_active: bool,
|
||||
config: Config,
|
||||
debug_search_term: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum Message {
|
||||
Start,
|
||||
Stop,
|
||||
Tick,
|
||||
ProfileChanged(Profile),
|
||||
EditProfile,
|
||||
DebugViewChanged(bool),
|
||||
LogLevelChanged(String),
|
||||
DebugSearchChanged(String),
|
||||
}
|
||||
|
||||
impl MainWin {
|
||||
fn get_profile_by_name(&self, name: &String) -> Option<&Profile> {
|
||||
for profile in &self.profiles {
|
||||
if &profile.name == name {
|
||||
return Some(profile);
|
||||
}
|
||||
}
|
||||
return None;
|
||||
}
|
||||
|
||||
fn get_selected_profile(&self) -> Option<&Profile> {
|
||||
self.get_profile_by_name(&self.config.selected_profile_name)
|
||||
}
|
||||
}
|
||||
|
||||
impl Application for MainWin {
|
||||
type Message = Message;
|
||||
type Flags = ();
|
||||
type Executor = executor::Default;
|
||||
type Theme = Theme;
|
||||
|
||||
fn new(_flags: Self::Flags) -> (Self, Command<Message>) {
|
||||
(
|
||||
Self {
|
||||
// TODO: load profiles from disk somehow
|
||||
profiles: vec![load_profile(&"./test/files/profile.json".to_string()).unwrap()],
|
||||
monado_active: false,
|
||||
config: get_config(),
|
||||
debug_search_term: "".into(),
|
||||
},
|
||||
Command::none(),
|
||||
)
|
||||
}
|
||||
|
||||
fn title(&self) -> String {
|
||||
return APP_NAME.into();
|
||||
}
|
||||
|
||||
fn update(&mut self, msg: Message) -> Command<Message> {
|
||||
match msg {
|
||||
Message::Start => self.monado_active = true,
|
||||
Message::Stop => self.monado_active = false,
|
||||
Message::ProfileChanged(profile) => {
|
||||
if self.config.selected_profile_name != profile.name {
|
||||
self.config.selected_profile_name = profile.name;
|
||||
save_config(&self.config);
|
||||
}
|
||||
}
|
||||
Message::EditProfile => {
|
||||
println!("Edit {}", self.get_selected_profile().unwrap())
|
||||
}
|
||||
Message::DebugViewChanged(nval) => {
|
||||
self.config.debug_view_enabled = nval;
|
||||
save_config(&self.config);
|
||||
}
|
||||
Message::DebugSearchChanged(term) => self.debug_search_term = term,
|
||||
_ => println!("unhandled"),
|
||||
};
|
||||
Command::none()
|
||||
}
|
||||
|
||||
fn view(&self) -> Element<Message> {
|
||||
row![
|
||||
column![
|
||||
column![match self.monado_active {
|
||||
true => button("Stop").on_press(Message::Stop),
|
||||
false => button("Start").on_press(Message::Start),
|
||||
}
|
||||
.padding(Padding::from([6, 24])),]
|
||||
.height(Length::Fill),
|
||||
row![
|
||||
pick_list(
|
||||
self.profiles.to_vec(),
|
||||
self.get_selected_profile().cloned(),
|
||||
Message::ProfileChanged
|
||||
),
|
||||
button("Edit").on_press(Message::EditProfile),
|
||||
row![].width(Length::Fill),
|
||||
checkbox(
|
||||
"Debug View",
|
||||
self.config.debug_view_enabled,
|
||||
Message::DebugViewChanged
|
||||
),
|
||||
]
|
||||
.width(Length::Fill),
|
||||
]
|
||||
.width(Length::Fill)
|
||||
.height(Length::Fill)
|
||||
.padding(12)
|
||||
.align_items(Alignment::Center),
|
||||
match self.config.debug_view_enabled {
|
||||
true => column![
|
||||
// TODO: tab bar
|
||||
scrollable(text("Foo bar baz"))
|
||||
.width(Length::Fill)
|
||||
.height(Length::Fill),
|
||||
row![
|
||||
pick_list(
|
||||
vec![
|
||||
"Debug".to_string(),
|
||||
"Info".into(),
|
||||
"Warning".into(),
|
||||
"Error".into(),
|
||||
],
|
||||
Some("Debug".into()),
|
||||
Message::LogLevelChanged
|
||||
),
|
||||
row![].width(Length::Fill),
|
||||
text_input("Search...", self.debug_search_term.as_str())
|
||||
.on_input(Message::DebugSearchChanged),
|
||||
]
|
||||
.padding(12)
|
||||
]
|
||||
.width(Length::Fill),
|
||||
false => column![],
|
||||
}
|
||||
]
|
||||
.into()
|
||||
}
|
||||
|
||||
fn theme(&self) -> Self::Theme {
|
||||
Theme::Dark
|
||||
}
|
||||
}
|
3
src/ui/mod.rs
Normal file
3
src/ui/mod.rs
Normal file
|
@ -0,0 +1,3 @@
|
|||
|
||||
pub mod main_win;
|
||||
|
Loading…
Add table
Reference in a new issue