diff --git a/src/builders/build_monado.rs b/src/builders/build_monado.rs index 6f59dcd..a095076 100644 --- a/src/builders/build_monado.rs +++ b/src/builders/build_monado.rs @@ -51,6 +51,13 @@ pub fn get_build_monado_jobs(profile: &Profile, clean_build: bool) -> VecDeque VecDeque, + #[serde(default = "HashMap::::default")] + pub xrservice_cmake_flags: HashMap, pub opencomposite_path: String, pub opencomposite_repo: Option, pub features: ProfileFeatures, @@ -239,6 +241,8 @@ impl Default for Profile { name: "Default profile name".into(), xrservice_path: data_monado_path(), xrservice_type: XRServiceType::Monado, + xrservice_repo: None, + xrservice_cmake_flags: HashMap::::default(), opencomposite_path: data_opencomposite_path(), features: ProfileFeatures::default(), environment: HashMap::new(), @@ -248,7 +252,6 @@ impl Default for Profile { ), can_be_built: true, pull_on_build: true, - xrservice_repo: None, opencomposite_repo: None, editable: true, lighthouse_driver: LighthouseDriver::default(), diff --git a/src/ui/factories/env_var_row_factory.rs b/src/ui/factories/env_var_row_factory.rs index b22b1a2..b3fb4dd 100644 --- a/src/ui/factories/env_var_row_factory.rs +++ b/src/ui/factories/env_var_row_factory.rs @@ -2,15 +2,23 @@ use crate::ui::profile_editor::ProfileEditorMsg; use adw::prelude::*; use relm4::{factory::AsyncFactoryComponent, prelude::*, AsyncFactorySender}; +#[derive(Debug, Clone, Copy)] +pub enum VarType { + EnvVar, + XrServiceCmakeFlags, +} + #[derive(Debug)] pub struct EnvVarModel { pub name: String, value: String, + var_type: VarType, } pub struct EnvVarModelInit { pub name: String, pub value: String, + pub var_type: VarType, } #[derive(Debug)] @@ -21,8 +29,8 @@ pub enum EnvVarModelMsg { #[derive(Debug)] pub enum EnvVarModelOutMsg { - Changed(String, String), - Delete(String), + Changed(VarType, String, String), + Delete(VarType, String), } #[relm4::factory(async pub)] @@ -58,20 +66,30 @@ impl AsyncFactoryComponent for EnvVarModel { match message { Self::Input::Changed(val) => { self.value = val.clone(); - sender - .output_sender() - .emit(Self::Output::Changed(self.name.clone(), val)); + sender.output_sender().emit(Self::Output::Changed( + self.var_type, + self.name.clone(), + val, + )); } Self::Input::Delete => { - sender.output(Self::Output::Delete(self.name.clone())); + sender.output(Self::Output::Delete(self.var_type, self.name.clone())); } } } fn forward_to_parent(output: Self::Output) -> Option { Some(match output { - Self::Output::Changed(name, value) => ProfileEditorMsg::EnvVarChanged(name, value), - Self::Output::Delete(name) => ProfileEditorMsg::EnvVarDelete(name), + Self::Output::Changed(var_type, name, value) => match var_type { + VarType::EnvVar => ProfileEditorMsg::EnvVarChanged(name, value), + VarType::XrServiceCmakeFlags => { + ProfileEditorMsg::XrServiceCmakeFlagsChanged(name, value) + } + }, + Self::Output::Delete(var_type, name) => match var_type { + VarType::EnvVar => ProfileEditorMsg::EnvVarDelete(name), + VarType::XrServiceCmakeFlags => ProfileEditorMsg::XrServiceCmakeFlagsDelete(name), + }, }) } @@ -83,6 +101,7 @@ impl AsyncFactoryComponent for EnvVarModel { Self { name: init.name, value: init.value, + var_type: init.var_type, } } } diff --git a/src/ui/profile_editor.rs b/src/ui/profile_editor.rs index 881eb0f..7fde772 100644 --- a/src/ui/profile_editor.rs +++ b/src/ui/profile_editor.rs @@ -1,6 +1,6 @@ use super::{ alert::alert, - factories::env_var_row_factory::{EnvVarModel, EnvVarModelInit}, + factories::env_var_row_factory::{EnvVarModel, EnvVarModelInit, VarType}, }; use crate::{ env_var_descriptions::env_var_descriptions_as_paragraph, @@ -19,6 +19,8 @@ pub struct ProfileEditor { win: Option, #[tracker::do_not_track] env_rows: AsyncFactoryVecDeque, + #[tracker::do_not_track] + xrservice_cmake_flags_rows: AsyncFactoryVecDeque, } #[derive(Debug)] @@ -26,7 +28,10 @@ pub enum ProfileEditorMsg { Present, EnvVarChanged(String, String), EnvVarDelete(String), + XrServiceCmakeFlagsChanged(String, String), + XrServiceCmakeFlagsDelete(String), AddEnvVar(String), + AddXrServiceCmakeFlag(String), SaveProfile, } @@ -112,7 +117,7 @@ impl SimpleComponent for ProfileEditor { "specific git ref (branch, tag, commit...) by ", "appending a '#' followed by the ref.\n\n", "For launch options, you can insert %command% as ", - "a placeholder for the actual XR Service command." + "a placeholder for the actual XR Service command.", )), add: { withclones![prof]; @@ -183,6 +188,7 @@ impl SimpleComponent for ProfileEditor { ) }, }, + add: model.xrservice_cmake_flags_rows.widget(), add: opencompgrp = &adw::PreferencesGroup { set_title: "OpenComposite", set_description: Some("OpenVR driver built on top of OpenXR\n\nWhen specifying a repository, you can set a specific git ref (branch, tag, commit...) by appending a '#' followed by the ref"), @@ -377,6 +383,26 @@ impl SimpleComponent for ProfileEditor { self.env_rows.guard().remove(p); } } + Self::Input::XrServiceCmakeFlagsChanged(name, value) => { + self.profile + .borrow_mut() + .xrservice_cmake_flags + .insert(name, value); + } + Self::Input::XrServiceCmakeFlagsDelete(name) => { + self.profile + .borrow_mut() + .xrservice_cmake_flags + .remove(&name); + let pos = self + .xrservice_cmake_flags_rows + .guard() + .iter() + .position(|evr| evr.unwrap().name == name); + if let Some(p) = pos { + self.xrservice_cmake_flags_rows.guard().remove(p); + } + } Self::Input::AddEnvVar(name) => { let mut prof = self.profile.borrow_mut(); if !prof.environment.contains_key(&name) { @@ -384,9 +410,24 @@ impl SimpleComponent for ProfileEditor { self.env_rows.guard().push_back(EnvVarModelInit { name, value: "".to_string(), + var_type: VarType::EnvVar, }); } } + Self::Input::AddXrServiceCmakeFlag(name) => { + let mut prof = self.profile.borrow_mut(); + if !prof.xrservice_cmake_flags.contains_key(&name) { + prof.xrservice_cmake_flags + .insert(name.clone(), "".to_string()); + self.xrservice_cmake_flags_rows + .guard() + .push_back(EnvVarModelInit { + name, + value: "".to_string(), + var_type: VarType::XrServiceCmakeFlags, + }); + } + } } } @@ -395,31 +436,53 @@ impl SimpleComponent for ProfileEditor { root: &Self::Root, sender: ComponentSender, ) -> ComponentParts { - let add_env_popover = gtk::Popover::builder().build(); - let add_env_popover_box = gtk::Box::builder() - .orientation(gtk::Orientation::Horizontal) - .css_classes(["linked"]) - .build(); - let add_env_name_entry = gtk::Entry::builder() - .placeholder_text("Env Var Name...") - .build(); - let add_env_btn = gtk::Button::builder() - .css_classes(["suggested-action"]) - .icon_name("list-add-symbolic") - .tooltip_text("Add Env Var") - .build(); - add_env_popover_box.append(&add_env_name_entry); - add_env_popover_box.append(&add_env_btn); - add_env_popover.set_child(Some(&add_env_popover_box)); + let (add_env_var_btn, add_cmake_flag_btn) = { + macro_rules! add_var_btn { + ($name:expr, $event:expr) => {{ + let popover = gtk::Popover::builder().build(); + let popover_box = gtk::Box::builder() + .orientation(gtk::Orientation::Horizontal) + .css_classes(["linked"]) + .build(); + let name_entry = gtk::Entry::builder() + .placeholder_text(format!("{} Name...", $name)) + .build(); + let add_btn = gtk::Button::builder() + .css_classes(["suggested-action"]) + .icon_name("list-add-symbolic") + .tooltip_text("Add Env Var") + .build(); + popover_box.append(&name_entry); + popover_box.append(&add_btn); + popover.set_child(Some(&popover_box)); - let add_env_var_btn = gtk::MenuButton::builder() - .icon_name("list-add-symbolic") - .tooltip_text("Add Environment Variable") - .css_classes(["flat"]) - .popover(&add_env_popover) - .valign(gtk::Align::Start) - .halign(gtk::Align::End) - .build(); + let btn = gtk::MenuButton::builder() + .icon_name("list-add-symbolic") + .tooltip_text("Add Environment Variable") + .css_classes(["flat"]) + .popover(&popover) + .valign(gtk::Align::Start) + .halign(gtk::Align::End) + .build(); + + withclones![sender, name_entry, popover]; + add_btn.connect_clicked(move |_| { + let key_gstr = name_entry.text(); + let key = key_gstr.trim(); + if !key.is_empty() { + popover.popdown(); + name_entry.set_text(""); + sender.input($event(key.to_string())); + } + }); + btn + }}; + } + ( + add_var_btn!("Env Var", Self::Input::AddEnvVar), + add_var_btn!("XR Service CMake Flag", Self::Input::AddXrServiceCmakeFlag), + ) + }; let profile = Rc::new(RefCell::new(init.profile)); let prof = profile.clone(); @@ -435,6 +498,13 @@ impl SimpleComponent for ProfileEditor { .build(), sender.input_sender(), ), + xrservice_cmake_flags_rows: AsyncFactoryVecDeque::new( + adw::PreferencesGroup::builder() + .title("XR Service CMake Flags") + .header_suffix(&add_cmake_flag_btn) + .build(), + sender.input_sender(), + ), tracker: 0, }; { @@ -444,6 +514,18 @@ impl SimpleComponent for ProfileEditor { guard.push_back(EnvVarModelInit { name: k.clone(), value: v.clone(), + var_type: VarType::EnvVar, + }); + } + } + { + let mut guard = model.xrservice_cmake_flags_rows.guard(); + guard.clear(); + for (k, v) in prof.borrow().xrservice_cmake_flags.iter() { + guard.push_back(EnvVarModelInit { + name: k.clone(), + value: v.clone(), + var_type: VarType::XrServiceCmakeFlags, }); } } @@ -451,19 +533,6 @@ impl SimpleComponent for ProfileEditor { let widgets = view_output!(); model.win = Some(widgets.win.clone()); - { - withclones![sender, add_env_name_entry, add_env_popover]; - add_env_btn.connect_clicked(move |_| { - let name_gstr = add_env_name_entry.text(); - let name = name_gstr.trim(); - if !name.is_empty() { - add_env_popover.popdown(); - add_env_name_entry.set_text(""); - sender.input(Self::Input::AddEnvVar(name.to_string())); - } - }); - } - ComponentParts { model, widgets } } }