diff --git a/src/ui/factories/entry_row_factory.rs b/src/ui/factories/env_var_row_factory.rs similarity index 58% rename from src/ui/factories/entry_row_factory.rs rename to src/ui/factories/env_var_row_factory.rs index c41d5c3..62b4ae7 100644 --- a/src/ui/factories/entry_row_factory.rs +++ b/src/ui/factories/env_var_row_factory.rs @@ -4,33 +4,35 @@ use gtk::prelude::*; use relm4::prelude::*; #[derive(Debug)] -pub struct EntryModel { - name: String, +pub struct EnvVarModel { + pub name: String, value: String, } -pub struct EntryModelInit { +pub struct EnvVarModelInit { pub name: String, pub value: String, } #[derive(Debug)] -pub enum EntryModelMsg { +pub enum EnvVarModelMsg { Changed(String), + Delete, } #[derive(Debug)] -pub enum EntryModelOutMsg { +pub enum EnvVarModelOutMsg { Changed(String, String), + Delete(String), } #[relm4::factory(pub)] -impl FactoryComponent for EntryModel { - type Init = EntryModelInit; - type Input = EntryModelMsg; - type Output = EntryModelOutMsg; +impl FactoryComponent for EnvVarModel { + type Init = EnvVarModelInit; + type Input = EnvVarModelMsg; + type Output = EnvVarModelOutMsg; type CommandOutput = (); - type Widgets = EntryModelWidgets; + type Widgets = EnvVarModelWidgets; type ParentInput = ProfileEditorMsg; type ParentWidget = adw::PreferencesGroup; @@ -38,6 +40,16 @@ impl FactoryComponent for EntryModel { root = adw::EntryRow { set_title: &self.name, set_text: &self.value, + add_suffix: del_btn = >k::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| { sender.input_sender().emit(Self::Input::Changed(entry.text().to_string())); }, @@ -52,12 +64,16 @@ impl FactoryComponent for EntryModel { .output_sender() .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 { 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), }) } diff --git a/src/ui/factories/mod.rs b/src/ui/factories/mod.rs index 5fc67a9..bdf58f9 100644 --- a/src/ui/factories/mod.rs +++ b/src/ui/factories/mod.rs @@ -1,3 +1,3 @@ -pub mod entry_row_factory; +pub mod env_var_row_factory; pub mod switch_row_factory; pub mod path_row_factory; diff --git a/src/ui/profile_editor.rs b/src/ui/profile_editor.rs index a6c477a..76b553e 100644 --- a/src/ui/profile_editor.rs +++ b/src/ui/profile_editor.rs @@ -1,5 +1,5 @@ use super::factories::{ - entry_row_factory::{EntryModel, EntryModelInit}, + env_var_row_factory::{EnvVarModel, EnvVarModelInit}, path_row_factory::{PathModel, PathModelInit}, switch_row_factory::{SwitchModel, SwitchModelInit}, }; @@ -24,7 +24,7 @@ pub struct ProfileEditor { #[tracker::do_not_track] type_row: adw::ComboRow, #[tracker::do_not_track] - env_rows: FactoryVecDeque, + env_rows: FactoryVecDeque, #[tracker::do_not_track] switch_rows: FactoryVecDeque, #[tracker::do_not_track] @@ -34,12 +34,13 @@ pub struct ProfileEditor { #[derive(Debug)] pub enum ProfileEditorMsg { Present(Profile), - EntryChanged(String, String), + EnvVarChanged(String, String), + EnvVarDelete(String), TextChanged(String, String), PathChanged(String, Option), SwitchChanged(String, bool), ComboChanged(String, String), - AddEnvVar, + AddEnvVar(String), SaveProfile, } @@ -107,7 +108,7 @@ impl SimpleComponent for ProfileEditor { self.env_rows.guard().clear(); for (k, v) in p.environment.iter() { - self.env_rows.guard().push_back(EntryModelInit { + self.env_rows.guard().push_back(EnvVarModelInit { name: k.clone(), value: v.clone(), }); @@ -175,18 +176,31 @@ impl SimpleComponent for ProfileEditor { sender.output(ProfileEditorOutMsg::SaveProfile(prof.clone())); self.win.as_ref().unwrap().close(); } 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 - // + make entryfactory take a signal to send to parent - Self::Input::EntryChanged(name, value) => { + Self::Input::EnvVarChanged(name, value) => { self.profile .as_mut() .unwrap() .environment .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) => { let prof = self.profile.as_mut().unwrap(); match key.as_str() { @@ -224,8 +238,15 @@ impl SimpleComponent for ProfileEditor { _ => panic!("Unknown profile text key"), } } - Self::Input::AddEnvVar => { - println!("Add env var"); + Self::Input::AddEnvVar(name) => { + 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, sender: ComponentSender, ) -> ComponentParts { - 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) .tooltip_text("Add Environment Variable") .css_classes(["flat"]) + .popover(&add_env_popover) .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,