diff --git a/src/ui/app.rs b/src/ui/app.rs index d303ecb..b905c9d 100644 --- a/src/ui/app.rs +++ b/src/ui/app.rs @@ -42,12 +42,13 @@ use crate::profiles::openhmd::openhmd_profile; use crate::profiles::survive::survive_profile; use crate::profiles::wivrn::wivrn_profile; use crate::profiles::wmr::wmr_profile; +use crate::stateless_action; use crate::ui::build_window::{BuildWindowMsg, BuildWindowOutMsg}; use crate::ui::debug_view::DebugViewInit; use crate::ui::libsurvive_setup_window::LibsurviveSetupMsg; use crate::ui::main_view::{MainView, MainViewInit, MainViewOutMsg}; use crate::xr_devices::XRDevice; -use crate::{stateless_action, withclones}; +use gtk::glib::clone; use gtk::prelude::*; use relm4::actions::{AccelsPlus, ActionGroupName, RelmAction, RelmActionGroup}; use relm4::adw::traits::MessageDialogExt; @@ -724,71 +725,76 @@ impl SimpleComponent for App { let mut actions = RelmActionGroup::::new(); - { - withclones![sender]; - stateless_action!(actions, BuildProfileAction, { + stateless_action!( + actions, + BuildProfileAction, + clone!(@strong sender => move |_| { sender.input_sender().emit(Msg::BuildProfile(false, false)); - }); - } - { - withclones![sender]; - stateless_action!(actions, BuildProfileCleanAction, { + }) + ); + stateless_action!( + actions, + BuildProfileCleanAction, + clone!(@strong sender => move |_| { sender.input_sender().emit(Msg::BuildProfile(true, false)); - }); - } - { - withclones![sender]; - stateless_action!(actions, BuildProfileDebugAction, { + }) + ); + stateless_action!( + actions, + BuildProfileDebugAction, + clone!(@strong sender => move |_| { sender.input_sender().emit(Msg::BuildProfile(false, true)); - }); - } - { - withclones![sender]; - stateless_action!(actions, BuildProfileCleanDebugAction, { + }) + ); + stateless_action!( + actions, + BuildProfileCleanDebugAction, + clone!(@strong sender => move |_| { sender.input_sender().emit(Msg::BuildProfile(true, true)); - }); - } - { - withclones![sender]; - stateless_action!(actions, ConfigFbtAction, { + }) + ); + stateless_action!( + actions, + ConfigFbtAction, + clone!(@strong sender => move |_| { sender.input_sender().emit(Msg::ConfigFbt); - }); - } + }) + ); { let abd_sender = model.about_dialog.sender().clone(); - stateless_action!(actions, AboutAction, { + stateless_action!(actions, AboutAction, move |_| { abd_sender.send(()).unwrap(); }); } - { - withclones![sender]; - stateless_action!(actions, QuitAction, { + stateless_action!( + actions, + QuitAction, + clone!(@strong sender => move |_| { sender.input(Msg::Quit); - }); - } - { - withclones![sender]; - stateless_action!(actions, DebugOpenDataAction, { + }) + ); + stateless_action!( + actions, + DebugOpenDataAction, + clone!(@strong sender => move |_| { sender.input(Msg::DebugOpenData); - }); - } - { - withclones![sender]; - stateless_action!(actions, DebugOpenPrefixAction, { + }) + ); + stateless_action!( + actions, + DebugOpenPrefixAction, + clone!(@strong sender => move |_| { sender.input(Msg::DebugOpenPrefix); - }); - } - { - withclones![sender]; - actions.add_action(RelmAction::::new_stateful( - &model.enable_debug_view, - move |_, state| { - let s = *state; - *state = !s; - sender.input(Msg::EnableDebugViewChanged(*state)); - }, - )) - } + }) + ); + actions.add_action(RelmAction::::new_stateful( + &model.enable_debug_view, + clone!(@strong sender => move |_, state| { + let s = *state; + *state = !s; + sender.input(Msg::EnableDebugViewChanged(*state)); + }), + )); root.insert_action_group(AppActionGroup::NAME, Some(&actions.into_action_group())); @@ -804,13 +810,13 @@ impl SimpleComponent for App { model.config.clone(), )); - { - withclones![sender]; - glib::timeout_add_local(Duration::from_millis(1000), move || { + glib::timeout_add_local( + Duration::from_millis(1000), + clone!(@strong sender => move || { sender.input(Msg::ClockTicking); glib::ControlFlow::Continue - }); - } + }), + ); model.split_view = Some(widgets.split_view.clone()); diff --git a/src/ui/debug_view.rs b/src/ui/debug_view.rs index 55f06d4..4eb7657 100644 --- a/src/ui/debug_view.rs +++ b/src/ui/debug_view.rs @@ -1,7 +1,7 @@ use crate::log_level::LogLevel; use crate::log_parser::MonadoLog; use crate::ui::app::{DebugOpenDataAction, DebugOpenPrefixAction}; -use crate::withclones; +use gtk::glib::clone; use gtk::prelude::*; use relm4::prelude::*; use relm4::{ComponentSender, SimpleComponent}; @@ -270,24 +270,18 @@ impl SimpleComponent for DebugView { if let Some(btn) = log_level_dropdown.first_child() { btn.add_css_class("flat"); } - { - withclones![sender]; - log_level_dropdown.connect_selected_notify(move |dd| { - sender.input(Self::Input::LogLevelChanged( - *LogLevel::iter() - .as_slice() - .get(dd.selected() as usize) - .unwrap(), - )); - }); - } + log_level_dropdown.connect_selected_notify(clone!(@strong sender => move |dd| { + sender.input(Self::Input::LogLevelChanged( + *LogLevel::iter() + .as_slice() + .get(dd.selected() as usize) + .unwrap(), + )); + })); - { - withclones![sender]; - adw::StyleManager::default().connect_dark_notify(move |_| { - sender.input(Self::Input::SetColorScheme); - }); - } + adw::StyleManager::default().connect_dark_notify(clone!(@strong sender => move |_| { + sender.input(Self::Input::SetColorScheme); + })); Self::set_color_scheme(&textbuf); let mut model = Self { diff --git a/src/ui/factories/tracker_role_group_factory.rs b/src/ui/factories/tracker_role_group_factory.rs index aa2ad75..2edf607 100644 --- a/src/ui/factories/tracker_role_group_factory.rs +++ b/src/ui/factories/tracker_role_group_factory.rs @@ -2,9 +2,9 @@ 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 gtk::glib::clone; use relm4::{factory::AsyncFactoryComponent, prelude::*, AsyncFactorySender}; #[derive(Debug)] @@ -54,27 +54,29 @@ impl AsyncFactoryComponent for TrackerRoleModel { } }, 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)); - }) + &entry_row( + "Device serial", + self.tracker_role.device_serial.as_str(), + clone!(@strong sender => 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::>(), - move |row| { + clone!(@strong sender => move |row| { let mut ntr = tr.clone(); ntr.role = XrtTrackerRole::from_number(&row.selected()); sender.input(Self::Input::Changed(ntr)); - } + }) ) }, } diff --git a/src/ui/fbt_config_editor.rs b/src/ui/fbt_config_editor.rs index c81a536..70abbe8 100644 --- a/src/ui/fbt_config_editor.rs +++ b/src/ui/fbt_config_editor.rs @@ -4,9 +4,9 @@ use crate::{ dump_monado_config_v0, get_monado_config_v0, MonadoConfigV0, TrackerRole, }, ui::factories::tracker_role_group_factory::TrackerRoleModelInit, - withclones, }; use adw::prelude::*; +use gtk::glib::clone; use relm4::{factory::AsyncFactoryVecDeque, prelude::*}; #[tracker::track] @@ -113,18 +113,12 @@ impl SimpleComponent for FbtConfigEditor { .label("Save") .css_classes(["suggested-action"]) .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); - }); - }; + add_btn.connect_clicked(clone!(@strong sender => move |_| { + sender.input(Self::Input::TrackerRoleNew); + })); + save_btn.connect_clicked(clone!(@strong sender => move |_| { + sender.input(Self::Input::Save); + })); let mut model = Self { win: None, diff --git a/src/ui/job_worker/internal_worker.rs b/src/ui/job_worker/internal_worker.rs index 2279861..a29b656 100644 --- a/src/ui/job_worker/internal_worker.rs +++ b/src/ui/job_worker/internal_worker.rs @@ -1,7 +1,4 @@ -use crate::{ - profile::{LighthouseDriver, Profile}, - withclones, -}; +use crate::profile::{LighthouseDriver, Profile}; use super::{ job::{CmdWorkerData, FuncWorkerOut, WorkerJob}, @@ -92,7 +89,7 @@ impl Worker for InternalJobWorker { let mut job = self.jobs.pop_front().unwrap(); match &mut job { WorkerJob::Cmd(data) => { - withclones![data]; + let data = data.clone(); if let Ok(mut cmd) = Command::new(data.command) .args(data.args) .envs(data.environment) diff --git a/src/ui/macros.rs b/src/ui/macros.rs index a66695d..1f00012 100644 --- a/src/ui/macros.rs +++ b/src/ui/macros.rs @@ -1,13 +1,6 @@ -#[macro_export] -macro_rules! withclones { - ($($var:ident),+) => { - $(let $var = $var.clone();)+ - }; -} - #[macro_export] macro_rules! stateless_action { - ($group:ident, $name:ident, $ex:expr) => { - $group.add_action(RelmAction::<$name>::new_stateless(move |_| $ex)); + ($group:ident, $name:ident, $fun:expr) => { + $group.add_action(RelmAction::<$name>::new_stateless($fun)); }; } diff --git a/src/ui/preference_rows.rs b/src/ui/preference_rows.rs index 0f28430..171b9b0 100644 --- a/src/ui/preference_rows.rs +++ b/src/ui/preference_rows.rs @@ -1,8 +1,7 @@ -use crate::withclones; use adw::prelude::*; use gtk::{ gio, - glib::{self, GString}, + glib::{self, clone, GString}, }; use relm4::prelude::*; @@ -94,33 +93,26 @@ pub fn path_row) + 'static + Clone>( .valign(gtk::Align::Center) .build(); row.add_suffix(&clear_btn); - { - withclones![path_label, cb]; - clear_btn.connect_clicked(move |_| { - path_label.set_label("(None)"); - cb(None) - }); - } + clear_btn.connect_clicked(clone!(@strong path_label, @strong cb => move |_| { + path_label.set_label("(None)"); + cb(None) + })); let filedialog = gtk::FileDialog::builder() .modal(true) .title(format!("Select Path for {}", title)) .build(); - { - withclones![path_label]; - row.connect_activated(move |_| { - withclones![path_label, cb]; - filedialog.select_folder(root_win.as_ref(), gio::Cancellable::NONE, move |res| { - if let Ok(file) = res { - if let Some(path) = file.path() { - let path_s = path.to_str().unwrap().to_string(); - path_label.set_text(path_s.as_str()); - cb(Some(path_s)) - } + row.connect_activated(clone!(@strong path_label => move |_| { + filedialog.select_folder(root_win.as_ref(), gio::Cancellable::NONE, clone!(@strong path_label, @strong cb => move |res| { + if let Ok(file) = res { + if let Some(path) = file.path() { + let path_s = path.to_str().unwrap().to_string(); + path_label.set_text(path_s.as_str()); + cb(Some(path_s)) } - }) - }); - } + } + })) + })); row } diff --git a/src/ui/profile_editor.rs b/src/ui/profile_editor.rs index 7fde772..20fbdc9 100644 --- a/src/ui/profile_editor.rs +++ b/src/ui/profile_editor.rs @@ -6,9 +6,9 @@ use crate::{ env_var_descriptions::env_var_descriptions_as_paragraph, profile::{LighthouseDriver, Profile, XRServiceType}, ui::preference_rows::{combo_row, entry_row, path_row, switch_row}, - withclones, }; use adw::prelude::*; +use gtk::glib::clone; use relm4::{factory::AsyncFactoryVecDeque, prelude::*}; use std::{cell::RefCell, rc::Rc}; @@ -80,35 +80,30 @@ impl SimpleComponent for ProfileEditor { set_vexpand: true, add: maingrp = &adw::PreferencesGroup { set_title: "General", - add: { - withclones![prof]; - &entry_row("Profile Name", model.profile.borrow().name.as_str(), move |row| { + add: &entry_row( + "Profile Name", + model.profile.borrow().name.as_str(), + clone!(@strong prof => move |row| { prof.borrow_mut().name = row.text().to_string(); }) - }, - add: { - withclones![prof]; - &switch_row( - "Update on Build", None, - model.profile.borrow().pull_on_build, - move |_, state| { - prof.borrow_mut().pull_on_build = state; - gtk::glib::Propagation::Proceed - } - ) - }, - add: { - withclones![prof]; - &path_row( - "Install Prefix", - None, - Some(model.profile.borrow().prefix.clone()), - Some(init.root_win.clone()), - move |n_path| { - prof.borrow_mut().prefix = n_path.unwrap_or_default() - }, - ) - }, + ), + add: &switch_row( + "Update on Build", None, + model.profile.borrow().pull_on_build, + clone!(@strong prof => move |_, state| { + prof.borrow_mut().pull_on_build = state; + gtk::glib::Propagation::Proceed + }) + ), + add: &path_row( + "Install Prefix", + None, + Some(model.profile.borrow().prefix.clone()), + Some(init.root_win.clone()), + clone!(@strong prof => move |n_path| { + prof.borrow_mut().prefix = n_path.unwrap_or_default() + }), + ), }, add: xrservicegrp = &adw::PreferencesGroup { set_title: "XR Service", @@ -119,227 +114,176 @@ impl SimpleComponent for ProfileEditor { "For launch options, you can insert %command% as ", "a placeholder for the actual XR Service command.", )), - add: { - withclones![prof]; - &combo_row( - "XR Service Type", - Some("Monado is for PCVR headsets, while WiVRn is for Andorid standalone headsets"), - model.profile.borrow().xrservice_type.to_string().as_str(), - XRServiceType::iter() - .map(XRServiceType::to_string) - .collect::>(), - move |row| { - prof.borrow_mut().xrservice_type = - XRServiceType::from_number(row.selected()); - }, - ) - }, - add: { - withclones![prof]; - &entry_row( - "XR Service Launch Options", - model.profile.borrow().xrservice_launch_options.as_str(), - move |row| { - prof.borrow_mut().xrservice_launch_options = row.text().trim().to_string(); - } - ) - }, - add: { - withclones![prof]; - &combo_row( - "Lighthouse Driver", - Some(concat!( - "Driver for lighhouse tracked XR devices (ie: Valve Index, HTC Vive...). Only applicable for Monado.\n\n", - "Vive: 3DOF tracking\n\n", - "Survive: 6DOF reverse engineered lighthouse tracking provided by Libsurvive\n\n", - "SteamVR: 6DOF lighthouse tracking using the proprietary SteamVR driver", - )), - model.profile.borrow().lighthouse_driver.to_string().as_str(), - LighthouseDriver::iter() - .map(LighthouseDriver::to_string) - .collect::>(), - move |row| { - prof.borrow_mut().lighthouse_driver = - LighthouseDriver::from_number(row.selected()); - } - ) - }, - add: { - withclones![prof]; - &path_row( - "XR Service Path", - None, - Some(model.profile.borrow().xrservice_path.clone()), - Some(init.root_win.clone()), - move |n_path| { - prof.borrow_mut().xrservice_path = n_path.unwrap_or_default() - }, - ) - }, - add: { - withclones![prof]; - &entry_row( - "XR Service Repo", - model.profile.borrow().xrservice_repo.clone().unwrap_or_default().as_str(), - move |row| { - let n_val = row.text().to_string(); - prof.borrow_mut().xrservice_repo = (!n_val.is_empty()).then_some(n_val); - } - ) - }, + add: &combo_row( + "XR Service Type", + Some("Monado is for PCVR headsets, while WiVRn is for Andorid standalone headsets"), + model.profile.borrow().xrservice_type.to_string().as_str(), + XRServiceType::iter() + .map(XRServiceType::to_string) + .collect::>(), + clone!(@strong prof => move |row| { + prof.borrow_mut().xrservice_type = + XRServiceType::from_number(row.selected()); + }), + ), + add: &entry_row( + "XR Service Launch Options", + model.profile.borrow().xrservice_launch_options.as_str(), + clone!(@strong prof => move |row| { + prof.borrow_mut().xrservice_launch_options = row.text().trim().to_string(); + }) + ), + add: &combo_row( + "Lighthouse Driver", + Some(concat!( + "Driver for lighhouse tracked XR devices (ie: Valve Index, HTC Vive...). Only applicable for Monado.\n\n", + "Vive: 3DOF tracking\n\n", + "Survive: 6DOF reverse engineered lighthouse tracking provided by Libsurvive\n\n", + "SteamVR: 6DOF lighthouse tracking using the proprietary SteamVR driver", + )), + model.profile.borrow().lighthouse_driver.to_string().as_str(), + LighthouseDriver::iter() + .map(LighthouseDriver::to_string) + .collect::>(), + clone!(@strong prof => move |row| { + prof.borrow_mut().lighthouse_driver = + LighthouseDriver::from_number(row.selected()); + }) + ), + add: &path_row( + "XR Service Path", + None, + Some(model.profile.borrow().xrservice_path.clone()), + Some(init.root_win.clone()), + clone!(@strong prof => move |n_path| { + prof.borrow_mut().xrservice_path = n_path.unwrap_or_default() + }), + ), + add: &entry_row( + "XR Service Repo", + model.profile.borrow().xrservice_repo.clone().unwrap_or_default().as_str(), + clone!(@strong prof => move |row| { + let n_val = row.text().to_string(); + prof.borrow_mut().xrservice_repo = (!n_val.is_empty()).then_some(n_val); + }) + ), }, 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"), - add: { - withclones![prof]; - &path_row( - "OpenComposite Path", None, - Some(model.profile.borrow().opencomposite_path.clone()), - Some(init.root_win.clone()), - move |n_path| { - prof.borrow_mut().opencomposite_path = n_path.unwrap_or_default(); - } - ) - }, - add: { - withclones![prof]; - &entry_row( - "OpenComposite Repo", - model.profile.borrow().opencomposite_repo.clone().unwrap_or_default().as_str(), - move |row| { - let n_val = row.text().to_string(); - prof.borrow_mut().opencomposite_repo = (!n_val.is_empty()).then_some(n_val); - } - ) - }, + add: &path_row( + "OpenComposite Path", None, + Some(model.profile.borrow().opencomposite_path.clone()), + Some(init.root_win.clone()), + clone!(@strong prof => move |n_path| { + prof.borrow_mut().opencomposite_path = n_path.unwrap_or_default(); + }) + ), + add: &entry_row( + "OpenComposite Repo", + model.profile.borrow().opencomposite_repo.clone().unwrap_or_default().as_str(), + clone!(@strong prof => move |row| { + let n_val = row.text().to_string(); + prof.borrow_mut().opencomposite_repo = (!n_val.is_empty()).then_some(n_val); + }) + ), }, add: libsurvivegrp = &adw::PreferencesGroup { set_title: "Libsurvive", set_description: Some("Lighthouse tracking driver\n\nWhen specifying a repository, you can set a specific git ref (branch, tag, commit...) by appending a '#' followed by the ref"), - add: { - withclones![prof]; - &switch_row( - "Enable Libsurvive", None, - model.profile.borrow().features.libsurvive.enabled, - move |_, state| { - prof.borrow_mut().features.libsurvive.enabled = state; - gtk::glib::Propagation::Proceed - } - ) - }, - add: { - withclones![prof]; - &path_row( - "Libsurvive Path", None, - model.profile.borrow().features.libsurvive.path.clone(), - Some(init.root_win.clone()), - move |n_path| { - prof.borrow_mut().features.libsurvive.path = n_path; - } - ) - }, - add: { - withclones![prof]; - &entry_row( - "Libsurvive Repo", - model.profile.borrow().features.libsurvive.repo.clone().unwrap_or_default().as_str(), - move |row| { - let n_val = row.text().to_string(); - prof.borrow_mut().features.libsurvive.repo = (!n_val.is_empty()).then_some(n_val); - } - ) - }, + add: &switch_row( + "Enable Libsurvive", None, + model.profile.borrow().features.libsurvive.enabled, + clone!(@strong prof => move |_, state| { + prof.borrow_mut().features.libsurvive.enabled = state; + gtk::glib::Propagation::Proceed + }) + ), + add: &path_row( + "Libsurvive Path", None, + model.profile.borrow().features.libsurvive.path.clone(), + Some(init.root_win.clone()), + clone!(@strong prof => move |n_path| { + prof.borrow_mut().features.libsurvive.path = n_path; + }) + ), + add: &entry_row( + "Libsurvive Repo", + model.profile.borrow().features.libsurvive.repo.clone().unwrap_or_default().as_str(), + clone!(@strong prof => move |row| { + let n_val = row.text().to_string(); + prof.borrow_mut().features.libsurvive.repo = (!n_val.is_empty()).then_some(n_val); + }) + ), }, add: openhmdgrp = &adw::PreferencesGroup { set_title: "OpenHMD", set_description: Some("Legacy driver for older Oculus HMDs\n\nWhen specifying a repository, you can set a specific git ref (branch, tag, commit...) by appending a '#' followed by the ref"), - add: { - withclones![prof]; - &switch_row( - "Enable OpenHMD", None, - model.profile.borrow().features.openhmd.enabled, - move |_, state| { - prof.borrow_mut().features.openhmd.enabled = state; - gtk::glib::Propagation::Proceed - } - ) - }, - add: { - withclones![prof]; - &path_row( - "OpenHMD Path", None, - model.profile.borrow().features.openhmd.path.clone(), - Some(init.root_win.clone()), - move |n_path| { - prof.borrow_mut().features.openhmd.path = n_path; - } - ) - }, - add: { - withclones![prof]; - &entry_row( - "OpenHMD Repo", - model.profile.borrow().features.openhmd.repo.clone().unwrap_or_default().as_str(), - move |row| { - let n_val = row.text().to_string(); - prof.borrow_mut().features.openhmd.repo = (!n_val.is_empty()).then_some(n_val); - } - ) - }, + add: &switch_row( + "Enable OpenHMD", None, + model.profile.borrow().features.openhmd.enabled, + clone!(@strong prof => move |_, state| { + prof.borrow_mut().features.openhmd.enabled = state; + gtk::glib::Propagation::Proceed + }) + ), + add: &path_row( + "OpenHMD Path", None, + model.profile.borrow().features.openhmd.path.clone(), + Some(init.root_win.clone()), + clone!(@strong prof => move |n_path| { + prof.borrow_mut().features.openhmd.path = n_path; + }) + ), + add: &entry_row( + "OpenHMD Repo", + model.profile.borrow().features.openhmd.repo.clone().unwrap_or_default().as_str(), + clone!(@strong prof => move |row| { + let n_val = row.text().to_string(); + prof.borrow_mut().features.openhmd.repo = (!n_val.is_empty()).then_some(n_val); + }) + ), }, add: basaltgrp = &adw::PreferencesGroup { set_title: "Basalt", set_description: Some("Camera based SLAM tracking driver\n\nWhen specifying a repository, you can set a specific git ref (branch, tag, commit...) by appending a '#' followed by the ref"), - add: { - withclones![prof]; - &switch_row( - "Enable Basalt", None, - model.profile.borrow().features.basalt.enabled, - move |_, state| { - prof.borrow_mut().features.basalt.enabled = state; - gtk::glib::Propagation::Proceed - } - ) - }, - add: { - withclones![prof]; - &path_row( - "Basalt Path", None, - model.profile.borrow().features.basalt.path.clone(), - Some(init.root_win.clone()), - move |n_path| { - prof.borrow_mut().features.basalt.path = n_path; - } - ) - }, - add: { - withclones![prof]; - &entry_row( - "Basalt Repo", - model.profile.borrow().features.basalt.repo.clone().unwrap_or_default().as_str(), - move |row| { - let n_val = row.text().to_string(); - prof.borrow_mut().features.basalt.repo = n_val.is_empty().then_some(n_val); - } - ) - }, + add: &switch_row( + "Enable Basalt", None, + model.profile.borrow().features.basalt.enabled, + clone!(@strong prof => move |_, state| { + prof.borrow_mut().features.basalt.enabled = state; + gtk::glib::Propagation::Proceed + }) + ), + add: &path_row( + "Basalt Path", None, + model.profile.borrow().features.basalt.path.clone(), + Some(init.root_win.clone()), + clone!(@strong prof => move |n_path| { + prof.borrow_mut().features.basalt.path = n_path; + }) + ), + add: &entry_row( + "Basalt Repo", + model.profile.borrow().features.basalt.repo.clone().unwrap_or_default().as_str(), + clone!(@strong prof => move |row| { + let n_val = row.text().to_string(); + prof.borrow_mut().features.basalt.repo = n_val.is_empty().then_some(n_val); + }) + ), }, add: mercurygrp = &adw::PreferencesGroup { set_title: "Mercury", set_description: Some("Camera and OpenCV based hand tracking driver"), - add: { - withclones![prof]; - &switch_row( - "Enable Mercury", None, - model.profile.borrow().features.mercury_enabled, - move |_, state| { - prof.borrow_mut().features.mercury_enabled = state; - gtk::glib::Propagation::Proceed - } - ) - }, + add: &switch_row( + "Enable Mercury", None, + model.profile.borrow().features.mercury_enabled, + clone!(@strong prof => move |_, state| { + prof.borrow_mut().features.mercury_enabled = state; + gtk::glib::Propagation::Proceed + }) + ), }, add: model.env_rows.widget(), } @@ -465,16 +409,17 @@ impl SimpleComponent for ProfileEditor { .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())); - } - }); + add_btn.connect_clicked( + clone!(@strong sender, @strong name_entry, @strong popover => 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 }}; }