mirror of
https://gitlab.com/gabmus/envision.git
synced 2025-09-15 14:02:01 +00:00
feat: initial ui for profile editing
This commit is contained in:
parent
e61e1308a5
commit
50a89399e2
13 changed files with 554 additions and 19 deletions
64
Cargo.lock
generated
64
Cargo.lock
generated
|
@ -1272,6 +1272,47 @@ version = "2.3.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9b2a4787296e9989611394c33f193f676704af1686e70b8f8033ab5ba9a35a94"
|
||||
|
||||
[[package]]
|
||||
name = "phf"
|
||||
version = "0.11.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "928c6535de93548188ef63bb7c4036bd415cd8f36ad25af44b9789b2ee72a48c"
|
||||
dependencies = [
|
||||
"phf_shared",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "phf_generator"
|
||||
version = "0.11.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b1181c94580fa345f50f19d738aaa39c0ed30a600d95cb2d3e23f94266f14fbf"
|
||||
dependencies = [
|
||||
"phf_shared",
|
||||
"rand",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "phf_macros"
|
||||
version = "0.11.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "92aacdc5f16768709a569e913f7451034034178b05bdc8acda226659a3dccc66"
|
||||
dependencies = [
|
||||
"phf_generator",
|
||||
"phf_shared",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 1.0.109",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "phf_shared"
|
||||
version = "0.11.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e1fb5f6f826b772a8d4c0394209441e7d37cbbb967ae9c7e0e8134365c9ee676"
|
||||
dependencies = [
|
||||
"siphasher",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pin-project"
|
||||
version = "1.1.0"
|
||||
|
@ -1372,6 +1413,21 @@ dependencies = [
|
|||
"proc-macro2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand"
|
||||
version = "0.8.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404"
|
||||
dependencies = [
|
||||
"rand_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand_core"
|
||||
version = "0.6.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
|
||||
|
||||
[[package]]
|
||||
name = "raw-window-handle"
|
||||
version = "0.4.3"
|
||||
|
@ -1523,6 +1579,8 @@ dependencies = [
|
|||
"gettext-rs",
|
||||
"gtk4",
|
||||
"nix",
|
||||
"phf",
|
||||
"phf_macros",
|
||||
"relm4",
|
||||
"relm4-components",
|
||||
"relm4-icons",
|
||||
|
@ -1672,6 +1730,12 @@ dependencies = [
|
|||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "siphasher"
|
||||
version = "0.3.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7bd3e3206899af3f8b12af284fafc038cc1dc2b41d1b89dd17297221c5d225de"
|
||||
|
||||
[[package]]
|
||||
name = "slab"
|
||||
version = "0.4.8"
|
||||
|
|
|
@ -16,12 +16,14 @@ gtk4 = { version = "0.6.6", features = [
|
|||
"v4_10"
|
||||
] }
|
||||
nix = "0.26.2"
|
||||
phf = "0.11.1"
|
||||
phf_macros = "0.11.1"
|
||||
relm4 = { version = "0.6.0", features = [
|
||||
"libadwaita"
|
||||
] }
|
||||
relm4-components = "0.6.0"
|
||||
relm4-icons = { version = "0.6.0", features = [
|
||||
"menu", "loupe", "copy"
|
||||
"menu", "loupe", "copy", "edit", "plus"
|
||||
] }
|
||||
reqwest = { version = "0.11.18", features = [
|
||||
"blocking"
|
||||
|
|
19
src/env_var_descriptions.rs
Normal file
19
src/env_var_descriptions.rs
Normal file
|
@ -0,0 +1,19 @@
|
|||
use phf::Map;
|
||||
use phf_macros::phf_map;
|
||||
|
||||
pub static ENV_VAR_DESCRIPTIONS: Map<&str, &str> = phf_map! {
|
||||
"XRT_COMPOSITOR_SCALE_PECENTAGE" =>
|
||||
"Render resolution percentage. A percentage higher than the native resolution (>100) will help with antialiasing and image clarity.",
|
||||
// "XRT_COMPOSITOR_COMPUTE" => "",
|
||||
"SURVIVE_GLOBALSCENESOLVER" =>
|
||||
"Continuously recalibrate lighthouse tracking during use. In the current state it's recommended to disable this feature by setting this value to 0.",
|
||||
// "SURVIVE_TIMECODE_OFFSET_MS" => "",
|
||||
"LD_LIBRARY_PATH" =>
|
||||
"Colon-separated list of directories where the dynamic linker will search for shared object libraries.",
|
||||
};
|
||||
|
||||
pub fn env_var_descriptions_as_paragraph() -> String {
|
||||
ENV_VAR_DESCRIPTIONS.into_iter()
|
||||
.map(|(k, v)| format!("{}: {}", k, v))
|
||||
.collect::<Vec<String>>().join("\n\n")
|
||||
}
|
|
@ -23,6 +23,7 @@ pub mod runner_pipeline;
|
|||
pub mod ui;
|
||||
pub mod adb;
|
||||
pub mod downloader;
|
||||
pub mod env_var_descriptions;
|
||||
|
||||
fn main() -> Result<()> {
|
||||
// Prepare i18n
|
||||
|
|
70
src/ui/factories/entry_row_factory.rs
Normal file
70
src/ui/factories/entry_row_factory.rs
Normal file
|
@ -0,0 +1,70 @@
|
|||
use crate::ui::profile_editor::ProfileEditorMsg;
|
||||
use adw::prelude::*;
|
||||
use gtk::prelude::*;
|
||||
use relm4::prelude::*;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct EntryModel {
|
||||
name: String,
|
||||
value: String,
|
||||
}
|
||||
|
||||
pub struct EntryModelInit {
|
||||
pub name: String,
|
||||
pub value: String,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum EntryModelMsg {
|
||||
Changed(String),
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum EntryModelOutMsg {
|
||||
Changed(String, String),
|
||||
}
|
||||
|
||||
#[relm4::factory(pub)]
|
||||
impl FactoryComponent for EntryModel {
|
||||
type Init = EntryModelInit;
|
||||
type Input = EntryModelMsg;
|
||||
type Output = EntryModelOutMsg;
|
||||
type CommandOutput = ();
|
||||
type Widgets = EntryModelWidgets;
|
||||
type ParentInput = ProfileEditorMsg;
|
||||
type ParentWidget = adw::PreferencesGroup;
|
||||
|
||||
view! {
|
||||
root = adw::EntryRow {
|
||||
set_title: &self.name,
|
||||
set_text: &self.value,
|
||||
connect_changed[sender] => move |entry| {
|
||||
sender.input_sender().emit(Self::Input::Changed(entry.text().to_string()));
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
fn update(&mut self, message: Self::Input, sender: FactorySender<Self>) {
|
||||
match message {
|
||||
Self::Input::Changed(val) => {
|
||||
self.value = val.clone();
|
||||
sender
|
||||
.output_sender()
|
||||
.emit(Self::Output::Changed(self.name.clone(), val));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn forward_to_parent(output: Self::Output) -> Option<Self::ParentInput> {
|
||||
Some(match output {
|
||||
Self::Output::Changed(name, value) => ProfileEditorMsg::EntryChanged(name, value),
|
||||
})
|
||||
}
|
||||
|
||||
fn init_model(init: Self::Init, index: &Self::Index, sender: FactorySender<Self>) -> Self {
|
||||
Self {
|
||||
name: init.name,
|
||||
value: init.value,
|
||||
}
|
||||
}
|
||||
}
|
3
src/ui/factories/mod.rs
Normal file
3
src/ui/factories/mod.rs
Normal file
|
@ -0,0 +1,3 @@
|
|||
pub mod entry_row_factory;
|
||||
pub mod switch_row_factory;
|
||||
pub mod path_row_factory;
|
87
src/ui/factories/path_row_factory.rs
Normal file
87
src/ui/factories/path_row_factory.rs
Normal file
|
@ -0,0 +1,87 @@
|
|||
use adw::prelude::*;
|
||||
use gtk::prelude::*;
|
||||
use relm4::prelude::*;
|
||||
|
||||
use crate::ui::profile_editor::ProfileEditorMsg;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct PathModel {
|
||||
name: String,
|
||||
key: String,
|
||||
value: String,
|
||||
path_label: gtk::Label,
|
||||
}
|
||||
|
||||
pub struct PathModelInit {
|
||||
pub name: String,
|
||||
pub key: String,
|
||||
pub value: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum PathModelMsg {
|
||||
Changed(String),
|
||||
OpenFileChooser,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum PathModelOutMsg {
|
||||
/** key, value */
|
||||
Changed(String, String),
|
||||
}
|
||||
|
||||
#[relm4::factory(pub)]
|
||||
impl FactoryComponent for PathModel {
|
||||
type Init = PathModelInit;
|
||||
type Input = PathModelMsg;
|
||||
type Output = PathModelOutMsg;
|
||||
type CommandOutput = ();
|
||||
type Widgets = PathModelWidgets;
|
||||
type ParentInput = ProfileEditorMsg;
|
||||
type ParentWidget = adw::PreferencesGroup;
|
||||
|
||||
view! {
|
||||
root = adw::ActionRow {
|
||||
set_title: &self.name,
|
||||
set_subtitle_lines: 0,
|
||||
set_icon_name: Some("folder-open-symbolic"),
|
||||
add_suffix: &self.path_label,
|
||||
set_activatable: true,
|
||||
connect_activated[sender] => move |_| {
|
||||
sender.input(Self::Input::OpenFileChooser)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn update(&mut self, message: Self::Input, sender: FactorySender<Self>) {
|
||||
match message {
|
||||
Self::Input::Changed(val) => {
|
||||
self.value = val.clone();
|
||||
self.path_label.set_label(val.as_str());
|
||||
sender
|
||||
.output_sender()
|
||||
.emit(Self::Output::Changed(self.key.clone(), val))
|
||||
}
|
||||
Self::Input::OpenFileChooser => {
|
||||
println!("file chooser");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn forward_to_parent(_output: Self::Output) -> Option<Self::ParentInput> {
|
||||
None
|
||||
}
|
||||
|
||||
fn init_model(init: Self::Init, index: &Self::Index, sender: FactorySender<Self>) -> Self {
|
||||
Self {
|
||||
name: init.name,
|
||||
key: init.key,
|
||||
value: match init.value.as_ref() {
|
||||
Some(val) => val.clone(), None => "".into()
|
||||
},
|
||||
path_label: gtk::Label::builder().label(match init.value {
|
||||
Some(val) => val, None => "(None)".into()
|
||||
}).ellipsize(gtk::pango::EllipsizeMode::Start).build(),
|
||||
}
|
||||
}
|
||||
}
|
86
src/ui/factories/switch_row_factory.rs
Normal file
86
src/ui/factories/switch_row_factory.rs
Normal file
|
@ -0,0 +1,86 @@
|
|||
use adw::prelude::*;
|
||||
use gtk::prelude::*;
|
||||
use relm4::prelude::*;
|
||||
|
||||
use crate::ui::profile_editor::ProfileEditorMsg;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct SwitchModel {
|
||||
name: String,
|
||||
description: Option<String>,
|
||||
key: String,
|
||||
value: bool,
|
||||
}
|
||||
|
||||
pub struct SwitchModelInit {
|
||||
pub name: String,
|
||||
pub description: Option<String>,
|
||||
pub key: String,
|
||||
pub value: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum SwitchModelMsg {
|
||||
Changed(bool),
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum SwitchModelOutMsg {
|
||||
/** key, value */
|
||||
Changed(String, bool),
|
||||
}
|
||||
|
||||
#[relm4::factory(pub)]
|
||||
impl FactoryComponent for SwitchModel {
|
||||
type Init = SwitchModelInit;
|
||||
type Input = SwitchModelMsg;
|
||||
type Output = SwitchModelOutMsg;
|
||||
type CommandOutput = ();
|
||||
type Widgets = SwitchModelWidgets;
|
||||
type ParentInput = ProfileEditorMsg;
|
||||
type ParentWidget = adw::PreferencesGroup;
|
||||
|
||||
view! {
|
||||
root = adw::ActionRow {
|
||||
set_title: &self.name,
|
||||
set_subtitle_lines: 0,
|
||||
set_subtitle: match &self.description {
|
||||
Some(s) => s,
|
||||
None => "".into(),
|
||||
},
|
||||
add_suffix: switch = >k::Switch {
|
||||
set_valign: gtk::Align::Center,
|
||||
set_active: self.value,
|
||||
connect_state_set[sender] => move |_, state| {
|
||||
sender.input(Self::Input::Changed(state));
|
||||
gtk::Inhibit(false)
|
||||
}
|
||||
},
|
||||
set_activatable_widget: Some(&switch),
|
||||
}
|
||||
}
|
||||
|
||||
fn update(&mut self, message: Self::Input, sender: FactorySender<Self>) {
|
||||
match message {
|
||||
Self::Input::Changed(val) => {
|
||||
self.value = val.clone();
|
||||
sender
|
||||
.output_sender()
|
||||
.emit(Self::Output::Changed(self.key.clone(), val))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn forward_to_parent(_output: Self::Output) -> Option<Self::ParentInput> {
|
||||
None
|
||||
}
|
||||
|
||||
fn init_model(init: Self::Init, index: &Self::Index, sender: FactorySender<Self>) -> Self {
|
||||
Self {
|
||||
name: init.name,
|
||||
description: init.description,
|
||||
key: init.key,
|
||||
value: init.value,
|
||||
}
|
||||
}
|
||||
}
|
|
@ -136,11 +136,7 @@ impl SimpleComponent for InstallWivrnBox {
|
|||
match message {
|
||||
Self::Input::ClockTicking => {
|
||||
if self.download_thread.is_some() {
|
||||
let finished = self
|
||||
.download_thread
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
.is_finished();
|
||||
let finished = self.download_thread.as_ref().unwrap().is_finished();
|
||||
if finished {
|
||||
let joinh = self.download_thread.take().unwrap();
|
||||
match joinh.join().unwrap() {
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
use super::install_wivrn_box::{InstallWivrnBox, InstallWivrnBoxInit, InstallWivrnBoxMsg};
|
||||
use super::profile_editor::{ProfileEditor, ProfileEditorMsg};
|
||||
use super::runtime_switcher_box::{
|
||||
RuntimeSwitcherBox, RuntimeSwitcherBoxInit, RuntimeSwitcherBoxMsg,
|
||||
};
|
||||
|
@ -9,6 +10,7 @@ use crate::profile::{Profile, XRServiceType};
|
|||
use crate::ui::app::{
|
||||
AboutAction, BuildProfileAction, DebugViewToggleAction, LibsurviveSetupAction,
|
||||
};
|
||||
use crate::ui::profile_editor::ProfileEditorInit;
|
||||
use gtk::prelude::*;
|
||||
use relm4::prelude::*;
|
||||
use relm4::{ComponentParts, ComponentSender, SimpleComponent};
|
||||
|
@ -17,9 +19,9 @@ use relm4_icons::icon_name;
|
|||
#[tracker::track]
|
||||
pub struct MainView {
|
||||
xrservice_active: bool,
|
||||
xrservice_type: XRServiceType,
|
||||
enable_debug_view: bool,
|
||||
profile_names: Vec<String>,
|
||||
selected_profile: Profile,
|
||||
#[tracker::do_not_track]
|
||||
profiles_dropdown: Option<gtk::DropDown>,
|
||||
#[tracker::do_not_track]
|
||||
|
@ -28,6 +30,8 @@ pub struct MainView {
|
|||
steam_launch_options_box: Controller<SteamLaunchOptionsBox>,
|
||||
#[tracker::do_not_track]
|
||||
runtime_switcher_box: Controller<RuntimeSwitcherBox>,
|
||||
#[tracker::do_not_track]
|
||||
profile_editor: Controller<ProfileEditor>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
|
@ -40,6 +44,7 @@ pub enum MainViewMsg {
|
|||
SetSelectedProfile(u32),
|
||||
ProfileSelected(u32),
|
||||
UpdateSelectedProfile(Profile),
|
||||
EditProfile,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
|
@ -94,16 +99,29 @@ impl SimpleComponent for MainView {
|
|||
set_icon_name: icon_name::MENU,
|
||||
set_menu_model: Some(&app_menu),
|
||||
},
|
||||
pack_start: profiles_dropdown = >k::DropDown {
|
||||
set_tooltip_text: Some("Profiles"),
|
||||
#[track = "model.changed(Self::profile_names())"]
|
||||
set_model: Some(&{
|
||||
let names: Vec<_> = model.profile_names.iter().map(String::as_str).collect();
|
||||
gtk::StringList::new(&names)
|
||||
}),
|
||||
connect_selected_item_notify[sender] => move |this| {
|
||||
sender.input(MainViewMsg::ProfileSelected(this.selected()));
|
||||
pack_start = >k::Box {
|
||||
set_orientation: gtk::Orientation::Horizontal,
|
||||
add_css_class: "linked",
|
||||
#[name(profiles_dropdown)]
|
||||
gtk::DropDown {
|
||||
set_tooltip_text: Some("Profiles"),
|
||||
#[track = "model.changed(Self::profile_names())"]
|
||||
set_model: Some(&{
|
||||
let names: Vec<_> = model.profile_names.iter().map(String::as_str).collect();
|
||||
gtk::StringList::new(&names)
|
||||
}),
|
||||
connect_selected_item_notify[sender] => move |this| {
|
||||
sender.input(MainViewMsg::ProfileSelected(this.selected()));
|
||||
},
|
||||
},
|
||||
gtk::Button {
|
||||
set_icon_name: icon_name::EDIT,
|
||||
add_css_class: "suggested-action",
|
||||
set_tooltip_text: Some("Edit Profile"),
|
||||
connect_clicked[sender] => move |_| {
|
||||
sender.input(Self::Input::EditProfile);
|
||||
}
|
||||
}
|
||||
},
|
||||
#[track = "model.changed(Self::enable_debug_view())"]
|
||||
set_show_end_title_buttons: !model.enable_debug_view,
|
||||
|
@ -173,7 +191,7 @@ impl SimpleComponent for MainView {
|
|||
self.set_enable_debug_view(val);
|
||||
}
|
||||
Self::Input::UpdateSelectedProfile(prof) => {
|
||||
self.set_xrservice_type(prof.xrservice_type.clone());
|
||||
self.set_selected_profile(prof.clone());
|
||||
self.install_wivrn_box
|
||||
.sender()
|
||||
.emit(InstallWivrnBoxMsg::UpdateSelectedProfile(prof.clone()));
|
||||
|
@ -212,6 +230,9 @@ impl SimpleComponent for MainView {
|
|||
self.profile_names.get(position as usize).unwrap().clone(),
|
||||
));
|
||||
}
|
||||
Self::Input::EditProfile => {
|
||||
self.profile_editor.emit(ProfileEditorMsg::Present(self.selected_profile.clone()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -222,7 +243,6 @@ impl SimpleComponent for MainView {
|
|||
) -> ComponentParts<Self> {
|
||||
let mut model = Self {
|
||||
xrservice_active: false,
|
||||
xrservice_type: XRServiceType::Monado,
|
||||
enable_debug_view: init.config.debug_view_enabled,
|
||||
profiles_dropdown: None,
|
||||
profile_names: vec![],
|
||||
|
@ -237,6 +257,8 @@ impl SimpleComponent for MainView {
|
|||
selected_profile: init.selected_profile.clone(),
|
||||
})
|
||||
.detach(),
|
||||
profile_editor: ProfileEditor::builder().launch(ProfileEditorInit {}).detach(),
|
||||
selected_profile: init.selected_profile.clone(),
|
||||
tracker: 0,
|
||||
};
|
||||
let widgets = view_output!();
|
||||
|
|
|
@ -7,3 +7,5 @@ pub mod libsurvive_setup_window;
|
|||
pub mod install_wivrn_box;
|
||||
pub mod steam_launch_options_box;
|
||||
pub mod runtime_switcher_box;
|
||||
pub mod profile_editor;
|
||||
pub mod factories;
|
||||
|
|
184
src/ui/profile_editor.rs
Normal file
184
src/ui/profile_editor.rs
Normal file
|
@ -0,0 +1,184 @@
|
|||
use super::factories::{entry_row_factory::{EntryModel, EntryModelInit}, switch_row_factory::{SwitchModel, SwitchModelInit}, path_row_factory::{PathModel, PathModelInit}};
|
||||
use crate::{env_var_descriptions::env_var_descriptions_as_paragraph, profile::Profile};
|
||||
use adw::prelude::*;
|
||||
use gtk::prelude::*;
|
||||
use relm4::{factory::FactoryVecDeque, prelude::*};
|
||||
use relm4_icons::icon_name;
|
||||
|
||||
pub struct ProfileEditor {
|
||||
profile: Option<Profile>,
|
||||
win: Option<adw::PreferencesWindow>,
|
||||
|
||||
name_row: adw::EntryRow,
|
||||
env_rows: FactoryVecDeque<EntryModel>,
|
||||
switch_rows: FactoryVecDeque<SwitchModel>,
|
||||
path_rows: FactoryVecDeque<PathModel>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum ProfileEditorMsg {
|
||||
Present(Profile),
|
||||
EntryChanged(String, String),
|
||||
AddEnvVar,
|
||||
SaveProfile, // ?
|
||||
}
|
||||
|
||||
pub struct ProfileEditorInit {}
|
||||
|
||||
#[relm4::component(pub)]
|
||||
impl SimpleComponent for ProfileEditor {
|
||||
type Init = ProfileEditorInit;
|
||||
type Input = ProfileEditorMsg;
|
||||
type Output = ();
|
||||
|
||||
view! {
|
||||
#[name(win)]
|
||||
adw::PreferencesWindow {
|
||||
set_hide_on_close: true,
|
||||
set_modal: true,
|
||||
add: mainpage = &adw::PreferencesPage {
|
||||
add: maingrp = &adw::PreferencesGroup {
|
||||
set_title: "Profile Info",
|
||||
model.name_row.clone(),
|
||||
},
|
||||
add: model.env_rows.widget(),
|
||||
add: model.switch_rows.widget(),
|
||||
add: model.path_rows.widget(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn update(&mut self, message: Self::Input, sender: ComponentSender<Self>) {
|
||||
match message {
|
||||
Self::Input::Present(prof) => {
|
||||
let win = self.win.as_ref().unwrap();
|
||||
let p = prof.clone();
|
||||
win.set_title(Some(p.name.as_str()));
|
||||
|
||||
self.name_row.set_text(p.name.as_str());
|
||||
self.env_rows.guard().clear();
|
||||
for (k, v) in p.environment.iter() {
|
||||
self.env_rows.guard().push_back(EntryModelInit {
|
||||
name: k.clone(),
|
||||
value: v.clone(),
|
||||
});
|
||||
}
|
||||
|
||||
self.switch_rows.guard().clear();
|
||||
self.switch_rows.guard().push_back(SwitchModelInit {
|
||||
name: "Libsurvive".into(),
|
||||
description: Some("Lighthouse based spacial tracking".into()),
|
||||
key: "libsurvive_enabled".into(),
|
||||
value: p.libsurvive_enabled,
|
||||
});
|
||||
self.switch_rows.guard().push_back(SwitchModelInit {
|
||||
name: "Basalt".into(),
|
||||
description: Some("Camera based SLAM tracking".into()),
|
||||
key: "basalt_enabled".into(),
|
||||
value: p.basalt_enabled,
|
||||
});
|
||||
self.switch_rows.guard().push_back(SwitchModelInit {
|
||||
name: "Mercury".into(),
|
||||
description: Some("Camera based hand tracking".into()),
|
||||
key: "mercury_enabled".into(),
|
||||
value: p.mercury_enabled,
|
||||
});
|
||||
|
||||
self.path_rows.guard().clear();
|
||||
self.path_rows.guard().push_back(PathModelInit {
|
||||
name: "XR Service Path".into(),
|
||||
key: "xrservice_path".into(),
|
||||
value: Some(p.xrservice_path)
|
||||
});
|
||||
self.path_rows.guard().push_back(PathModelInit {
|
||||
name: "OpenComposite Path".into(),
|
||||
key: "opencomposite_path".into(),
|
||||
value: Some(p.opencomposite_path)
|
||||
});
|
||||
self.path_rows.guard().push_back(PathModelInit {
|
||||
name: "Libsurvive Path".into(),
|
||||
key: "libsurvive_path".into(),
|
||||
value: p.libsurvive_path
|
||||
});
|
||||
self.path_rows.guard().push_back(PathModelInit {
|
||||
name: "Basalt Path".into(),
|
||||
key: "basalt_path".into(),
|
||||
value: p.basalt_path
|
||||
});
|
||||
self.path_rows.guard().push_back(PathModelInit {
|
||||
name: "Mercury Path".into(),
|
||||
key: "mercury_path".into(),
|
||||
value: p.mercury_path
|
||||
});
|
||||
|
||||
self.profile = Some(prof);
|
||||
win.present();
|
||||
}
|
||||
Self::Input::SaveProfile => {}
|
||||
Self::Input::EntryChanged(name, value) => {
|
||||
println!("{}: {}", name, value);
|
||||
self.profile
|
||||
.as_mut()
|
||||
.unwrap()
|
||||
.environment
|
||||
.insert(name, value);
|
||||
}
|
||||
Self::Input::AddEnvVar => {
|
||||
println!("Add env var");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn init(
|
||||
init: Self::Init,
|
||||
root: &Self::Root,
|
||||
sender: ComponentSender<Self>,
|
||||
) -> ComponentParts<Self> {
|
||||
let add_env_var_btn = gtk::Button::builder()
|
||||
.icon_name(icon_name::PLUS)
|
||||
.tooltip_text("Add Environment Variable")
|
||||
.css_classes(["flat"])
|
||||
.valign(gtk::Align::Start)
|
||||
.halign(gtk::Align::End)
|
||||
.build();
|
||||
{
|
||||
let btn_sender = sender.clone();
|
||||
add_env_var_btn.connect_clicked(move |_| {
|
||||
btn_sender.input(Self::Input::AddEnvVar);
|
||||
});
|
||||
}
|
||||
|
||||
let mut model = Self {
|
||||
profile: None,
|
||||
win: None,
|
||||
name_row: adw::EntryRow::builder().title("Name").build(),
|
||||
env_rows: FactoryVecDeque::new(
|
||||
adw::PreferencesGroup::builder()
|
||||
.title("Environment Variables")
|
||||
.description(env_var_descriptions_as_paragraph())
|
||||
.header_suffix(&add_env_var_btn)
|
||||
.build(),
|
||||
sender.input_sender(),
|
||||
),
|
||||
switch_rows: FactoryVecDeque::new(
|
||||
adw::PreferencesGroup::builder()
|
||||
.title("Components")
|
||||
.description("Enable or disable features")
|
||||
.build(),
|
||||
sender.input_sender(),
|
||||
),
|
||||
path_rows: FactoryVecDeque::new(
|
||||
adw::PreferencesGroup::builder()
|
||||
.title("Paths")
|
||||
// .description("")
|
||||
.build(),
|
||||
sender.input_sender(),
|
||||
)
|
||||
};
|
||||
|
||||
let widgets = view_output!();
|
||||
model.win = Some(widgets.win.clone());
|
||||
|
||||
ComponentParts { model, widgets }
|
||||
}
|
||||
}
|
|
@ -1,6 +1,5 @@
|
|||
use relm4::prelude::*;
|
||||
use gtk::prelude::*;
|
||||
|
||||
use crate::{constants::APP_NAME, file_builders::{active_runtime_json::{get_current_active_runtime, self, set_current_active_runtime_to_profile, set_current_active_runtime_to_steam}, openvrpaths_vrpath::{set_current_openvrpaths_to_profile, set_current_openvrpaths_to_steam}}, profile::Profile};
|
||||
|
||||
pub struct RuntimeSwitcherBox {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue