feat: profile editor can add and remove env vars

This commit is contained in:
Gabriele Musco 2023-06-25 12:57:28 +02:00
commit 371f874e3b
No known key found for this signature in database
GPG key ID: 1068D795C80E51DE
3 changed files with 93 additions and 30 deletions

View file

@ -4,33 +4,35 @@ use gtk::prelude::*;
use relm4::prelude::*; use relm4::prelude::*;
#[derive(Debug)] #[derive(Debug)]
pub struct EntryModel { pub struct EnvVarModel {
name: String, pub name: String,
value: String, value: String,
} }
pub struct EntryModelInit { pub struct EnvVarModelInit {
pub name: String, pub name: String,
pub value: String, pub value: String,
} }
#[derive(Debug)] #[derive(Debug)]
pub enum EntryModelMsg { pub enum EnvVarModelMsg {
Changed(String), Changed(String),
Delete,
} }
#[derive(Debug)] #[derive(Debug)]
pub enum EntryModelOutMsg { pub enum EnvVarModelOutMsg {
Changed(String, String), Changed(String, String),
Delete(String),
} }
#[relm4::factory(pub)] #[relm4::factory(pub)]
impl FactoryComponent for EntryModel { impl FactoryComponent for EnvVarModel {
type Init = EntryModelInit; type Init = EnvVarModelInit;
type Input = EntryModelMsg; type Input = EnvVarModelMsg;
type Output = EntryModelOutMsg; type Output = EnvVarModelOutMsg;
type CommandOutput = (); type CommandOutput = ();
type Widgets = EntryModelWidgets; type Widgets = EnvVarModelWidgets;
type ParentInput = ProfileEditorMsg; type ParentInput = ProfileEditorMsg;
type ParentWidget = adw::PreferencesGroup; type ParentWidget = adw::PreferencesGroup;
@ -38,6 +40,16 @@ impl FactoryComponent for EntryModel {
root = adw::EntryRow { root = adw::EntryRow {
set_title: &self.name, set_title: &self.name,
set_text: &self.value, set_text: &self.value,
add_suffix: del_btn = &gtk::Button {
set_icon_name: "edit-delete-symbolic",
set_tooltip_text: Some("Delete Environment Variable"),
set_valign: gtk::Align::Center,
add_css_class: "flat",
add_css_class: "circular",
connect_clicked[sender] => move |_| {
sender.input(Self::Input::Delete);
}
},
connect_changed[sender] => move |entry| { connect_changed[sender] => move |entry| {
sender.input_sender().emit(Self::Input::Changed(entry.text().to_string())); sender.input_sender().emit(Self::Input::Changed(entry.text().to_string()));
}, },
@ -52,12 +64,16 @@ impl FactoryComponent for EntryModel {
.output_sender() .output_sender()
.emit(Self::Output::Changed(self.name.clone(), val)); .emit(Self::Output::Changed(self.name.clone(), val));
} }
Self::Input::Delete => {
sender.output(Self::Output::Delete(self.name.clone()));
}
} }
} }
fn forward_to_parent(output: Self::Output) -> Option<Self::ParentInput> { fn forward_to_parent(output: Self::Output) -> Option<Self::ParentInput> {
Some(match output { Some(match output {
Self::Output::Changed(name, value) => ProfileEditorMsg::EntryChanged(name, value), Self::Output::Changed(name, value) => ProfileEditorMsg::EnvVarChanged(name, value),
Self::Output::Delete(name) => ProfileEditorMsg::EnvVarDelete(name),
}) })
} }

View file

@ -1,3 +1,3 @@
pub mod entry_row_factory; pub mod env_var_row_factory;
pub mod switch_row_factory; pub mod switch_row_factory;
pub mod path_row_factory; pub mod path_row_factory;

View file

