From 17f356dde797db4432f316c1e57f16358e2c1e21 Mon Sep 17 00:00:00 2001 From: yggverse Date: Thu, 21 Nov 2024 12:03:09 +0200 Subject: [PATCH] add auth widget actions --- .../window/tab/item/identity/gemini/widget.rs | 25 +++++++++++- .../tab/item/identity/gemini/widget/action.rs | 20 ++++++++++ .../identity/gemini/widget/action/update.rs | 33 ++++++++++++++++ .../tab/item/identity/gemini/widget/form.rs | 22 +++++++++-- .../item/identity/gemini/widget/form/name.rs | 39 ++++++++++++++----- 5 files changed, 123 insertions(+), 16 deletions(-) create mode 100644 src/app/browser/window/tab/item/identity/gemini/widget/action.rs create mode 100644 src/app/browser/window/tab/item/identity/gemini/widget/action/update.rs diff --git a/src/app/browser/window/tab/item/identity/gemini/widget.rs b/src/app/browser/window/tab/item/identity/gemini/widget.rs index 660a8ab8..5657a6f7 100644 --- a/src/app/browser/window/tab/item/identity/gemini/widget.rs +++ b/src/app/browser/window/tab/item/identity/gemini/widget.rs @@ -1,4 +1,7 @@ +mod action; pub mod form; + +use action::Action; use form::{list::item::value::Value, Form}; use adw::{ @@ -20,6 +23,7 @@ const RESPONSE_CANCEL: (&str, &str) = ("cancel", "Cancel"); // Select options pub struct Widget { + // pub action: Rc, pub form: Rc
, pub gobject: AlertDialog, } @@ -29,8 +33,11 @@ impl Widget { /// Create new `Self` pub fn new() -> Self { + // Init actions + let action = Rc::new(Action::new()); + // Init child container - let form = Rc::new(Form::new()); + let form = Rc::new(Form::new(action.clone())); // Init main `GObject` let gobject = AlertDialog::builder() @@ -55,8 +62,22 @@ impl Widget { gobject.set_response_appearance(RESPONSE_APPLY.0, ResponseAppearance::Suggested); gobject.set_response_appearance(RESPONSE_CANCEL.0, ResponseAppearance::Destructive); + // Init events + action.update.connect_activate({ + let form = form.clone(); + let gobject = gobject.clone(); + move || { + // Deactivate apply button if the form values could not be processed + gobject.set_response_enabled(RESPONSE_APPLY.0, form.is_valid()); + } + }); + // Return new activated `Self` - Self { form, gobject } + Self { + // action, + form, + gobject, + } } // Actions diff --git a/src/app/browser/window/tab/item/identity/gemini/widget/action.rs b/src/app/browser/window/tab/item/identity/gemini/widget/action.rs new file mode 100644 index 00000000..09d06c30 --- /dev/null +++ b/src/app/browser/window/tab/item/identity/gemini/widget/action.rs @@ -0,0 +1,20 @@ +mod update; +use update::Update; + +use std::rc::Rc; + +/// [SimpleActionGroup](https://docs.gtk.org/gio/class.SimpleActionGroup.html) wrapper +pub struct Action { + pub update: Rc, +} + +impl Action { + // Constructors + + /// Create new `Self` + pub fn new() -> Self { + Self { + update: Rc::new(Update::new()), + } + } +} diff --git a/src/app/browser/window/tab/item/identity/gemini/widget/action/update.rs b/src/app/browser/window/tab/item/identity/gemini/widget/action/update.rs new file mode 100644 index 00000000..d0ad0355 --- /dev/null +++ b/src/app/browser/window/tab/item/identity/gemini/widget/action/update.rs @@ -0,0 +1,33 @@ +use gtk::{gio::SimpleAction, glib::uuid_string_random, prelude::ActionExt}; + +/// [SimpleAction](https://docs.gtk.org/gio/class.SimpleAction.html) wrapper for `Update` action +pub struct Update { + pub gobject: SimpleAction, +} + +impl Update { + // Constructors + + /// Create new `Self` + pub fn new() -> Self { + Self { + gobject: SimpleAction::new(&uuid_string_random(), None), + } + } + + // Actions + + /// Emit [activate](https://docs.gtk.org/gio/signal.SimpleAction.activate.html) signal + /// with formatted for this action [Variant](https://docs.gtk.org/glib/struct.Variant.html) value + pub fn activate(&self) { + self.gobject.activate(None); + } + + // Events + + /// Define callback function for + /// [SimpleAction::activate](https://docs.gtk.org/gio/signal.SimpleAction.activate.html) signal + pub fn connect_activate(&self, callback: impl Fn() + 'static) { + self.gobject.connect_activate(move |_, _| callback()); + } +} diff --git a/src/app/browser/window/tab/item/identity/gemini/widget/form.rs b/src/app/browser/window/tab/item/identity/gemini/widget/form.rs index 987bb903..fe6bcccc 100644 --- a/src/app/browser/window/tab/item/identity/gemini/widget/form.rs +++ b/src/app/browser/window/tab/item/identity/gemini/widget/form.rs @@ -4,6 +4,7 @@ mod name; use list::{item::value::Value, List}; use name::Name; +use super::Action; use gtk::{ prelude::{BoxExt, WidgetExt}, Box, Orientation, @@ -11,19 +12,20 @@ use gtk::{ use std::rc::Rc; pub struct Form { - pub gobject: Box, + // pub action: Rc, pub list: Rc, pub name: Rc, + pub gobject: Box, } impl Form { // Constructors /// Create new `Self` - pub fn new() -> Self { + pub fn new(action: Rc) -> Self { // Init components let list = Rc::new(List::new()); - let name = Rc::new(Name::new()); + let name = Rc::new(Name::new(action.clone())); // Init main container let gobject = Box::builder().orientation(Orientation::Vertical).build(); @@ -34,19 +36,31 @@ impl Form { // Connect events list.on_select({ let name = name.clone(); + let update = action.update.clone(); move |key| { + // Change name entry visibility name.gobject.set_visible(match key { Value::GENERATE_NEW_AUTH => true, _ => false, - }) + }); + + // Update widget + update.activate(); } }); // Return activated `Self` Self { + // action, gobject, list, name, } } + + // Actions + + pub fn is_valid(&self) -> bool { + self.name.is_valid() // @TODO + } } diff --git a/src/app/browser/window/tab/item/identity/gemini/widget/form/name.rs b/src/app/browser/window/tab/item/identity/gemini/widget/form/name.rs index d7ba5f77..0ed2dcfa 100644 --- a/src/app/browser/window/tab/item/identity/gemini/widget/form/name.rs +++ b/src/app/browser/window/tab/item/identity/gemini/widget/form/name.rs @@ -1,7 +1,15 @@ -use gtk::{glib::GString, prelude::EditableExt, Entry}; +use super::Action; +use gtk::{ + glib::GString, + prelude::{EditableExt, EntryExt}, + Entry, +}; +use std::rc::Rc; -const PLACEHOLDER_TEXT: &str = "Identity name (optional)"; +const PLACEHOLDER_TEXT: &str = "Identity name (required)"; const MARGIN: i32 = 8; +const MIN_LENGTH: u16 = 1; +const MAX_LENGTH: u16 = 36; pub struct Name { pub gobject: Entry, @@ -11,14 +19,25 @@ impl Name { // Constructors /// Create new `Self` - pub fn new() -> Self { - Self { - gobject: Entry::builder() - .max_length(36) // @TODO use profile const - .placeholder_text(PLACEHOLDER_TEXT) - .margin_top(MARGIN) - .build(), - } + pub fn new(action: Rc) -> Self { + // Init `GObject` + let gobject = Entry::builder() + .max_length(MAX_LENGTH as i32) + .placeholder_text(PLACEHOLDER_TEXT) + .margin_top(MARGIN) + .build(); + + // Init events + gobject.connect_changed(move |_| action.update.activate()); + + // Return activated `Self` + Self { gobject } + } + + // Actions + + pub fn is_valid(&self) -> bool { + self.gobject.text_length() >= MIN_LENGTH && self.gobject.text_length() <= MAX_LENGTH } // Getters