diff --git a/Cargo.lock b/Cargo.lock index 3d0c94b..2f9a9a5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -282,6 +282,7 @@ dependencies = [ "quick-xml", "secret-service", "surf", + "unicase", "url", "zbar-rust", ] diff --git a/Cargo.toml b/Cargo.toml index d100ca8..1ef5044 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -21,6 +21,7 @@ url = "2.1" zbar-rust = "0.0" secret-service = "1.1" once_cell = "1.5" +unicase = "2.6" [dependencies.gtk] git = "https://github.com/gtk-rs/gtk4-rs" diff --git a/data/resources.gresource.xml b/data/resources.gresource.xml index cc58d81..9df56d2 100644 --- a/data/resources.gresource.xml +++ b/data/resources.gresource.xml @@ -9,7 +9,9 @@ resources/ui/account_row.ui + resources/ui/provider_page.ui resources/ui/provider_row.ui + resources/ui/providers_all.ui providers_list.ui @@ -18,8 +20,6 @@ window.ui preferences.ui preferences_password_page.ui - resources/ui/preferences_provider_page.ui - resources/ui/preferences_providers_page.ui resources/ui/provider_image.ui diff --git a/data/resources/ui/account_add.ui b/data/resources/ui/account_add.ui index e8a1833..3c888c3 100644 --- a/data/resources/ui/account_add.ui +++ b/data/resources/ui/account_add.ui @@ -50,8 +50,6 @@ 12 12 True - 400 - 400 vertical diff --git a/data/resources/ui/preferences_providers_page.ui b/data/resources/ui/preferences_providers_page.ui deleted file mode 100644 index 9442d95..0000000 --- a/data/resources/ui/preferences_providers_page.ui +++ /dev/null @@ -1,26 +0,0 @@ - - - - package-x-generic-symbolic - Providers - - - - - True - True - - - true - True - - - - - - - - - diff --git a/data/resources/ui/preferences_provider_page.ui b/data/resources/ui/provider_page.ui similarity index 84% rename from data/resources/ui/preferences_provider_page.ui rename to data/resources/ui/provider_page.ui index f030a16..49229c1 100644 --- a/data/resources/ui/preferences_provider_page.ui +++ b/data/resources/ui/provider_page.ui @@ -9,10 +9,11 @@ + - + diff --git a/data/resources/ui/providers_all.ui b/data/resources/ui/providers_all.ui new file mode 100644 index 0000000..1315546 --- /dev/null +++ b/data/resources/ui/providers_all.ui @@ -0,0 +1,79 @@ + + + + diff --git a/data/resources/ui/window.ui.in b/data/resources/ui/window.ui.in index 2180ee8..c15304d 100644 --- a/data/resources/ui/window.ui.in +++ b/data/resources/ui/window.ui.in @@ -1,6 +1,10 @@ + + _Providers + app.providers + _Lock the application app.lock diff --git a/src/application.rs b/src/application.rs index fc91814..af109b1 100644 --- a/src/application.rs +++ b/src/application.rs @@ -1,7 +1,7 @@ use crate::config; use crate::helpers::Keyring; use crate::models::{Account, Provider, ProvidersModel}; -use crate::widgets::{AccountAddDialog, PreferencesWindow, View, Window}; +use crate::widgets::{AccountAddDialog, PreferencesWindow, ProvidersDialog, View, Window}; use gio::prelude::*; use glib::subclass::prelude::*; use glib::{subclass, WeakRef}; @@ -149,6 +149,17 @@ impl ApplicationImpl for ApplicationPrivate { }) ); + action!( + app, + "providers", + clone!(@strong app, @weak self.model as model => move |_, _| { + let window = app.get_active_window().unwrap(); + let providers = ProvidersDialog::new(model); + providers.set_transient_for(Some(&window)); + providers.show(); + }) + ); + action!( app, "lock", @@ -159,10 +170,12 @@ impl ApplicationImpl for ApplicationPrivate { app.bind_property("can-be-locked", &get_action!(app, @lock), "enabled") .flags(glib::BindingFlags::DEFAULT | glib::BindingFlags::SYNC_CREATE) .build(); - app.bind_property("locked", &get_action!(app, @preferences), "enabled") .flags(glib::BindingFlags::INVERT_BOOLEAN | glib::BindingFlags::SYNC_CREATE) .build(); + app.bind_property("locked", &get_action!(app, @providers), "enabled") + .flags(glib::BindingFlags::INVERT_BOOLEAN | glib::BindingFlags::SYNC_CREATE) + .build(); } fn activate(&self, app: &Self::Type) { @@ -181,10 +194,12 @@ impl ApplicationImpl for ApplicationPrivate { app.set_resource_base_path(Some("/com/belmoussaoui/Authenticator")); app.set_accels_for_action("app.quit", &["q"]); app.set_accels_for_action("app.lock", &["l"]); + app.set_accels_for_action("app.providers", &["p"]); app.set_accels_for_action("app.preferences", &["comma"]); app.set_accels_for_action("win.show-help-overlay", &["question"]); app.set_accels_for_action("win.search", &["f"]); app.set_accels_for_action("win.add-account", &["n"]); + app.set_locked(has_set_password); app.set_can_be_locked(has_set_password); let receiver = self.receiver.borrow_mut().take().unwrap(); diff --git a/src/meson.build b/src/meson.build index 64478d0..61354b9 100644 --- a/src/meson.build +++ b/src/meson.build @@ -44,6 +44,7 @@ sources = files( 'helpers/mod.rs', 'helpers/qrcode.rs', 'models/account.rs', + 'models/algorithm.rs', 'models/database.rs', 'models/favicon.rs', 'models/mod.rs', @@ -52,14 +53,14 @@ sources = files( 'widgets/accounts/add.rs', 'widgets/accounts/mod.rs', 'widgets/accounts/row.rs', - 'widgets/providers/list.rs', - 'widgets/providers/mod.rs', - 'widgets/providers/row.rs', 'widgets/preferences/mod.rs', 'widgets/preferences/password_page.rs', - 'widgets/preferences/provider_page.rs', - 'widgets/preferences/providers_page.rs', 'widgets/preferences/window.rs', + 'widgets/providers/all.rs', + 'widgets/providers/list.rs', + 'widgets/providers/mod.rs', + 'widgets/providers/page.rs', + 'widgets/providers/row.rs', 'widgets/mod.rs', 'widgets/window.rs', 'application.rs', diff --git a/src/models/algorithm.rs b/src/models/algorithm.rs index 7c17f06..81d8621 100644 --- a/src/models/algorithm.rs +++ b/src/models/algorithm.rs @@ -10,7 +10,6 @@ pub enum Algorithm { OTP = 0, #[genum(name = "HOTP")] HOTP = 1, - #[genum(name = "Steam")] Steam = 2, } diff --git a/src/models/provider.rs b/src/models/provider.rs index a50aa85..f867320 100644 --- a/src/models/provider.rs +++ b/src/models/provider.rs @@ -12,6 +12,7 @@ use gtk::FilterListModelExt; use std::cell::{Cell, RefCell}; use std::str::FromStr; use std::string::ToString; +use unicase::UniCase; use url::Url; #[derive(Insertable)] @@ -250,7 +251,7 @@ impl Provider { let provider1 = obj1.downcast_ref::().unwrap(); let provider2 = obj2.downcast_ref::().unwrap(); - provider1.name().cmp(&provider2.name()) + UniCase::new(provider1.name()).cmp(&UniCase::new(provider2.name())) } pub fn load() -> Result> { diff --git a/src/widgets/mod.rs b/src/widgets/mod.rs index 82cb76d..bd9f4ed 100644 --- a/src/widgets/mod.rs +++ b/src/widgets/mod.rs @@ -5,5 +5,5 @@ mod window; pub use self::accounts::AccountAddDialog; pub use self::preferences::PreferencesWindow; -pub use self::providers::ProvidersList; +pub use self::providers::{ProvidersDialog, ProvidersList}; pub use self::window::{View, Window}; diff --git a/src/widgets/preferences/mod.rs b/src/widgets/preferences/mod.rs index f55f1e6..c99fef9 100644 --- a/src/widgets/preferences/mod.rs +++ b/src/widgets/preferences/mod.rs @@ -1,6 +1,4 @@ mod password_page; -mod provider_page; -mod providers_page; mod window; pub use window::PreferencesWindow; diff --git a/src/widgets/preferences/provider_page.rs b/src/widgets/preferences/provider_page.rs deleted file mode 100644 index c192d1f..0000000 --- a/src/widgets/preferences/provider_page.rs +++ /dev/null @@ -1,63 +0,0 @@ -use crate::models::{Algorithm, Provider}; -use glib::translate::ToGlib; -use gtk::prelude::*; -use libhandy::{ComboRowExt, EnumListModelExt}; -use std::rc::Rc; - -pub struct ProviderPage { - pub widget: gtk::Box, - builder: gtk::Builder, - algorithms_model: libhandy::EnumListModel, -} - -impl ProviderPage { - pub fn new() -> Rc { - let builder = gtk::Builder::from_resource( - "/com/belmoussaoui/Authenticator/preferences_provider_page.ui", - ); - get_widget!(builder, gtk::Box, provider_page); - let algorithms_model = libhandy::EnumListModel::new(Algorithm::static_type()); - - let page = Rc::new(Self { - widget: provider_page, - builder, - algorithms_model, - }); - page.init(); - page - } - - pub fn set_provider(&self, provider: Provider) { - get_widget!(self.builder, gtk::Entry, @name_entry).set_text(&provider.name()); - get_widget!(self.builder, gtk::SpinButton, @period_spinbutton) - .set_value(provider.period() as f64); - - if let Some(ref website) = provider.website() { - get_widget!(self.builder, gtk::Entry, @provider_website_entry).set_text(website); - } - - get_widget!(self.builder, gtk::Stack, @image_stack).set_visible_child_name("loading"); - get_widget!(self.builder, gtk::Spinner, @spinner).start(); - - get_widget!(self.builder, libhandy::ComboRow, algorithm_comborow); - algorithm_comborow.set_selected( - self.algorithms_model - .find_position(provider.algorithm().to_glib()), - ); - - /*let sender = self.sender.clone(); - spawn!(async move { - if let Ok(file) = p.favicon().await { - send!(sender, AddAccountAction::SetIcon(file)); - } - });*/ - - get_widget!(self.builder, gtk::Label, title); - title.set_text(&format!("Editing provider: {}", provider.name())); - } - - fn init(&self) { - get_widget!(self.builder, libhandy::ComboRow, algorithm_comborow); - algorithm_comborow.set_model(Some(&self.algorithms_model)); - } -} diff --git a/src/widgets/preferences/providers_page.rs b/src/widgets/preferences/providers_page.rs deleted file mode 100644 index 74b52e4..0000000 --- a/src/widgets/preferences/providers_page.rs +++ /dev/null @@ -1,67 +0,0 @@ -use crate::models::{Provider, ProvidersModel}; -use gio::ListModelExt; -use gtk::prelude::*; -use std::rc::Rc; - -pub struct ProvidersPage { - pub widget: libhandy::PreferencesPage, - builder: gtk::Builder, - model: Rc, -} - -impl ProvidersPage { - pub fn new(model: Rc) -> Rc { - let builder = gtk::Builder::from_resource( - "/com/belmoussaoui/Authenticator/preferences_providers_page.ui", - ); - get_widget!(builder, libhandy::PreferencesPage, providers_page); - - let page = Rc::new(Self { - widget: providers_page, - builder, - model, - }); - page.init(); - page - } - - fn init(&self) { - get_widget!(self.builder, gtk::ListView, providers_list); - - let factory = gtk::SignalListItemFactory::new(); - factory.connect_bind(|_, list_item| { - let item = list_item.get_item().unwrap(); - let provider = item.downcast_ref::().unwrap(); - let row = Row::new(provider); - list_item.set_child(Some(&row.widget)); - }); - providers_list.set_factory(Some(&factory)); - - let selection_model = gtk::NoSelection::new(Some(&self.model.model)); - providers_list.set_model(Some(&selection_model)); - - providers_list.connect_activate(move |listview, pos| { - let model = listview.get_model().unwrap(); - let provider = model - .get_object(pos) - .unwrap() - .downcast::() - .unwrap(); - // send!(sender, PreferencesAction::EditProvider(provider)); - }); - } -} - -pub struct Row<'a> { - pub widget: libhandy::ActionRow, - provider: &'a Provider, -} - -impl<'a> Row<'a> { - pub fn new(provider: &'a Provider) -> Self { - let widget = libhandy::ActionRowBuilder::new() - .title(&provider.name()) - .build(); - Self { widget, provider } - } -} diff --git a/src/widgets/providers/all.rs b/src/widgets/providers/all.rs new file mode 100644 index 0000000..567e8d9 --- /dev/null +++ b/src/widgets/providers/all.rs @@ -0,0 +1,311 @@ +use super::{ProviderPage, ProviderPageMode}; +use crate::models::{Provider, ProvidersModel}; +use gio::prelude::*; +use gio::subclass::ObjectSubclass; +use gio::ListModelExt; +use glib::subclass::prelude::*; +use glib::{glib_object_subclass, glib_wrapper}; +use gtk::{prelude::*, CompositeTemplate}; +use libhandy::{LeafletExt, LeafletPageExt}; +use row::ProviderActionRow; +use std::rc::Rc; + +mod imp { + use super::*; + use glib::subclass; + use gtk::subclass::prelude::*; + use libhandy::subclass::window::WindowImpl as HdyWindowImpl; + + #[derive(CompositeTemplate)] + pub struct ProvidersDialog { + #[template_child(id = "providers_list")] + pub providers_list: TemplateChild, + #[template_child(id = "deck")] + pub deck: TemplateChild, + #[template_child(id = "search_entry")] + pub search_entry: TemplateChild, + #[template_child(id = "search_bar")] + pub search_bar: TemplateChild, + #[template_child(id = "search_btn")] + pub search_btn: TemplateChild, + pub page: ProviderPage, + pub actions: gio::SimpleActionGroup, + pub filter_model: gtk::FilterListModel, + } + + impl ObjectSubclass for ProvidersDialog { + const NAME: &'static str = "ProvidersDialog"; + type Type = super::ProvidersDialog; + type ParentType = libhandy::Window; + type Instance = subclass::simple::InstanceStruct; + type Class = subclass::simple::ClassStruct; + + glib_object_subclass!(); + + fn new() -> Self { + let filter_model = + gtk::FilterListModel::new(gtk::NONE_FILTER_LIST_MODEL, gtk::NONE_FILTER); + Self { + deck: TemplateChild::default(), + providers_list: TemplateChild::default(), + search_entry: TemplateChild::default(), + search_bar: TemplateChild::default(), + search_btn: TemplateChild::default(), + page: ProviderPage::new(), + actions: gio::SimpleActionGroup::new(), + filter_model, + } + } + + fn class_init(klass: &mut Self::Class) { + klass.set_template_from_resource("/com/belmoussaoui/Authenticator/providers_all.ui"); + Self::bind_template_children(klass); + } + } + + impl ObjectImpl for ProvidersDialog { + fn constructed(&self, obj: &Self::Type) { + obj.init_template(); + self.parent_constructed(obj); + } + } + impl WidgetImpl for ProvidersDialog {} + impl WindowImpl for ProvidersDialog {} + impl HdyWindowImpl for ProvidersDialog {} +} +glib_wrapper! { + pub struct ProvidersDialog(ObjectSubclass) @extends gtk::Widget, gtk::Window, libhandy::Window; +} + +impl ProvidersDialog { + pub fn new(model: Rc) -> Self { + let dialog = glib::Object::new(Self::static_type(), &[]) + .expect("Failed to create ProvidersDialog") + .downcast::() + .expect("Created object is of wrong type"); + + dialog.setup_widgets(model); + dialog.setup_actions(); + dialog + } + + fn setup_widgets(&self, model: Rc) { + let self_ = imp::ProvidersDialog::from_instance(self); + + self_.filter_model.set_model(Some(&model.model)); + self_ + .search_bar + .get() + .bind_property("search-mode-enabled", &self_.search_btn.get(), "active") + .flags(glib::BindingFlags::BIDIRECTIONAL | glib::BindingFlags::SYNC_CREATE) + .build(); + + self_.search_entry.get().connect_search_changed( + clone!(@weak self as dialog => move |entry| { + let text = entry.get_text().unwrap().to_string(); + dialog.search(text); + }), + ); + + let search_btn = self_.search_btn.get(); + + self_ + .search_btn + .get() + .bind_property("active", &self_.search_bar.get(), "search-mode-enabled") + .flags(glib::BindingFlags::BIDIRECTIONAL | glib::BindingFlags::SYNC_CREATE) + .build(); + + let factory = gtk::SignalListItemFactory::new(); + factory.connect_bind(|_, list_item| { + let item = list_item.get_item().unwrap(); + let provider = item.clone().downcast::().unwrap(); + let row = ProviderActionRow::new(provider); + list_item.set_child(Some(&row)); + }); + self_.providers_list.get().set_factory(Some(&factory)); + + let selection_model = gtk::NoSelection::new(Some(&self_.filter_model)); + self_.providers_list.get().set_model(Some(&selection_model)); + + self_.providers_list.get().connect_activate( + clone!(@weak self as dialog => move |listview, pos| { + let model = listview.get_model().unwrap(); + let provider = model + .get_object(pos) + .unwrap() + .downcast::() + .unwrap(); + dialog.edit_provider(provider); + }), + ); + + let deck_page = self_.deck.get().add(&self_.page).unwrap(); + deck_page.set_name("provider"); + } + + fn setup_actions(&self) { + let self_ = imp::ProvidersDialog::from_instance(self); + + let deck = self_.deck.get(); + let search_bar = self_.search_bar.get(); + action!( + self_.actions, + "search", + clone!(@weak search_bar => move |_,_| { + search_bar.set_search_mode(!search_bar.get_search_mode()); + }) + ); + action!( + self_.actions, + "back", + clone!(@weak deck => move |_ , _| { + deck.set_visible_child_name("providers"); + }) + ); + + action!( + self_.actions, + "add", + clone!(@weak self as dialog => move |_, _| { + dialog.add_provider(); + }) + ); + + self.insert_action_group("providers", Some(&self_.actions)); + } + + fn search(&self, text: String) { + let self_ = imp::ProvidersDialog::from_instance(self); + + let providers_filter = gtk::CustomFilter::new(Some(Box::new(move |object| { + let provider = object.downcast_ref::().unwrap(); + provider + .name() + .to_ascii_lowercase() + .contains(&text.to_ascii_lowercase()) + }))); + self_.filter_model.set_filter(Some(&providers_filter)); + } + + fn add_provider(&self) { + let self_ = imp::ProvidersDialog::from_instance(self); + self_.deck.get().set_visible_child_name("provider"); + self_.page.set_mode(ProviderPageMode::Create); + } + + fn edit_provider(&self, provider: Provider) { + let self_ = imp::ProvidersDialog::from_instance(self); + self_.deck.get().set_visible_child_name("provider"); + self_.page.set_provider(provider); + self_.page.set_mode(ProviderPageMode::Edit); + } +} + +mod row { + use super::*; + mod imp { + use super::*; + use glib::subclass; + use gtk::subclass::prelude::*; + use std::cell::RefCell; + + static PROPERTIES: [subclass::Property; 1] = [subclass::Property("provider", |name| { + glib::ParamSpec::object( + name, + "Provider", + "The Provider", + Provider::static_type(), + glib::ParamFlags::READWRITE | glib::ParamFlags::CONSTRUCT_ONLY, + ) + })]; + + pub struct ProviderActionRow { + pub provider: RefCell>, + pub actions: gio::SimpleActionGroup, + } + + impl ObjectSubclass for ProviderActionRow { + const NAME: &'static str = "ProviderActionRow"; + type Type = super::ProviderActionRow; + type ParentType = libhandy::ActionRow; + type Instance = subclass::simple::InstanceStruct; + type Class = subclass::simple::ClassStruct; + + glib_object_subclass!(); + + fn new() -> Self { + let actions = gio::SimpleActionGroup::new(); + + Self { + actions, + provider: RefCell::new(None), + } + } + + fn class_init(klass: &mut Self::Class) { + klass.install_properties(&PROPERTIES); + } + } + + impl ObjectImpl for ProviderActionRow { + fn set_property(&self, _obj: &Self::Type, id: usize, value: &glib::Value) { + let prop = &PROPERTIES[id]; + + match *prop { + subclass::Property("provider", ..) => { + let provider = value + .get() + .expect("type conformity checked by `Object::set_property`"); + self.provider.replace(provider); + } + _ => unimplemented!(), + } + } + + fn get_property(&self, _obj: &Self::Type, id: usize) -> glib::Value { + let prop = &PROPERTIES[id]; + match *prop { + subclass::Property("provider", ..) => self.provider.borrow().to_value(), + _ => unimplemented!(), + } + } + + fn constructed(&self, obj: &Self::Type) { + obj.init_template(); + obj.setup_widgets(); + self.parent_constructed(obj); + } + } + impl WidgetImpl for ProviderActionRow {} + impl ListBoxRowImpl for ProviderActionRow {} + impl libhandy::subclass::action_row::ActionRowImpl for ProviderActionRow {} + } + + glib_wrapper! { + pub struct ProviderActionRow(ObjectSubclass) @extends gtk::Widget, gtk::ListBoxRow, libhandy::ActionRow; + } + + impl ProviderActionRow { + pub fn new(provider: Provider) -> Self { + glib::Object::new(Self::static_type(), &[("provider", &provider)]) + .expect("Failed to create ProviderActionRow") + .downcast::() + .expect("Created object is of wrong type") + } + + fn provider(&self) -> Provider { + let provider = self.get_property("provider").unwrap(); + provider.get::().unwrap().unwrap() + } + + fn setup_widgets(&self) { + let self_ = imp::ProviderActionRow::from_instance(self); + + self.provider() + .bind_property("name", self, "title") + .flags(glib::BindingFlags::DEFAULT | glib::BindingFlags::SYNC_CREATE) + .build(); + } + } +} diff --git a/src/widgets/providers/mod.rs b/src/widgets/providers/mod.rs index 094e603..4bfc695 100644 --- a/src/widgets/providers/mod.rs +++ b/src/widgets/providers/mod.rs @@ -1,4 +1,8 @@ +mod all; mod list; +mod page; mod row; +pub use self::all::ProvidersDialog; pub use self::list::ProvidersList; +pub use self::page::{ProviderPage, ProviderPageMode}; pub use self::row::ProviderRow; diff --git a/src/widgets/providers/page.rs b/src/widgets/providers/page.rs new file mode 100644 index 0000000..97f4efe --- /dev/null +++ b/src/widgets/providers/page.rs @@ -0,0 +1,156 @@ +use crate::models::{Algorithm, Provider}; +use gio::subclass::ObjectSubclass; +use glib::subclass::prelude::*; +use glib::translate::ToGlib; +use glib::{glib_object_subclass, glib_wrapper}; +use gtk::{prelude::*, CompositeTemplate}; +use libhandy::{ComboRowExt, EnumListModelExt}; + +pub enum ProviderPageMode { + Create, + Edit, +} + +mod imp { + use super::*; + use glib::subclass; + use gtk::subclass::prelude::*; + + #[derive(Debug, CompositeTemplate)] + pub struct ProviderPage { + #[template_child(id = "name_entry")] + pub name_entry: TemplateChild, + #[template_child(id = "period_spinbutton")] + pub period_spinbutton: TemplateChild, + #[template_child(id = "provider_website_entry")] + pub provider_website_entry: TemplateChild, + #[template_child(id = "provider_help_entry")] + pub provider_help_entry: TemplateChild, + #[template_child(id = "image_stack")] + pub image_stack: TemplateChild, + #[template_child(id = "spinner")] + pub spinner: TemplateChild, + #[template_child(id = "algorithm_comborow")] + pub algorithm_comborow: TemplateChild, + #[template_child(id = "title")] + pub title: TemplateChild, + pub algorithms_model: libhandy::EnumListModel, + } + + impl ObjectSubclass for ProviderPage { + const NAME: &'static str = "ProviderPage"; + type Type = super::ProviderPage; + type ParentType = gtk::Box; + type Instance = subclass::simple::InstanceStruct; + type Class = subclass::simple::ClassStruct; + + glib_object_subclass!(); + + fn new() -> Self { + let algorithms_model = libhandy::EnumListModel::new(Algorithm::static_type()); + + Self { + name_entry: TemplateChild::default(), + period_spinbutton: TemplateChild::default(), + provider_website_entry: TemplateChild::default(), + provider_help_entry: TemplateChild::default(), + image_stack: TemplateChild::default(), + spinner: TemplateChild::default(), + algorithm_comborow: TemplateChild::default(), + title: TemplateChild::default(), + algorithms_model, + } + } + + fn class_init(klass: &mut Self::Class) { + klass.set_template_from_resource("/com/belmoussaoui/Authenticator/provider_page.ui"); + Self::bind_template_children(klass); + } + } + + impl ObjectImpl for ProviderPage { + fn constructed(&self, obj: &Self::Type) { + obj.init_template(); + obj.setup_widgets(); + self.parent_constructed(obj); + } + } + impl WidgetImpl for ProviderPage {} + impl BoxImpl for ProviderPage {} +} + +glib_wrapper! { + pub struct ProviderPage(ObjectSubclass) @extends gtk::Widget, gtk::Box; +} +impl ProviderPage { + pub fn new() -> Self { + glib::Object::new(Self::static_type(), &[]) + .expect("Failed to create ProviderPage") + .downcast::() + .expect("Created object is of wrong type") + } + + pub fn set_provider(&self, provider: Provider) { + let self_ = imp::ProviderPage::from_instance(self); + self_.name_entry.get().set_text(&provider.name()); + self_ + .period_spinbutton + .get() + .set_value(provider.period() as f64); + + if let Some(ref website) = provider.website() { + self_.provider_website_entry.get().set_text(website); + } + + if let Some(ref website) = provider.help_url() { + self_.provider_help_entry.get().set_text(website); + } + + self_.image_stack.get().set_visible_child_name("loading"); + self_.spinner.get().start(); + + self_.algorithm_comborow.get().set_selected( + self_ + .algorithms_model + .find_position(provider.algorithm().to_glib()), + ); + + /*let sender = self.sender.clone(); + spawn!(async move { + if let Ok(file) = p.favicon().await { + send!(sender, AddAccountAction::SetIcon(file)); + } + });*/ + + self_ + .title + .get() + .set_text(&format!("Editing provider: {}", provider.name())); + } + + fn setup_widgets(&self) { + let self_ = imp::ProviderPage::from_instance(self); + self_ + .algorithm_comborow + .get() + .set_model(Some(&self_.algorithms_model)); + } + + pub fn set_mode(&self, mode: ProviderPageMode) { + let self_ = imp::ProviderPage::from_instance(self); + match mode { + ProviderPageMode::Create => { + self_.title.get().set_label("New Provider"); + self_.name_entry.get().set_text(""); + self_.period_spinbutton.get().set_value(30_f64); + self_.provider_website_entry.get().set_text(""); + self_.provider_help_entry.get().set_text(""); + + self_.image_stack.get().set_visible_child_name("image"); + self_.spinner.get().stop(); + self_.algorithm_comborow.get().set_selected(0); + } + ProviderPageMode::Edit => {} + } + } +} diff --git a/src/widgets/window.rs b/src/widgets/window.rs index e46eadd..595a28b 100644 --- a/src/widgets/window.rs +++ b/src/widgets/window.rs @@ -175,7 +175,6 @@ impl Window { "gtk-application-prefer-dark-theme", gio::SettingsBindFlags::DEFAULT, ); - self.set_default_size(360, 600); } fn setup_actions(&self, app: &Application, sender: Sender) {