@ -1,5 +1,5 @@
use super::factories::{ use super::factories::{
entry_row_factory::{EntryModel, EntryModelInit}, env_var_row_factory::{EnvVarModel, EnvVarModelInit},
path_row_factory::{PathModel, PathModelInit}, path_row_factory::{PathModel, PathModelInit},
switch_row_factory::{SwitchModel, SwitchModelInit}, switch_row_factory::{SwitchModel, SwitchModelInit},
}; };
@ -24,7 +24,7 @@ pub struct ProfileEditor {
#[tracker::do_not_track] #[tracker::do_not_track]
type_row: adw::ComboRow, type_row: adw::ComboRow,
#[tracker::do_not_track] #[tracker::do_not_track]
env_rows: FactoryVecDeque<EntryModel>, env_rows: FactoryVecDeque<EnvVarModel>,
#[tracker::do_not_track] #[tracker::do_not_track]
switch_rows: FactoryVecDeque<SwitchModel>, switch_rows: FactoryVecDeque<SwitchModel>,
#[tracker::do_not_track] #[tracker::do_not_track]
@ -34,12 +34,13 @@ pub struct ProfileEditor {
#[derive(Debug)] #[derive(Debug)]
pub enum ProfileEditorMsg { pub enum ProfileEditorMsg {
Present(Profile), Present(Profile),
EntryChanged(String, String), EnvVarChanged(String, String),
EnvVarDelete(String),
TextChanged(String, String), TextChanged(String, String),
PathChanged(String, Option<String>), PathChanged(String, Option<String>),
SwitchChanged(String, bool), SwitchChanged(String, bool),
ComboChanged(String, String), ComboChanged(String, String),
AddEnvVar, AddEnvVar(String),
SaveProfile, SaveProfile,
} }
@ -107,7 +108,7 @@ impl SimpleComponent for ProfileEditor {
self.env_rows.guard().clear(); self.env_rows.guard().clear();
for (k, v) in p.environment.iter() { for (k, v) in p.environment.iter() {
self.env_rows.guard().push_back(EntryModelInit { self.env_rows.guard().push_back(EnvVarModelInit {
name: k.clone(), name: k.clone(),
value: v.clone(), value: v.clone(),
}); });
@ -175,18 +176,31 @@ impl SimpleComponent for ProfileEditor {
sender.output(ProfileEditorOutMsg::SaveProfile(prof.clone())); sender.output(ProfileEditorOutMsg::SaveProfile(prof.clone()));
self.win.as_ref().unwrap().close(); self.win.as_ref().unwrap().close();
} else { } else {
self.win.as_ref().unwrap().add_toast(adw::Toast::builder().title("Profile failed validation").build()); self.win.as_ref().unwrap().add_toast(
adw::Toast::builder()
.title("Profile failed validation")
.build(),
);
} }
} }
// TODO: rename to reflect this is only for env Self::Input::EnvVarChanged(name, value) => {
// + make entryfactory take a signal to send to parent
Self::Input::EntryChanged(name, value) => {
self.profile self.profile
.as_mut() .as_mut()
.unwrap() .unwrap()
.environment .environment
.insert(name, value); .insert(name, value);
} }
Self::Input::EnvVarDelete(name) => {
self.profile.as_mut().unwrap().environment.remove(&name);
let pos = self
.env_rows
.guard()
.iter()
.position(|evr| evr.name == name);
if pos.is_some() {
self.env_rows.guard().remove(pos.unwrap());
}
}
Self::Input::PathChanged(key, value) => { Self::Input::PathChanged(key, value) => {
let prof = self.profile.as_mut().unwrap(); let prof = self.profile.as_mut().unwrap();
match key.as_str() { match key.as_str() {
@ -224,8 +238,15 @@ impl SimpleComponent for ProfileEditor {
_ => panic!("Unknown profile text key"), _ => panic!("Unknown profile text key"),
} }
} }
Self::Input::AddEnvVar => { Self::Input::AddEnvVar(name) => {
println!("Add env var"); let prof = self.profile.as_mut().unwrap();
if !prof.environment.contains_key(&name) {
prof.environment.insert(name.clone(), "".to_string());
self.env_rows.guard().push_back(EnvVarModelInit {
name,
value: "".to_string(),
});
}
} }
} }
} }
@ -235,19 +256,45 @@ impl SimpleComponent for ProfileEditor {
root: &Self::Root, root: &Self::Root,
sender: ComponentSender<Self>, sender: ComponentSender<Self>,
) -> ComponentParts<Self> { ) -> ComponentParts<Self> {
let add_env_var_btn = gtk::Button::builder() let add_env_popover = gtk::Popover::builder().build();
let add_env_popover_box = gtk::Box::builder()
.orientation(gtk::Orientation::Horizontal)
.css_classes(["linked"])
.build();
let add_env_name_entry = gtk::Entry::builder()
.placeholder_text("Env Var Name...")
.build();
let add_env_btn = gtk::Button::builder()
.css_classes(["suggested-action"])
.icon_name("list-add-symbolic")
.tooltip_text("Add Env Var")
.build();
{
let aeb_sender = sender.clone();
let entry = add_env_name_entry.clone();
let popover = add_env_popover.clone();
add_env_btn.connect_clicked(move |_| {
let name_gstr = entry.text();
let name = name_gstr.trim();
if !name.is_empty() {
popover.popdown();
entry.set_text("");
aeb_sender.input(Self::Input::AddEnvVar(name.to_string()));
}
});
}
add_env_popover_box.append(&add_env_name_entry);
add_env_popover_box.append(&add_env_btn);
add_env_popover.set_child(Some(&add_env_popover_box));
let add_env_var_btn = gtk::MenuButton::builder()
.icon_name(icon_name::PLUS) .icon_name(icon_name::PLUS)
.tooltip_text("Add Environment Variable") .tooltip_text("Add Environment Variable")
.css_classes(["flat"]) .css_classes(["flat"])
.popover(&add_env_popover)
.valign(gtk::Align::Start) .valign(gtk::Align::Start)
.halign(gtk::Align::End) .halign(gtk::Align::End)
.build(); .build();
{
let btn_sender = sender.clone();
add_env_var_btn.connect_clicked(move |_| {
btn_sender.input(Self::Input::AddEnvVar);
});
}
let mut model = Self { let mut model = Self {
profile: None, profile: None,