mirror of
https://gitlab.com/gabmus/envision.git
synced 2025-08-02 22:29:01 +00:00
feat: can enable and disable plugins
This commit is contained in:
parent
2f8caafac2
commit
781e2f4fd2
5 changed files with 178 additions and 29 deletions
|
@ -7,15 +7,36 @@ use crate::{
|
||||||
lighthouse::lighthouse_profile, openhmd::openhmd_profile, simulated::simulated_profile,
|
lighthouse::lighthouse_profile, openhmd::openhmd_profile, simulated::simulated_profile,
|
||||||
survive::survive_profile, wivrn::wivrn_profile, wmr::wmr_profile,
|
survive::survive_profile, wivrn::wivrn_profile, wmr::wmr_profile,
|
||||||
},
|
},
|
||||||
|
ui::plugins::Plugin,
|
||||||
util::file_utils::get_writer,
|
util::file_utils::get_writer,
|
||||||
};
|
};
|
||||||
use serde::{de::Error, Deserialize, Serialize};
|
use serde::{de::Error, Deserialize, Serialize};
|
||||||
use std::{
|
use std::{
|
||||||
|
collections::HashMap,
|
||||||
fs::File,
|
fs::File,
|
||||||
io::BufReader,
|
io::BufReader,
|
||||||
path::{Path, PathBuf},
|
path::{Path, PathBuf},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||||
|
pub struct PluginConfig {
|
||||||
|
pub appid: String,
|
||||||
|
pub version: String,
|
||||||
|
pub enabled: bool,
|
||||||
|
pub exec_path: PathBuf,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<&Plugin> for PluginConfig {
|
||||||
|
fn from(p: &Plugin) -> Self {
|
||||||
|
Self {
|
||||||
|
appid: p.appid.clone(),
|
||||||
|
version: p.version.clone(),
|
||||||
|
enabled: true,
|
||||||
|
exec_path: p.exec_path(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const DEFAULT_WIN_SIZE: [i32; 2] = [360, 400];
|
const DEFAULT_WIN_SIZE: [i32; 2] = [360, 400];
|
||||||
|
|
||||||
const fn default_win_size() -> [i32; 2] {
|
const fn default_win_size() -> [i32; 2] {
|
||||||
|
@ -29,6 +50,8 @@ pub struct Config {
|
||||||
pub user_profiles: Vec<Profile>,
|
pub user_profiles: Vec<Profile>,
|
||||||
#[serde(default = "default_win_size")]
|
#[serde(default = "default_win_size")]
|
||||||
pub win_size: [i32; 2],
|
pub win_size: [i32; 2],
|
||||||
|
#[serde(default)]
|
||||||
|
pub plugins: HashMap<String, PluginConfig>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for Config {
|
impl Default for Config {
|
||||||
|
@ -37,8 +60,9 @@ impl Default for Config {
|
||||||
// TODO: using an empty string here is ugly
|
// TODO: using an empty string here is ugly
|
||||||
selected_profile_uuid: "".to_string(),
|
selected_profile_uuid: "".to_string(),
|
||||||
debug_view_enabled: false,
|
debug_view_enabled: false,
|
||||||
user_profiles: vec![],
|
user_profiles: Vec::default(),
|
||||||
win_size: DEFAULT_WIN_SIZE,
|
win_size: DEFAULT_WIN_SIZE,
|
||||||
|
plugins: HashMap::default(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,7 +11,7 @@ use super::{
|
||||||
},
|
},
|
||||||
libsurvive_setup_window::{LibsurviveSetupMsg, LibsurviveSetupWindow},
|
libsurvive_setup_window::{LibsurviveSetupMsg, LibsurviveSetupWindow},
|
||||||
main_view::{MainView, MainViewInit, MainViewMsg, MainViewOutMsg},
|
main_view::{MainView, MainViewInit, MainViewMsg, MainViewOutMsg},
|
||||||
plugins::store::{PluginStore, PluginStoreMsg},
|
plugins::store::{PluginStore, PluginStoreInit, PluginStoreMsg, PluginStoreOutMsg},
|
||||||
util::{copiable_code_snippet, copy_text, open_with_default_handler},
|
util::{copiable_code_snippet, copy_text, open_with_default_handler},
|
||||||
wivrn_conf_editor::{WivrnConfEditor, WivrnConfEditorInit, WivrnConfEditorMsg},
|
wivrn_conf_editor::{WivrnConfEditor, WivrnConfEditorInit, WivrnConfEditorMsg},
|
||||||
};
|
};
|
||||||
|
@ -22,7 +22,7 @@ use crate::{
|
||||||
build_opencomposite::get_build_opencomposite_jobs, build_openhmd::get_build_openhmd_jobs,
|
build_opencomposite::get_build_opencomposite_jobs, build_openhmd::get_build_openhmd_jobs,
|
||||||
build_wivrn::get_build_wivrn_jobs,
|
build_wivrn::get_build_wivrn_jobs,
|
||||||
},
|
},
|
||||||
config::Config,
|
config::{Config, PluginConfig},
|
||||||
constants::APP_NAME,
|
constants::APP_NAME,
|
||||||
depcheck::common::dep_pkexec,
|
depcheck::common::dep_pkexec,
|
||||||
file_builders::{
|
file_builders::{
|
||||||
|
@ -57,7 +57,11 @@ use relm4::{
|
||||||
new_action_group, new_stateful_action, new_stateless_action,
|
new_action_group, new_stateful_action, new_stateless_action,
|
||||||
prelude::*,
|
prelude::*,
|
||||||
};
|
};
|
||||||
use std::{collections::VecDeque, fs::remove_file, time::Duration};
|
use std::{
|
||||||
|
collections::{HashMap, VecDeque},
|
||||||
|
fs::remove_file,
|
||||||
|
time::Duration,
|
||||||
|
};
|
||||||
use tracing::error;
|
use tracing::error;
|
||||||
|
|
||||||
pub struct App {
|
pub struct App {
|
||||||
|
@ -123,6 +127,7 @@ pub enum Msg {
|
||||||
OnProberExit(bool),
|
OnProberExit(bool),
|
||||||
WivrnCheckPairMode,
|
WivrnCheckPairMode,
|
||||||
OpenPluginStore,
|
OpenPluginStore,
|
||||||
|
UpdateConfigPlugins(HashMap<String, PluginConfig>),
|
||||||
NoOp,
|
NoOp,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -795,10 +800,20 @@ impl AsyncComponent for App {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Msg::OpenPluginStore => {
|
Msg::OpenPluginStore => {
|
||||||
let pluginstore = PluginStore::builder().launch(()).detach();
|
let pluginstore = PluginStore::builder()
|
||||||
|
.launch(PluginStoreInit {
|
||||||
|
config_plugins: self.config.plugins.clone(),
|
||||||
|
})
|
||||||
|
.forward(sender.input_sender(), move |msg| match msg {
|
||||||
|
PluginStoreOutMsg::UpdateConfigPlugins(cp) => Msg::UpdateConfigPlugins(cp),
|
||||||
|
});
|
||||||
pluginstore.sender().emit(PluginStoreMsg::Present);
|
pluginstore.sender().emit(PluginStoreMsg::Present);
|
||||||
self.pluginstore = Some(pluginstore);
|
self.pluginstore = Some(pluginstore);
|
||||||
}
|
}
|
||||||
|
Msg::UpdateConfigPlugins(cp) => {
|
||||||
|
self.config.plugins = cp;
|
||||||
|
self.config.save();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -3,10 +3,14 @@ use super::{
|
||||||
store_row_factory::{StoreRowModel, StoreRowModelInit, StoreRowModelMsg, StoreRowModelOutMsg},
|
store_row_factory::{StoreRowModel, StoreRowModelInit, StoreRowModelMsg, StoreRowModelOutMsg},
|
||||||
Plugin,
|
Plugin,
|
||||||
};
|
};
|
||||||
use crate::{downloader::download_file_async, ui::alert::alert};
|
use crate::{
|
||||||
|
config::PluginConfig,
|
||||||
|
downloader::download_file_async,
|
||||||
|
ui::{alert::alert, SENDER_IO_ERR_MSG},
|
||||||
|
};
|
||||||
use adw::prelude::*;
|
use adw::prelude::*;
|
||||||
use relm4::{factory::AsyncFactoryVecDeque, prelude::*};
|
use relm4::{factory::AsyncFactoryVecDeque, prelude::*};
|
||||||
use std::fs::remove_file;
|
use std::{collections::HashMap, fs::remove_file};
|
||||||
|
|
||||||
#[tracker::track]
|
#[tracker::track]
|
||||||
pub struct PluginStore {
|
pub struct PluginStore {
|
||||||
|
@ -18,6 +22,8 @@ pub struct PluginStore {
|
||||||
details: AsyncController<StoreDetail>,
|
details: AsyncController<StoreDetail>,
|
||||||
#[tracker::do_not_track]
|
#[tracker::do_not_track]
|
||||||
main_stack: Option<gtk::Stack>,
|
main_stack: Option<gtk::Stack>,
|
||||||
|
#[tracker::do_not_track]
|
||||||
|
config_plugins: HashMap<String, PluginConfig>,
|
||||||
refreshing: bool,
|
refreshing: bool,
|
||||||
locked: bool,
|
locked: bool,
|
||||||
plugins: Vec<Plugin>,
|
plugins: Vec<Plugin>,
|
||||||
|
@ -32,18 +38,24 @@ pub enum PluginStoreMsg {
|
||||||
InstallDownload(Plugin, relm4::Sender<StoreRowModelMsg>),
|
InstallDownload(Plugin, relm4::Sender<StoreRowModelMsg>),
|
||||||
RemoveFromDetails(Plugin),
|
RemoveFromDetails(Plugin),
|
||||||
Remove(Plugin, relm4::Sender<StoreRowModelMsg>),
|
Remove(Plugin, relm4::Sender<StoreRowModelMsg>),
|
||||||
Enable(String),
|
SetEnabled(Plugin, bool),
|
||||||
Disable(String),
|
|
||||||
ShowDetails(usize),
|
ShowDetails(usize),
|
||||||
ShowPluginList,
|
ShowPluginList,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum PluginStoreOutMsg {}
|
pub struct PluginStoreInit {
|
||||||
|
pub config_plugins: HashMap<String, PluginConfig>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum PluginStoreOutMsg {
|
||||||
|
UpdateConfigPlugins(HashMap<String, PluginConfig>),
|
||||||
|
}
|
||||||
|
|
||||||
#[relm4::component(pub async)]
|
#[relm4::component(pub async)]
|
||||||
impl AsyncComponent for PluginStore {
|
impl AsyncComponent for PluginStore {
|
||||||
type Init = ();
|
type Init = PluginStoreInit;
|
||||||
type Input = PluginStoreMsg;
|
type Input = PluginStoreMsg;
|
||||||
type Output = PluginStoreOutMsg;
|
type Output = PluginStoreOutMsg;
|
||||||
type CommandOutput = ();
|
type CommandOutput = ();
|
||||||
|
@ -181,6 +193,10 @@ impl AsyncComponent for PluginStore {
|
||||||
self.plugins.iter().for_each(|plugin| {
|
self.plugins.iter().for_each(|plugin| {
|
||||||
guard.push_back(StoreRowModelInit {
|
guard.push_back(StoreRowModelInit {
|
||||||
plugin: plugin.clone(),
|
plugin: plugin.clone(),
|
||||||
|
enabled: self
|
||||||
|
.config_plugins
|
||||||
|
.get(&plugin.appid)
|
||||||
|
.is_some_and(|cp| cp.enabled),
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -231,9 +247,17 @@ impl AsyncComponent for PluginStore {
|
||||||
)),
|
)),
|
||||||
Some(&self.win.as_ref().unwrap().clone().upcast::<gtk::Window>()),
|
Some(&self.win.as_ref().unwrap().clone().upcast::<gtk::Window>()),
|
||||||
);
|
);
|
||||||
|
} else {
|
||||||
|
self.config_plugins
|
||||||
|
.insert(plugin.appid.clone(), PluginConfig::from(&plugin));
|
||||||
|
sender
|
||||||
|
.output(Self::Output::UpdateConfigPlugins(
|
||||||
|
self.config_plugins.clone(),
|
||||||
|
))
|
||||||
|
.expect(SENDER_IO_ERR_MSG);
|
||||||
}
|
}
|
||||||
row_sender.emit(StoreRowModelMsg::Refresh);
|
row_sender.emit(StoreRowModelMsg::Refresh(true));
|
||||||
self.details.emit(StoreDetailMsg::Refresh);
|
self.details.emit(StoreDetailMsg::Refresh(true));
|
||||||
self.set_locked(false);
|
self.set_locked(false);
|
||||||
}
|
}
|
||||||
Self::Input::Remove(plugin, row_sender) => {
|
Self::Input::Remove(plugin, row_sender) => {
|
||||||
|
@ -249,21 +273,58 @@ impl AsyncComponent for PluginStore {
|
||||||
)),
|
)),
|
||||||
Some(&self.win.as_ref().unwrap().clone().upcast::<gtk::Window>()),
|
Some(&self.win.as_ref().unwrap().clone().upcast::<gtk::Window>()),
|
||||||
);
|
);
|
||||||
|
} else {
|
||||||
|
self.config_plugins.remove(&plugin.appid);
|
||||||
|
sender
|
||||||
|
.output(Self::Output::UpdateConfigPlugins(
|
||||||
|
self.config_plugins.clone(),
|
||||||
|
))
|
||||||
|
.expect(SENDER_IO_ERR_MSG);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
row_sender.emit(StoreRowModelMsg::Refresh);
|
row_sender.emit(StoreRowModelMsg::Refresh(false));
|
||||||
self.details.emit(StoreDetailMsg::Refresh);
|
self.details.emit(StoreDetailMsg::Refresh(false));
|
||||||
self.set_locked(false);
|
self.set_locked(false);
|
||||||
}
|
}
|
||||||
Self::Input::Enable(_) => todo!(),
|
Self::Input::SetEnabled(plugin, enabled) => {
|
||||||
Self::Input::Disable(_) => todo!(),
|
if let Some(cp) = self.config_plugins.get_mut(&plugin.appid) {
|
||||||
|
cp.enabled = enabled;
|
||||||
|
if let Some(row) = self
|
||||||
|
.plugin_rows
|
||||||
|
.as_mut()
|
||||||
|
.unwrap()
|
||||||
|
.guard()
|
||||||
|
.iter()
|
||||||
|
.find(|row| row.is_some_and(|row| row.plugin.appid == plugin.appid))
|
||||||
|
.flatten()
|
||||||
|
{
|
||||||
|
row.input_sender.emit(StoreRowModelMsg::Refresh(enabled));
|
||||||
|
} else {
|
||||||
|
eprintln!("could not find corresponding listbox row!")
|
||||||
|
}
|
||||||
|
self.details.emit(StoreDetailMsg::Refresh(enabled));
|
||||||
|
} else {
|
||||||
|
eprintln!(
|
||||||
|
"failed to set plugin {} enabled: could not find in hashmap",
|
||||||
|
plugin.appid
|
||||||
|
)
|
||||||
|
}
|
||||||
|
sender
|
||||||
|
.output(Self::Output::UpdateConfigPlugins(
|
||||||
|
self.config_plugins.clone(),
|
||||||
|
))
|
||||||
|
.expect(SENDER_IO_ERR_MSG);
|
||||||
|
}
|
||||||
// we use index here because it's the listbox not the row that can
|
// we use index here because it's the listbox not the row that can
|
||||||
// send this signal, so I don't directly have the plugin object
|
// send this signal, so I don't directly have the plugin object
|
||||||
Self::Input::ShowDetails(index) => {
|
Self::Input::ShowDetails(index) => {
|
||||||
if let Some(plugin) = self.plugins.get(index) {
|
if let Some(plugin) = self.plugins.get(index) {
|
||||||
self.details
|
self.details.sender().emit(StoreDetailMsg::SetPlugin(
|
||||||
.sender()
|
plugin.clone(),
|
||||||
.emit(StoreDetailMsg::SetPlugin(plugin.clone()));
|
self.config_plugins
|
||||||
|
.get(&plugin.appid)
|
||||||
|
.is_some_and(|cp| cp.enabled),
|
||||||
|
));
|
||||||
self.main_stack
|
self.main_stack
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
|
@ -282,7 +343,7 @@ impl AsyncComponent for PluginStore {
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn init(
|
async fn init(
|
||||||
_init: Self::Init,
|
init: Self::Init,
|
||||||
root: Self::Root,
|
root: Self::Root,
|
||||||
sender: AsyncComponentSender<Self>,
|
sender: AsyncComponentSender<Self>,
|
||||||
) -> AsyncComponentParts<Self> {
|
) -> AsyncComponentParts<Self> {
|
||||||
|
@ -299,7 +360,11 @@ impl AsyncComponent for PluginStore {
|
||||||
StoreDetailOutMsg::GoBack => Self::Input::ShowPluginList,
|
StoreDetailOutMsg::GoBack => Self::Input::ShowPluginList,
|
||||||
StoreDetailOutMsg::Install(plugin) => Self::Input::InstallFromDetails(plugin),
|
StoreDetailOutMsg::Install(plugin) => Self::Input::InstallFromDetails(plugin),
|
||||||
StoreDetailOutMsg::Remove(plugin) => Self::Input::RemoveFromDetails(plugin),
|
StoreDetailOutMsg::Remove(plugin) => Self::Input::RemoveFromDetails(plugin),
|
||||||
|
StoreDetailOutMsg::SetEnabled(plugin, enabled) => {
|
||||||
|
Self::Input::SetEnabled(plugin, enabled)
|
||||||
|
}
|
||||||
}),
|
}),
|
||||||
|
config_plugins: init.config_plugins,
|
||||||
main_stack: None,
|
main_stack: None,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -318,6 +383,9 @@ impl AsyncComponent for PluginStore {
|
||||||
StoreRowModelOutMsg::Remove(appid, row_sender) => {
|
StoreRowModelOutMsg::Remove(appid, row_sender) => {
|
||||||
Self::Input::Remove(appid, row_sender)
|
Self::Input::Remove(appid, row_sender)
|
||||||
}
|
}
|
||||||
|
StoreRowModelOutMsg::SetEnabled(plugin, enabled) => {
|
||||||
|
Self::Input::SetEnabled(plugin, enabled)
|
||||||
|
}
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
model.main_stack = Some(widgets.main_stack.clone());
|
model.main_stack = Some(widgets.main_stack.clone());
|
||||||
|
|
|
@ -6,6 +6,7 @@ use relm4::prelude::*;
|
||||||
#[tracker::track]
|
#[tracker::track]
|
||||||
pub struct StoreDetail {
|
pub struct StoreDetail {
|
||||||
plugin: Option<Plugin>,
|
plugin: Option<Plugin>,
|
||||||
|
enabled: bool,
|
||||||
#[tracker::do_not_track]
|
#[tracker::do_not_track]
|
||||||
carousel: Option<adw::Carousel>,
|
carousel: Option<adw::Carousel>,
|
||||||
#[tracker::do_not_track]
|
#[tracker::do_not_track]
|
||||||
|
@ -15,12 +16,13 @@ pub struct StoreDetail {
|
||||||
#[allow(clippy::large_enum_variant)]
|
#[allow(clippy::large_enum_variant)]
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum StoreDetailMsg {
|
pub enum StoreDetailMsg {
|
||||||
SetPlugin(Plugin),
|
SetPlugin(Plugin, bool),
|
||||||
SetIcon,
|
SetIcon,
|
||||||
SetScreenshots,
|
SetScreenshots,
|
||||||
Refresh,
|
Refresh(bool),
|
||||||
Install,
|
Install,
|
||||||
Remove,
|
Remove,
|
||||||
|
SetEnabled(bool),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
|
@ -28,6 +30,7 @@ pub enum StoreDetailOutMsg {
|
||||||
Install(Plugin),
|
Install(Plugin),
|
||||||
Remove(Plugin),
|
Remove(Plugin),
|
||||||
GoBack,
|
GoBack,
|
||||||
|
SetEnabled(Plugin, bool),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[relm4::component(pub async)]
|
#[relm4::component(pub async)]
|
||||||
|
@ -128,11 +131,18 @@ impl AsyncComponent for StoreDetail {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
gtk::Switch {
|
gtk::Switch {
|
||||||
#[track = "self.changed(Self::plugin())"]
|
#[track = "model.changed(Self::plugin())"]
|
||||||
set_visible: model.plugin.as_ref()
|
set_visible: model.plugin.as_ref()
|
||||||
.is_some_and(|p| p.is_installed()),
|
.is_some_and(|p| p.is_installed()),
|
||||||
|
#[track = "model.changed(Self::enabled())"]
|
||||||
|
set_active: model.enabled,
|
||||||
|
set_tooltip_text: Some("Plugin enabled"),
|
||||||
set_valign: gtk::Align::Center,
|
set_valign: gtk::Align::Center,
|
||||||
set_halign: gtk::Align::Center,
|
set_halign: gtk::Align::Center,
|
||||||
|
connect_state_set[sender] => move |_, state| {
|
||||||
|
sender.input(Self::Input::SetEnabled(state));
|
||||||
|
gtk::glib::Propagation::Proceed
|
||||||
|
}
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -184,8 +194,9 @@ impl AsyncComponent for StoreDetail {
|
||||||
self.reset();
|
self.reset();
|
||||||
|
|
||||||
match message {
|
match message {
|
||||||
Self::Input::SetPlugin(p) => {
|
Self::Input::SetPlugin(p, enabled) => {
|
||||||
self.set_plugin(Some(p));
|
self.set_plugin(Some(p));
|
||||||
|
self.set_enabled(enabled);
|
||||||
sender.input(Self::Input::SetIcon);
|
sender.input(Self::Input::SetIcon);
|
||||||
sender.input(Self::Input::SetScreenshots);
|
sender.input(Self::Input::SetScreenshots);
|
||||||
}
|
}
|
||||||
|
@ -233,8 +244,9 @@ impl AsyncComponent for StoreDetail {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Self::Input::Refresh => {
|
Self::Input::Refresh(enabled) => {
|
||||||
self.mark_all_changed();
|
self.mark_all_changed();
|
||||||
|
self.set_enabled(enabled);
|
||||||
}
|
}
|
||||||
Self::Input::Install => {
|
Self::Input::Install => {
|
||||||
if let Some(plugin) = self.plugin.as_ref() {
|
if let Some(plugin) = self.plugin.as_ref() {
|
||||||
|
@ -250,6 +262,14 @@ impl AsyncComponent for StoreDetail {
|
||||||
.expect(SENDER_IO_ERR_MSG);
|
.expect(SENDER_IO_ERR_MSG);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Self::Input::SetEnabled(enabled) => {
|
||||||
|
self.set_enabled(enabled);
|
||||||
|
if let Some(plugin) = self.plugin.as_ref() {
|
||||||
|
sender
|
||||||
|
.output(Self::Output::SetEnabled(plugin.clone(), enabled))
|
||||||
|
.expect(SENDER_IO_ERR_MSG);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -261,6 +281,7 @@ impl AsyncComponent for StoreDetail {
|
||||||
let mut model = Self {
|
let mut model = Self {
|
||||||
tracker: 0,
|
tracker: 0,
|
||||||
plugin: None,
|
plugin: None,
|
||||||
|
enabled: false,
|
||||||
carousel: None,
|
carousel: None,
|
||||||
icon: None,
|
icon: None,
|
||||||
};
|
};
|
||||||
|
|
|
@ -14,23 +14,27 @@ pub struct StoreRowModel {
|
||||||
icon: Option<gtk::Image>,
|
icon: Option<gtk::Image>,
|
||||||
#[tracker::do_not_track]
|
#[tracker::do_not_track]
|
||||||
pub input_sender: relm4::Sender<StoreRowModelMsg>,
|
pub input_sender: relm4::Sender<StoreRowModelMsg>,
|
||||||
|
pub enabled: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct StoreRowModelInit {
|
pub struct StoreRowModelInit {
|
||||||
pub plugin: Plugin,
|
pub plugin: Plugin,
|
||||||
|
pub enabled: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum StoreRowModelMsg {
|
pub enum StoreRowModelMsg {
|
||||||
LoadIcon,
|
LoadIcon,
|
||||||
Refresh,
|
Refresh(bool),
|
||||||
|
SetEnabled(bool),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum StoreRowModelOutMsg {
|
pub enum StoreRowModelOutMsg {
|
||||||
Install(Plugin, relm4::Sender<StoreRowModelMsg>),
|
Install(Plugin, relm4::Sender<StoreRowModelMsg>),
|
||||||
Remove(Plugin, relm4::Sender<StoreRowModelMsg>),
|
Remove(Plugin, relm4::Sender<StoreRowModelMsg>),
|
||||||
|
SetEnabled(Plugin, bool),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[relm4::factory(async pub)]
|
#[relm4::factory(async pub)]
|
||||||
|
@ -121,15 +125,22 @@ impl AsyncFactoryComponent for StoreRowModel {
|
||||||
gtk::Switch {
|
gtk::Switch {
|
||||||
#[track = "self.changed(StoreRowModel::plugin())"]
|
#[track = "self.changed(StoreRowModel::plugin())"]
|
||||||
set_visible: self.plugin.is_installed(),
|
set_visible: self.plugin.is_installed(),
|
||||||
|
#[track = "self.changed(StoreRowModel::enabled())"]
|
||||||
|
set_active: self.enabled,
|
||||||
set_valign: gtk::Align::Center,
|
set_valign: gtk::Align::Center,
|
||||||
set_halign: gtk::Align::Center,
|
set_halign: gtk::Align::Center,
|
||||||
|
set_tooltip_text: Some("Plugin enabled"),
|
||||||
|
connect_state_set[sender] => move |_, state| {
|
||||||
|
sender.input(Self::Input::SetEnabled(state));
|
||||||
|
gtk::glib::Propagation::Proceed
|
||||||
|
}
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn update(&mut self, message: Self::Input, _sender: AsyncFactorySender<Self>) {
|
async fn update(&mut self, message: Self::Input, sender: AsyncFactorySender<Self>) {
|
||||||
self.reset();
|
self.reset();
|
||||||
|
|
||||||
match message {
|
match message {
|
||||||
|
@ -145,7 +156,16 @@ impl AsyncFactoryComponent for StoreRowModel {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Self::Input::Refresh => self.mark_all_changed(),
|
Self::Input::SetEnabled(state) => {
|
||||||
|
self.set_enabled(state);
|
||||||
|
sender
|
||||||
|
.output(Self::Output::SetEnabled(self.plugin.clone(), state))
|
||||||
|
.expect(SENDER_IO_ERR_MSG);
|
||||||
|
}
|
||||||
|
Self::Input::Refresh(enabled) => {
|
||||||
|
self.mark_all_changed();
|
||||||
|
self.set_enabled(enabled)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -157,6 +177,7 @@ impl AsyncFactoryComponent for StoreRowModel {
|
||||||
Self {
|
Self {
|
||||||
tracker: 0,
|
tracker: 0,
|
||||||
plugin: init.plugin,
|
plugin: init.plugin,
|
||||||
|
enabled: init.enabled,
|
||||||
icon: None,
|
icon: None,
|
||||||
input_sender: sender.input_sender().clone(),
|
input_sender: sender.input_sender().clone(),
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue