Ensure new accounts can be created by hitting Enter.

This commit is contained in:
Michael Gratton 2018-09-08 20:53:38 +10:00 committed by Michael James Gratton
parent 468a7a674b
commit 81e04a21d5
2 changed files with 60 additions and 34 deletions

View file

@ -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() {

View file

@ -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;
}