mirror of
https://gitlab.com/gabmus/envision.git
synced 2025-08-03 14:49:04 +00:00
feat: full body tracking tracker role assignment ui
This commit is contained in:
parent
7580988ed8
commit
ac340ae6f2
8 changed files with 400 additions and 6 deletions
|
@ -1,3 +1,5 @@
|
||||||
|
use std::slice::Iter;
|
||||||
|
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
|
@ -41,6 +43,101 @@ pub enum XrtTrackerRole {
|
||||||
ViveTrackerHtcxKeyboard,
|
ViveTrackerHtcxKeyboard,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl XrtTrackerRole {
|
||||||
|
pub fn to_picker_string(&self) -> &str {
|
||||||
|
match self {
|
||||||
|
Self::ViveTrackerHtcxWaist => "Waist",
|
||||||
|
Self::ViveTrackerHtcxLeftFoot => "Left foot",
|
||||||
|
Self::ViveTrackerHtcxRightFoot => "Right foot",
|
||||||
|
Self::ViveTrackerHtcxHandheldObject => "Handheld object",
|
||||||
|
Self::ViveTrackerHtcxLeftShoulder => "Left shoulder",
|
||||||
|
Self::ViveTrackerHtcxRightShoulder => "Right shoulder",
|
||||||
|
Self::ViveTrackerHtcxLeftElbow => "Left elbow",
|
||||||
|
Self::ViveTrackerHtcxRightElbow => "Right elbow",
|
||||||
|
Self::ViveTrackerHtcxLeftKnee => "Left knee",
|
||||||
|
Self::ViveTrackerHtcxRightKnee => "Right knee",
|
||||||
|
Self::ViveTrackerHtcxChest => "Chest",
|
||||||
|
Self::ViveTrackerHtcxCamera => "Camera",
|
||||||
|
Self::ViveTrackerHtcxKeyboard => "Keyboard",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn from_picker_string(s: &str) -> Self {
|
||||||
|
match s.to_lowercase().trim() {
|
||||||
|
"waist" => Self::ViveTrackerHtcxWaist,
|
||||||
|
"left foot" => Self::ViveTrackerHtcxLeftFoot,
|
||||||
|
"right foot" => Self::ViveTrackerHtcxRightFoot,
|
||||||
|
"handheld object" => Self::ViveTrackerHtcxHandheldObject,
|
||||||
|
"left shoulder" => Self::ViveTrackerHtcxLeftShoulder,
|
||||||
|
"right shoulder" => Self::ViveTrackerHtcxRightShoulder,
|
||||||
|
"left elbow" => Self::ViveTrackerHtcxLeftElbow,
|
||||||
|
"right elbow" => Self::ViveTrackerHtcxRightElbow,
|
||||||
|
"left knee" => Self::ViveTrackerHtcxLeftKnee,
|
||||||
|
"right knee" => Self::ViveTrackerHtcxRightKnee,
|
||||||
|
"chest" => Self::ViveTrackerHtcxChest,
|
||||||
|
"camera" => Self::ViveTrackerHtcxCamera,
|
||||||
|
"keyboard" => Self::ViveTrackerHtcxKeyboard,
|
||||||
|
_ => Self::ViveTrackerHtcxWaist,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn iter() -> Iter<'static, Self> {
|
||||||
|
[
|
||||||
|
Self::ViveTrackerHtcxWaist,
|
||||||
|
Self::ViveTrackerHtcxLeftFoot,
|
||||||
|
Self::ViveTrackerHtcxRightFoot,
|
||||||
|
Self::ViveTrackerHtcxHandheldObject,
|
||||||
|
Self::ViveTrackerHtcxLeftShoulder,
|
||||||
|
Self::ViveTrackerHtcxRightShoulder,
|
||||||
|
Self::ViveTrackerHtcxLeftElbow,
|
||||||
|
Self::ViveTrackerHtcxRightElbow,
|
||||||
|
Self::ViveTrackerHtcxLeftKnee,
|
||||||
|
Self::ViveTrackerHtcxRightKnee,
|
||||||
|
Self::ViveTrackerHtcxChest,
|
||||||
|
Self::ViveTrackerHtcxCamera,
|
||||||
|
Self::ViveTrackerHtcxKeyboard,
|
||||||
|
]
|
||||||
|
.iter()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn to_number(&self) -> u32 {
|
||||||
|
match self {
|
||||||
|
Self::ViveTrackerHtcxWaist => 0,
|
||||||
|
Self::ViveTrackerHtcxLeftFoot => 1,
|
||||||
|
Self::ViveTrackerHtcxRightFoot => 2,
|
||||||
|
Self::ViveTrackerHtcxHandheldObject => 3,
|
||||||
|
Self::ViveTrackerHtcxLeftShoulder => 4,
|
||||||
|
Self::ViveTrackerHtcxRightShoulder => 5,
|
||||||
|
Self::ViveTrackerHtcxLeftElbow => 6,
|
||||||
|
Self::ViveTrackerHtcxRightElbow => 7,
|
||||||
|
Self::ViveTrackerHtcxLeftKnee => 8,
|
||||||
|
Self::ViveTrackerHtcxRightKnee => 9,
|
||||||
|
Self::ViveTrackerHtcxChest => 10,
|
||||||
|
Self::ViveTrackerHtcxCamera => 11,
|
||||||
|
Self::ViveTrackerHtcxKeyboard => 12,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn from_number(n: &u32) -> Self {
|
||||||
|
match n {
|
||||||
|
0 => Self::ViveTrackerHtcxWaist,
|
||||||
|
1 => Self::ViveTrackerHtcxLeftFoot,
|
||||||
|
2 => Self::ViveTrackerHtcxRightFoot,
|
||||||
|
3 => Self::ViveTrackerHtcxHandheldObject,
|
||||||
|
4 => Self::ViveTrackerHtcxLeftShoulder,
|
||||||
|
5 => Self::ViveTrackerHtcxRightShoulder,
|
||||||
|
6 => Self::ViveTrackerHtcxLeftElbow,
|
||||||
|
7 => Self::ViveTrackerHtcxRightElbow,
|
||||||
|
8 => Self::ViveTrackerHtcxLeftKnee,
|
||||||
|
9 => Self::ViveTrackerHtcxRightKnee,
|
||||||
|
10 => Self::ViveTrackerHtcxChest,
|
||||||
|
11 => Self::ViveTrackerHtcxCamera,
|
||||||
|
12 => Self::ViveTrackerHtcxKeyboard,
|
||||||
|
_ => Self::ViveTrackerHtcxWaist,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
||||||
pub struct TrackerRole {
|
pub struct TrackerRole {
|
||||||
pub device_serial: String,
|
pub device_serial: String,
|
||||||
|
@ -48,6 +145,16 @@ pub struct TrackerRole {
|
||||||
pub xrt_input_name: XrtInputName,
|
pub xrt_input_name: XrtInputName,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Default for TrackerRole {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
device_serial: "".into(),
|
||||||
|
role: XrtTrackerRole::ViveTrackerHtcxWaist,
|
||||||
|
xrt_input_name: XrtInputName::XrtInputGenericTrackerPose,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Default)]
|
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Default)]
|
||||||
pub struct MonadoConfigV0 {
|
pub struct MonadoConfigV0 {
|
||||||
#[serde(skip_serializing_if = "Option::is_none", rename = "$schema")]
|
#[serde(skip_serializing_if = "Option::is_none", rename = "$schema")]
|
||||||
|
|
|
@ -2,6 +2,7 @@ use super::about_dialog::AboutDialog;
|
||||||
use super::alert::alert;
|
use super::alert::alert;
|
||||||
use super::build_window::{BuildStatus, BuildWindow};
|
use super::build_window::{BuildStatus, BuildWindow};
|
||||||
use super::debug_view::{DebugView, DebugViewMsg};
|
use super::debug_view::{DebugView, DebugViewMsg};
|
||||||
|
use super::fbt_config_editor::{FbtConfigEditor, FbtConfigEditorInit, FbtConfigEditorMsg};
|
||||||
use super::libsurvive_setup_window::LibsurviveSetupWindow;
|
use super::libsurvive_setup_window::LibsurviveSetupWindow;
|
||||||
use super::main_view::MainViewMsg;
|
use super::main_view::MainViewMsg;
|
||||||
use crate::builders::build_basalt::get_build_basalt_runners;
|
use crate::builders::build_basalt::get_build_basalt_runners;
|
||||||
|
@ -86,6 +87,8 @@ pub struct App {
|
||||||
profiles: Vec<Profile>,
|
profiles: Vec<Profile>,
|
||||||
#[tracker::do_not_track]
|
#[tracker::do_not_track]
|
||||||
xr_devices: XRDevices,
|
xr_devices: XRDevices,
|
||||||
|
#[tracker::do_not_track]
|
||||||
|
fbt_config_editor: Option<Controller<FbtConfigEditor>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
|
@ -103,6 +106,7 @@ pub enum Msg {
|
||||||
SaveWinSize(i32, i32),
|
SaveWinSize(i32, i32),
|
||||||
Quit,
|
Quit,
|
||||||
ParseLog(Vec<String>),
|
ParseLog(Vec<String>),
|
||||||
|
ConfigFbt,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl App {
|
impl App {
|
||||||
|
@ -528,6 +532,19 @@ impl SimpleComponent for App {
|
||||||
))
|
))
|
||||||
.expect("Failed to present Libsurvive Setup Window");
|
.expect("Failed to present Libsurvive Setup Window");
|
||||||
}
|
}
|
||||||
|
Msg::ConfigFbt => {
|
||||||
|
self.fbt_config_editor = Some(
|
||||||
|
FbtConfigEditor::builder()
|
||||||
|
.launch(FbtConfigEditorInit {
|
||||||
|
root_win: self.app_win.clone().upcast::<gtk::Window>(),
|
||||||
|
})
|
||||||
|
.detach(),
|
||||||
|
);
|
||||||
|
self.fbt_config_editor
|
||||||
|
.as_ref()
|
||||||
|
.unwrap()
|
||||||
|
.emit(FbtConfigEditorMsg::Present);
|
||||||
|
}
|
||||||
Msg::SaveWinSize(w, h) => {
|
Msg::SaveWinSize(w, h) => {
|
||||||
self.config.win_size = [w, h];
|
self.config.win_size = [w, h];
|
||||||
self.config.save();
|
self.config.save();
|
||||||
|
@ -621,6 +638,7 @@ impl SimpleComponent for App {
|
||||||
xrservice_runner: None,
|
xrservice_runner: None,
|
||||||
build_pipeline: None,
|
build_pipeline: None,
|
||||||
xr_devices: XRDevices::default(),
|
xr_devices: XRDevices::default(),
|
||||||
|
fbt_config_editor: None,
|
||||||
};
|
};
|
||||||
let widgets = view_output!();
|
let widgets = view_output!();
|
||||||
|
|
||||||
|
@ -638,6 +656,12 @@ impl SimpleComponent for App {
|
||||||
sender.input_sender().emit(Msg::BuildProfile(true));
|
sender.input_sender().emit(Msg::BuildProfile(true));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
{
|
||||||
|
withclones![sender];
|
||||||
|
stateless_action!(actions, ConfigFbtAction, {
|
||||||
|
sender.input_sender().emit(Msg::ConfigFbt);
|
||||||
|
});
|
||||||
|
}
|
||||||
{
|
{
|
||||||
let abd_sender = model.about_dialog.sender().clone();
|
let abd_sender = model.about_dialog.sender().clone();
|
||||||
stateless_action!(actions, AboutAction, {
|
stateless_action!(actions, AboutAction, {
|
||||||
|
@ -692,5 +716,6 @@ new_action_group!(pub AppActionGroup, "win");
|
||||||
new_stateless_action!(pub AboutAction, AppActionGroup, "about");
|
new_stateless_action!(pub AboutAction, AppActionGroup, "about");
|
||||||
new_stateless_action!(pub BuildProfileAction, AppActionGroup, "buildprofile");
|
new_stateless_action!(pub BuildProfileAction, AppActionGroup, "buildprofile");
|
||||||
new_stateless_action!(pub BuildProfileCleanAction, AppActionGroup, "buildprofileclean");
|
new_stateless_action!(pub BuildProfileCleanAction, AppActionGroup, "buildprofileclean");
|
||||||
|
new_stateless_action!(pub ConfigFbtAction, AppActionGroup, "configfbt");
|
||||||
new_stateless_action!(pub QuitAction, AppActionGroup, "quit");
|
new_stateless_action!(pub QuitAction, AppActionGroup, "quit");
|
||||||
new_stateful_action!(pub DebugViewToggleAction, AppActionGroup, "debugviewtoggle", (), bool);
|
new_stateful_action!(pub DebugViewToggleAction, AppActionGroup, "debugviewtoggle", (), bool);
|
||||||
|
|
|
@ -1 +1,2 @@
|
||||||
pub mod env_var_row_factory;
|
pub mod env_var_row_factory;
|
||||||
|
pub mod tracker_role_group_factory;
|
||||||
|
|
109
src/ui/factories/tracker_role_group_factory.rs
Normal file
109
src/ui/factories/tracker_role_group_factory.rs
Normal file
|
@ -0,0 +1,109 @@
|
||||||
|
use crate::{
|
||||||
|
file_builders::monado_config_v0::{TrackerRole, XrtTrackerRole},
|
||||||
|
ui::fbt_config_editor::FbtConfigEditorMsg,
|
||||||
|
ui::preference_rows::{combo_row, entry_row},
|
||||||
|
withclones,
|
||||||
|
};
|
||||||
|
use adw::prelude::*;
|
||||||
|
use relm4::prelude::*;
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct TrackerRoleModel {
|
||||||
|
index: usize,
|
||||||
|
tracker_role: TrackerRole,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct TrackerRoleModelInit {
|
||||||
|
pub index: usize,
|
||||||
|
pub tracker_role: Option<TrackerRole>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum TrackerRoleModelMsg {
|
||||||
|
Changed(TrackerRole),
|
||||||
|
Delete,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum TrackerRoleModelOutMsg {
|
||||||
|
Changed(usize, TrackerRole),
|
||||||
|
Delete(usize),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[relm4::factory(pub)]
|
||||||
|
impl FactoryComponent for TrackerRoleModel {
|
||||||
|
type Init = TrackerRoleModelInit;
|
||||||
|
type Input = TrackerRoleModelMsg;
|
||||||
|
type Output = TrackerRoleModelOutMsg;
|
||||||
|
type CommandOutput = ();
|
||||||
|
type Widgets = TrackerRoleModelWidgets;
|
||||||
|
type ParentInput = FbtConfigEditorMsg;
|
||||||
|
type ParentWidget = adw::PreferencesPage;
|
||||||
|
|
||||||
|
view! {
|
||||||
|
root = adw::PreferencesGroup {
|
||||||
|
set_title: "Tracker",
|
||||||
|
#[wrap(Some)]
|
||||||
|
set_header_suffix: del_btn = >k::Button {
|
||||||
|
set_icon_name: "edit-delete-symbolic",
|
||||||
|
set_tooltip_text: Some("Delete Tracker"),
|
||||||
|
set_valign: gtk::Align::Center,
|
||||||
|
add_css_class: "flat",
|
||||||
|
add_css_class: "circular",
|
||||||
|
connect_clicked[sender] => move |_| {
|
||||||
|
sender.input(Self::Input::Delete);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
add: {
|
||||||
|
withclones![sender];
|
||||||
|
let tr = self.tracker_role.clone();
|
||||||
|
&entry_row("Device serial", self.tracker_role.device_serial.as_str(), move |row| {
|
||||||
|
let mut ntr = tr.clone();
|
||||||
|
ntr.device_serial = row.text().to_string();
|
||||||
|
sender.input(Self::Input::Changed(ntr));
|
||||||
|
})
|
||||||
|
},
|
||||||
|
add: {
|
||||||
|
withclones![sender];
|
||||||
|
let tr = self.tracker_role.clone();
|
||||||
|
&combo_row("Tracker role", None, &tr.role.clone().to_picker_string(),
|
||||||
|
XrtTrackerRole::iter()
|
||||||
|
.map(XrtTrackerRole::to_picker_string)
|
||||||
|
.map(String::from)
|
||||||
|
.collect::<Vec<String>>(),
|
||||||
|
move |row| {
|
||||||
|
let mut ntr = tr.clone();
|
||||||
|
ntr.role = XrtTrackerRole::from_number(&row.selected());
|
||||||
|
sender.input(Self::Input::Changed(ntr));
|
||||||
|
}
|
||||||
|
)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn forward_to_parent(output: Self::Output) -> Option<Self::ParentInput> {
|
||||||
|
Some(match output {
|
||||||
|
Self::Output::Changed(index, tracker_role) => {
|
||||||
|
Self::ParentInput::TrackerRoleChanged(index, tracker_role.clone())
|
||||||
|
}
|
||||||
|
Self::Output::Delete(index) => Self::ParentInput::TrackerRoleDeleted(index),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn update(&mut self, message: Self::Input, sender: FactorySender<Self>) {
|
||||||
|
match message {
|
||||||
|
Self::Input::Changed(r) => {
|
||||||
|
self.tracker_role = r;
|
||||||
|
sender.output(Self::Output::Changed(self.index, self.tracker_role.clone()));
|
||||||
|
}
|
||||||
|
Self::Input::Delete => sender.output(Self::Output::Delete(self.index)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn init_model(init: Self::Init, _index: &Self::Index, _sender: FactorySender<Self>) -> Self {
|
||||||
|
Self {
|
||||||
|
index: init.index,
|
||||||
|
tracker_role: init.tracker_role.unwrap_or_else(|| TrackerRole::default()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
152
src/ui/fbt_config_editor.rs
Normal file
152
src/ui/fbt_config_editor.rs
Normal file
|
@ -0,0 +1,152 @@
|
||||||
|
use super::factories::tracker_role_group_factory::TrackerRoleModel;
|
||||||
|
use crate::{
|
||||||
|
file_builders::monado_config_v0::{
|
||||||
|
dump_monado_config_v0, get_monado_config_v0, MonadoConfigV0, TrackerRole,
|
||||||
|
},
|
||||||
|
ui::factories::tracker_role_group_factory::TrackerRoleModelInit,
|
||||||
|
withclones,
|
||||||
|
};
|
||||||
|
use adw::prelude::*;
|
||||||
|
use relm4::{factory::FactoryVecDeque, prelude::*};
|
||||||
|
|
||||||
|
#[tracker::track]
|
||||||
|
pub struct FbtConfigEditor {
|
||||||
|
monado_config_v0: MonadoConfigV0,
|
||||||
|
|
||||||
|
#[tracker::do_not_track]
|
||||||
|
win: Option<adw::PreferencesWindow>,
|
||||||
|
|
||||||
|
#[tracker::do_not_track]
|
||||||
|
tracker_role_groups: FactoryVecDeque<TrackerRoleModel>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum FbtConfigEditorMsg {
|
||||||
|
Present,
|
||||||
|
Save,
|
||||||
|
TrackerRoleNew,
|
||||||
|
TrackerRoleChanged(usize, TrackerRole),
|
||||||
|
TrackerRoleDeleted(usize),
|
||||||
|
Repopulate,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct FbtConfigEditorInit {
|
||||||
|
pub root_win: gtk::Window,
|
||||||
|
}
|
||||||
|
#[relm4::component(pub)]
|
||||||
|
impl SimpleComponent for FbtConfigEditor {
|
||||||
|
type Init = FbtConfigEditorInit;
|
||||||
|
type Input = FbtConfigEditorMsg;
|
||||||
|
type Output = ();
|
||||||
|
|
||||||
|
view! {
|
||||||
|
#[name(win)]
|
||||||
|
adw::PreferencesWindow {
|
||||||
|
set_title: Some("Full Body Trackers"),
|
||||||
|
set_modal: true,
|
||||||
|
set_transient_for: Some(&init.root_win),
|
||||||
|
add: model.tracker_role_groups.widget(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn update(&mut self, message: Self::Input, sender: ComponentSender<Self>) {
|
||||||
|
self.reset();
|
||||||
|
|
||||||
|
match message {
|
||||||
|
Self::Input::Present => {
|
||||||
|
self.win.as_ref().unwrap().present();
|
||||||
|
}
|
||||||
|
Self::Input::Save => {
|
||||||
|
dump_monado_config_v0(&self.monado_config_v0);
|
||||||
|
self.win.as_ref().unwrap().close();
|
||||||
|
}
|
||||||
|
Self::Input::TrackerRoleChanged(index, n_role) => {
|
||||||
|
let role = self.monado_config_v0.tracker_roles.get_mut(index).unwrap();
|
||||||
|
role.device_serial = n_role.device_serial;
|
||||||
|
role.role = n_role.role;
|
||||||
|
role.xrt_input_name = n_role.xrt_input_name;
|
||||||
|
}
|
||||||
|
Self::Input::TrackerRoleDeleted(index) => {
|
||||||
|
self.monado_config_v0.tracker_roles.remove(index);
|
||||||
|
sender.input(Self::Input::Repopulate);
|
||||||
|
}
|
||||||
|
Self::Input::TrackerRoleNew => {
|
||||||
|
self.monado_config_v0
|
||||||
|
.tracker_roles
|
||||||
|
.push(TrackerRole::default());
|
||||||
|
self.tracker_role_groups
|
||||||
|
.guard()
|
||||||
|
.push_back(TrackerRoleModelInit {
|
||||||
|
index: self.monado_config_v0.tracker_roles.len() - 1,
|
||||||
|
tracker_role: None,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
Self::Input::Repopulate => {
|
||||||
|
self.populate_tracker_roles();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn init(
|
||||||
|
init: Self::Init,
|
||||||
|
root: &Self::Root,
|
||||||
|
sender: ComponentSender<Self>,
|
||||||
|
) -> ComponentParts<Self> {
|
||||||
|
let page = adw::PreferencesPage::builder().build();
|
||||||
|
let grp = adw::PreferencesGroup::builder().build();
|
||||||
|
let add_btn = gtk::Button::builder()
|
||||||
|
.label("Add Tracker")
|
||||||
|
.margin_bottom(12)
|
||||||
|
.css_classes(["pill"])
|
||||||
|
.halign(gtk::Align::Center)
|
||||||
|
.build();
|
||||||
|
let save_btn = gtk::Button::builder()
|
||||||
|
.label("Save")
|
||||||
|
.margin_bottom(12)
|
||||||
|
.halign(gtk::Align::Center)
|
||||||
|
.css_classes(["suggested-action", "pill"])
|
||||||
|
.build();
|
||||||
|
{
|
||||||
|
withclones![sender];
|
||||||
|
add_btn.connect_clicked(move |_| {
|
||||||
|
sender.input(Self::Input::TrackerRoleNew);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
{
|
||||||
|
withclones![sender];
|
||||||
|
save_btn.connect_clicked(move |_| {
|
||||||
|
sender.input(Self::Input::Save);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
grp.add(&save_btn);
|
||||||
|
grp.add(&add_btn);
|
||||||
|
page.add(&grp);
|
||||||
|
|
||||||
|
let mut model = Self {
|
||||||
|
win: None,
|
||||||
|
tracker: 0,
|
||||||
|
monado_config_v0: get_monado_config_v0(),
|
||||||
|
tracker_role_groups: FactoryVecDeque::new(page, sender.input_sender()),
|
||||||
|
};
|
||||||
|
|
||||||
|
model.populate_tracker_roles();
|
||||||
|
|
||||||
|
let widgets = view_output!();
|
||||||
|
model.win = Some(widgets.win.clone());
|
||||||
|
|
||||||
|
ComponentParts { model, widgets }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FbtConfigEditor {
|
||||||
|
fn populate_tracker_roles(&mut self) {
|
||||||
|
let mut guard = self.tracker_role_groups.guard();
|
||||||
|
guard.clear();
|
||||||
|
for (i, v) in self.monado_config_v0.tracker_roles.iter().enumerate() {
|
||||||
|
guard.push_back(TrackerRoleModelInit {
|
||||||
|
index: i,
|
||||||
|
tracker_role: Some(v.clone()),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -10,7 +10,8 @@ use crate::gpu_profile::{get_gpu_power_profile, get_set_vr_pow_prof_cmd, GpuPowe
|
||||||
use crate::profile::{LighthouseDriver, Profile};
|
use crate::profile::{LighthouseDriver, Profile};
|
||||||
use crate::steamvr_utils::chaperone_info_exists;
|
use crate::steamvr_utils::chaperone_info_exists;
|
||||||
use crate::ui::app::{
|
use crate::ui::app::{
|
||||||
AboutAction, BuildProfileAction, BuildProfileCleanAction, DebugViewToggleAction,
|
AboutAction, BuildProfileAction, BuildProfileCleanAction, ConfigFbtAction,
|
||||||
|
DebugViewToggleAction,
|
||||||
};
|
};
|
||||||
use crate::ui::profile_editor::ProfileEditorInit;
|
use crate::ui::profile_editor::ProfileEditorInit;
|
||||||
use crate::ui::util::limit_dropdown_width;
|
use crate::ui::util::limit_dropdown_width;
|
||||||
|
@ -109,6 +110,7 @@ impl SimpleComponent for MainView {
|
||||||
"_Debug View" => DebugViewToggleAction,
|
"_Debug View" => DebugViewToggleAction,
|
||||||
"_Build Profile" => BuildProfileAction,
|
"_Build Profile" => BuildProfileAction,
|
||||||
"C_lean Build Profile" => BuildProfileCleanAction,
|
"C_lean Build Profile" => BuildProfileCleanAction,
|
||||||
|
"Configure Full Body _Tracking" => ConfigFbtAction,
|
||||||
},
|
},
|
||||||
section! {
|
section! {
|
||||||
"_About" => AboutAction,
|
"_About" => AboutAction,
|
||||||
|
|
|
@ -5,6 +5,7 @@ pub mod build_window;
|
||||||
pub mod debug_view;
|
pub mod debug_view;
|
||||||
pub mod devices_box;
|
pub mod devices_box;
|
||||||
pub mod factories;
|
pub mod factories;
|
||||||
|
pub mod fbt_config_editor;
|
||||||
pub mod install_wivrn_box;
|
pub mod install_wivrn_box;
|
||||||
pub mod libsurvive_setup_window;
|
pub mod libsurvive_setup_window;
|
||||||
pub mod macros;
|
pub mod macros;
|
||||||
|
|
|
@ -1,11 +1,8 @@
|
||||||
use super::{
|
use super::factories::env_var_row_factory::{EnvVarModel, EnvVarModelInit};
|
||||||
factories::env_var_row_factory::{EnvVarModel, EnvVarModelInit},
|
|
||||||
preference_rows::combo_row,
|
|
||||||
};
|
|
||||||
use crate::{
|
use crate::{
|
||||||
env_var_descriptions::env_var_descriptions_as_paragraph,
|
env_var_descriptions::env_var_descriptions_as_paragraph,
|
||||||
profile::{LighthouseDriver, Profile, XRServiceType},
|
profile::{LighthouseDriver, Profile, XRServiceType},
|
||||||
ui::preference_rows::{entry_row, path_row, switch_row},
|
ui::preference_rows::{combo_row, entry_row, path_row, switch_row},
|
||||||
withclones,
|
withclones,
|
||||||
};
|
};
|
||||||
use adw::prelude::*;
|
use adw::prelude::*;
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue