feat: initial ui prototype; valve index default profile; app config file

This commit is contained in:
Gabriele Musco 2023-06-02 22:36:17 +02:00
parent f44a25d679
commit e6a839ae19
8 changed files with 2977 additions and 2 deletions

2715
Cargo.lock generated

File diff suppressed because it is too large Load diff

View file

@ -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
View 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
)
}
}

View file

@ -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
View file

@ -0,0 +1,3 @@
pub mod valve_index;

View 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
View 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
View file

@ -0,0 +1,3 @@
pub mod main_win;