[geary/wip/714104-refine-account-dialog: 8/15] Ensure new accounts can be created by hitting Enter.



commit 5a98bfd1b1e856d97c7c77f9beb7ecf0f4e8e8b6
Author: Michael Gratton <mike vee net>
Date:   Sat Sep 8 20:53:38 2018 +1000

    Ensure new accounts can be created by hitting Enter.

 src/client/accounts/accounts-editor-add-pane.vala | 44 ++++++++++++++------
 src/client/components/components-validator.vala   | 50 +++++++++++++----------
 2 files changed, 60 insertions(+), 34 deletions(-)
---
diff --git a/src/client/accounts/accounts-editor-add-pane.vala 
b/src/client/accounts/accounts-editor-add-pane.vala
index 405d26ca..e763678c 100644
--- a/src/client/accounts/accounts-editor-add-pane.vala
+++ b/src/client/accounts/accounts-editor-add-pane.vala
@@ -68,6 +68,8 @@ internal class Accounts.EditorAddPane : Gtk.Grid, EditorPane {
     private LoginRow smtp_login = new LoginRow();
     private PasswordRow smtp_password = new PasswordRow();
 
+    private bool controls_valid = false;
+
 
     internal EditorAddPane(Editor editor, Geary.ServiceProvider provider) {
         this.editor = editor;
@@ -101,20 +103,28 @@ internal class Accounts.EditorAddPane : Gtk.Grid, EditorPane {
         this.details_list.add(this.real_name);
         this.details_list.add(this.email);
 
-        this.real_name.validator.notify["state"].connect(on_validated);
-        this.email.validator.notify["state"].connect(on_validated);
+        this.real_name.validator.state_changed.connect(on_validated);
+        this.real_name.value.activate.connect(on_activated);
+        this.email.validator.state_changed.connect(on_validated);
+        this.email.value.activate.connect(on_activated);
         this.email.value.changed.connect(on_email_changed);
 
-        this.imap_hostname.validator.notify["state"].connect(on_validated);
+        this.imap_hostname.validator.state_changed.connect(on_validated);
+        this.imap_hostname.value.activate.connect(on_activated);
         this.imap_tls.hide();
-        this.imap_login.validator.notify["state"].connect(on_validated);
-        this.imap_password.validator.notify["state"].connect(on_validated);
+        this.imap_login.validator.state_changed.connect(on_validated);
+        this.imap_login.value.activate.connect(on_activated);
+        this.imap_password.validator.state_changed.connect(on_validated);
+        this.imap_password.value.activate.connect(on_activated);
 
-        this.smtp_hostname.validator.notify["state"].connect(on_validated);
+        this.smtp_hostname.validator.state_changed.connect(on_validated);
+        this.smtp_hostname.value.activate.connect(on_activated);
         this.smtp_tls.hide();
         this.smtp_auth.value.changed.connect(on_smtp_auth_changed);
-        this.smtp_login.validator.notify["state"].connect(on_validated);
-        this.smtp_password.validator.notify["state"].connect(on_validated);
+        this.smtp_login.validator.state_changed.connect(on_validated);
+        this.smtp_login.value.activate.connect(on_activated);
+        this.smtp_password.validator.state_changed.connect(on_validated);
+        this.smtp_password.value.activate.connect(on_activated);
 
         if (provider == Geary.ServiceProvider.OTHER) {
             this.receiving_list.add(this.imap_hostname);
@@ -346,22 +356,32 @@ internal class Accounts.EditorAddPane : Gtk.Grid, EditorPane {
     }
 
     private void check_validation() {
-        bool is_valid = true;
+        bool controls_valid = true;
         foreach (Gtk.ListBox list in new Gtk.ListBox[] {
                 this.details_list, this.receiving_list, this.sending_list
             }) {
             list.foreach((child) => {
                     AddPaneRow? validatable = child as AddPaneRow;
                     if (validatable != null && !validatable.validator.is_valid) {
-                        is_valid = false;
+                        controls_valid = false;
                     }
                 });
         }
-        this.create_button.set_sensitive(is_valid);
+        this.create_button.set_sensitive(controls_valid);
+        this.controls_valid = controls_valid;
     }
 
-    private void on_validated() {
+    private void on_validated(Components.Validator.Trigger reason) {
         check_validation();
+        if (this.controls_valid && reason == Components.Validator.Trigger.ACTIVATED) {
+            this.create_button.clicked();
+        }
+    }
+
+    private void on_activated() {
+        if (this.controls_valid) {
+            this.create_button.clicked();
+        }
     }
 
     private void on_email_changed() {
diff --git a/src/client/components/components-validator.vala b/src/client/components/components-validator.vala
index 4432c1b8..ee500a59 100644
--- a/src/client/components/components-validator.vala
+++ b/src/client/components/components-validator.vala
@@ -47,11 +47,13 @@ public class Components.Validator : GLib.Object {
     }
 
     /** The cause of a validity check being required. */
-    protected enum Trigger {
+    public enum Trigger {
         /** The entry's contents changed */
         CHANGED,
-        /** The user performed an action indicating they are done. */
-        COMPLETE;
+        /** The entry lost the keyboard focus. */
+        LOST_FOCUS,
+        /** The user activated the entry. */
+        ACTIVATED;
     }
 
     /** Defines the UI state for a specific validity. */
@@ -100,6 +102,10 @@ public class Components.Validator : GLib.Object {
     private Geary.TimeoutManager ui_update_timer;
 
 
+    /** Fired when the validation state changes. */
+    public signal void state_changed(Trigger reason, Validity prev_state);
+
+
     public Validator(Gtk.Entry target) {
         this.target = target;
 
@@ -157,7 +163,7 @@ public class Components.Validator : GLib.Object {
      * By default, this always returns {@link Validity.VALID}, making
      * it useful for required, but otherwise free-form fields only.
      */
-    protected virtual Validity validate(string value, Trigger cause) {
+    protected virtual Validity validate(string value, Trigger reason) {
         return Validity.VALID;
     }
 
@@ -168,14 +174,20 @@ public class Components.Validator : GLib.Object {
      * CPU-intensive or long-running validation routine and it has
      * completed validating a value. See {@link validate} for details.
      */
-    protected void update_state(Validity new_state) {
+    protected void update_state(Validity new_state, Trigger reason) {
         if (this.state != new_state) {
             Validity old_state = this.state;
 
+            // Fire the signal after updating the state but before
+            // updating the UI so listeners can update UI settings
+            // first if needed.
             this.state = new_state;
-            if (new_state == Validity.VALID) {
-                // Update the UI straight away when going valid to
-                // provide instant feedback
+            state_changed(reason, old_state);
+
+            if (new_state == Validity.VALID || reason != Trigger.CHANGED) {
+                // Update the UI straight away when going valid or
+                // when editing is complete to provide instant
+                // feedback
                 update_ui(new_state);
             } else {
                 if (old_state == Validity.EMPTY) {
@@ -194,23 +206,17 @@ public class Components.Validator : GLib.Object {
         }
     }
 
-    private void validate_entry(Trigger cause) {
+    private void validate_entry(Trigger reason) {
         string value = this.target.get_text();
         Validity new_state = this.state;
         if (Geary.String.is_empty_or_whitespace(value)) {
             new_state = this.is_required
                 ? Validity.EMPTY : Validity.INDETERMINATE;
         } else {
-            new_state = validate(value, cause);
+            new_state = validate(value, reason);
         }
 
-        update_state(new_state);
-
-        if (cause == Trigger.COMPLETE) {
-            // Update the UI instantly since we know the user is done
-            // editing it an will want instant feedback.
-            update_ui(this.state);
-        }
+        update_state(new_state, reason);
     }
 
     private void update_ui(Validity state) {
@@ -256,7 +262,7 @@ public class Components.Validator : GLib.Object {
     }
 
     private void on_activate() {
-        validate_entry(Trigger.COMPLETE);
+        validate_entry(Trigger.ACTIVATED);
     }
 
     private void on_update_ui() {
@@ -275,7 +281,7 @@ public class Components.Validator : GLib.Object {
         // the focused widget any more, rather than the whole window
         // having lost focus.
         if (!this.target.is_focus) {
-            validate_entry(Trigger.COMPLETE);
+            validate_entry(Trigger.LOST_FOCUS);
         }
         return Gdk.EVENT_PROPAGATE;
     }
@@ -302,7 +308,7 @@ public class Components.EmailValidator : Validator {
 
 
     protected override Validator.Validity validate(string value,
-                                                   Validator.Trigger cause) {
+                                                   Validator.Trigger reason) {
         return Geary.RFC822.MailboxAddress.is_valid_address(value)
             ? Validator.Validity.VALID : Validator.Validity.INVALID;
     }
@@ -384,12 +390,12 @@ public class Components.NetworkAddressValidator : Validator {
                     try {
                         this.resolver.lookup_by_name_async.end(res);
                         this.validated_address = address;
-                        update_state(Validator.Validity.VALID);
+                        update_state(Validator.Validity.VALID, reason);
                     } catch (GLib.IOError.CANCELLED err) {
                         this.validated_address = null;
                     } catch (GLib.Error err) {
                         this.validated_address = null;
-                        update_state(Validator.Validity.INVALID);
+                        update_state(Validator.Validity.INVALID, reason);
                     }
                     this.cancellable = null;
                 }


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]