[gnome-contacts] Window: Implement the UI as an FSM.
- From: Niels De Graef <nielsdg src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gnome-contacts] Window: Implement the UI as an FSM.
- Date: Sat, 13 Jan 2018 17:34:17 +0000 (UTC)
commit 279fecd0459f6b7e9112e7b3542c2d5300943d39
Author: Niels De Graef <nielsdegraef gmail com>
Date: Sat Jan 13 18:28:28 2018 +0100
Window: Implement the UI as an FSM.
This way it's clear what parts of the UI should be active when
selecting, editing, ...
It also allows us to get rid of a hard to follow combo of
edit_mode/selection_mode/new_contact variables.
src/contacts-ui-state.vala | 60 +++++++++++++++++++++
src/contacts-window.vala | 126 +++++++++++++++++++-------------------------
src/meson.build | 1 +
3 files changed, 116 insertions(+), 71 deletions(-)
---
diff --git a/src/contacts-ui-state.vala b/src/contacts-ui-state.vala
new file mode 100644
index 0000000..ad7230b
--- /dev/null
+++ b/src/contacts-ui-state.vala
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2018 Niels De Graef <nielsdegraef gmail com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/**
+ * Roughly put, the behaviour of the UI of Contacts can be divided in several
+ * categories. We represent this with the UiState enum, which can be shared
+ * (and sync-ed) between the different parts of the app.
+ *
+ * Note that there is one exception to this: the initial setup is handled
+ * completely separately in the {@link SetupWindow}.
+ */
+public enum Contacts.UiState {
+ /**
+ * The start state: no contact is selected/displayed.
+ */
+ NORMAL,
+
+ /**
+ * A contact has been selected and is displayed.
+ */
+ SHOWING,
+
+ /**
+ * Zero or more contacts are selected (but this can be changed).
+ * No contact should be displayed.
+ */
+ SELECTING,
+
+ /**
+ * The selected contact is being edited.
+ */
+ UPDATING,
+
+ /**
+ * A new contact is being created.
+ */
+ CREATING;
+
+ /**
+ * Returns whether we're editing a contact, either by changing an existing
+ * one, or by creating a new one.
+ */
+ public bool editing () {
+ return this == UiState.UPDATING || this == UiState.CREATING;
+ }
+}
diff --git a/src/contacts-window.vala b/src/contacts-window.vala
index 9ce6c43..f20cc8f 100644
--- a/src/contacts-window.vala
+++ b/src/contacts-window.vala
@@ -52,13 +52,17 @@ public class Contacts.Window : Gtk.ApplicationWindow {
private ListPane list_pane;
private ContactPane contact_pane;
+ // We start in the normal UI state
+ private UiState _state = UiState.NORMAL;
+ public UiState state {
+ get { return this._state; }
+ set { change_ui_state (value); }
+ }
+
public Store store {
get; construct set;
}
- private bool selection_mode = false;
- private bool editing_new_contact = false;
-
public Window (App app, Store contacts_store) {
Object (
application: app,
@@ -68,7 +72,6 @@ public class Contacts.Window : Gtk.ApplicationWindow {
debug ("everyone creation: finalized already!!!");
create_contact_pane ();
-
set_headerbar_layout ();
connect_button_signals ();
}
@@ -113,60 +116,49 @@ public class Contacts.Window : Gtk.ApplicationWindow {
list_pane.show ();
}
- public void activate_selection_mode (bool active) {
- this.selection_mode = active;
-
- // Show some buttons when selecting (and vice versa)
- this.select_cancel_button.visible = active;
- // and hide some
- this.select_button.visible = !active;
- this.add_button.visible = !active;
- this.edit_button.visible = !active;
- this.right_header.show_close_button = !active;
-
- this.list_pane.activate_selection_mode (active);
-
- if (active) {
- left_header.get_style_context ().add_class ("selection-mode");
- right_header.get_style_context ().add_class ("selection-mode");
-
- left_header.set_title (_("Select"));
-
+ private void change_ui_state (UiState new_state) {
+ // UI when we're not editing of selecting stuff
+ this.add_button.visible
+ = this.right_header.show_close_button
+ = this.select_button.visible
+ = (new_state == UiState.NORMAL || new_state == UiState.SHOWING);
+
+ // UI when showing a contact
+ this.edit_button.visible = (new_state == UiState.SHOWING);
+
+ // Selecting UI
+ this.select_cancel_button.visible = (new_state == UiState.SELECTING);
+ this.list_pane.activate_selection_mode (new_state == UiState.SELECTING);
+
+ // Editing UI
+ this.cancel_button.visible
+ = this.done_button.visible
+ = new_state.editing ();
+ if (new_state.editing ())
+ this.done_button.label = (new_state == UiState.CREATING)? _("Add") : _("Done");
+
+ // When selecting or editing, we get special headerbars
+ if (new_state == UiState.SELECTING || new_state.editing ()) {
+ this.left_header.get_style_context ().add_class ("selection-mode");
+ this.right_header.get_style_context ().add_class ("selection-mode");
+
+ this.left_header.title = (new_state == UiState.SELECTING)? _("Select") : "";
} else {
- left_header.get_style_context ().remove_class ("selection-mode");
- right_header.get_style_context ().remove_class ("selection-mode");
-
- left_header.set_title (_("All Contacts"));
+ this.left_header.get_style_context ().remove_class ("selection-mode");
+ this.right_header.get_style_context ().remove_class ("selection-mode");
- /* could be no contact selected whatsoever */
- if (this.contact_pane.contact == null)
- edit_button.hide ();
+ this.left_header.title = _("All Contacts");
}
- }
-
- private void activate_edit_mode (bool active) {
- this.done_button.visible = active;
- this.cancel_button.visible = active;
-
- this.edit_button.visible = !active;
- this.add_button.visible = !active;
- this.select_button.visible = !active;
- this.right_header.show_close_button = !active;
- if (active) {
- left_header.get_style_context ().add_class ("selection-mode");
- right_header.get_style_context ().add_class ("selection-mode");
- } else {
- left_header.get_style_context ().remove_class ("selection-mode");
- right_header.get_style_context ().remove_class ("selection-mode");
- }
+ // Save the result
+ this._state = new_state;
}
private void edit_contact () {
if (this.contact_pane.contact == null)
return;
- activate_edit_mode (true);
+ this.state = UiState.UPDATING;
var name = this.contact_pane.contact.display_name;
this.right_header.title = _("Editing %s").printf (name);
@@ -174,18 +166,14 @@ public class Contacts.Window : Gtk.ApplicationWindow {
this.contact_pane.set_edit_mode (true);
}
- private void leave_edit_mode (bool drop_changes = false) {
- activate_edit_mode (false);
-
- if (this.editing_new_contact) {
- done_button.label = _("Done");
+ private void stop_editing (bool drop_changes = false) {
+ if (this.state == UiState.CREATING) {
if (drop_changes) {
this.contact_pane.set_edit_mode (false, drop_changes);
} else {
this.contact_pane.create_contact.begin ();
}
- this.editing_new_contact = false;
} else {
this.contact_pane.set_edit_mode (false, drop_changes);
}
@@ -194,8 +182,9 @@ public class Contacts.Window : Gtk.ApplicationWindow {
this.right_header.title = this.contact_pane.contact.display_name;
} else {
this.right_header.title = "";
- edit_button.hide ();
}
+
+ this.state = UiState.SHOWING;
}
public void add_notification (InAppNotification notification) {
@@ -206,27 +195,24 @@ public class Contacts.Window : Gtk.ApplicationWindow {
public void set_shown_contact (Contact? c) {
/* FIXME: ask the user to leave edit-mode and act accordingly */
if (this.contact_pane.on_edit_mode)
- leave_edit_mode ();
+ stop_editing ();
+
+ this.state = (c != null)? UiState.SHOWING : UiState.NORMAL;
this.contact_pane.show_contact (c, false);
if (list_pane != null)
list_pane.select_contact (c);
// clearing right_header
- this.right_header.title = (c != null)? c.display_name : "";
-
- edit_button.visible = (c != null) && !this.selection_mode;
+ if (c != null)
+ this.right_header.title = c.display_name;
}
[GtkCallback]
public void new_contact () {
- /* FIXME: eventually ContactPane will become just a skeleton and
- * this call will go through to ContactEditor */
- activate_edit_mode (true);
- this.editing_new_contact = true;
+ this.state = UiState.CREATING;
this.right_header.title = _("New Contact");
- this.done_button.label = _("Add");
this.contact_pane.new_contact ();
}
@@ -247,11 +233,11 @@ public class Contacts.Window : Gtk.ApplicationWindow {
}
private void connect_button_signals () {
- this.select_button.clicked.connect (() => activate_selection_mode (true));
- this.select_cancel_button.clicked.connect (() => activate_selection_mode (false));
+ this.select_button.clicked.connect (() => { this.state = UiState.SELECTING; });
+ this.select_cancel_button.clicked.connect (() => { this.state = UiState.NORMAL; });
this.edit_button.clicked.connect (() => edit_contact ());
- this.done_button.clicked.connect (() => leave_edit_mode ());
- this.cancel_button.clicked.connect (() => leave_edit_mode (true));
+ this.done_button.clicked.connect (() => stop_editing ());
+ this.cancel_button.clicked.connect (() => stop_editing (true));
}
[GtkCallback]
@@ -293,7 +279,6 @@ public class Contacts.Window : Gtk.ApplicationWindow {
void list_pane_link_contacts_cb (LinkedList<Contact> contact_list) {
/* getting out of selection mode */
set_shown_contact (null);
- activate_selection_mode (false);
LinkOperation2 operation = null;
link_contacts_list.begin (contact_list, this.store, (obj, result) => {
@@ -320,7 +305,7 @@ public class Contacts.Window : Gtk.ApplicationWindow {
void list_pane_delete_contacts_cb (LinkedList<Contact> contact_list) {
/* getting out of selection mode */
set_shown_contact (null);
- activate_selection_mode (false);
+ this.state == UiState.NORMAL;
string msg = ngettext ("%d contact deleted",
"%d contacts deleted",
@@ -353,7 +338,6 @@ public class Contacts.Window : Gtk.ApplicationWindow {
private void contact_pane_delete_contact_cb (Contact contact) {
/* unsetting edit-mode */
set_shown_contact (null);
- activate_selection_mode (false);
var msg = _("Contact deleted: ā%sā").printf (contact.display_name);
var b = new Button.with_mnemonic (_("_Undo"));
diff --git a/src/meson.build b/src/meson.build
index 2a387c3..a7bbf95 100644
--- a/src/meson.build
+++ b/src/meson.build
@@ -24,6 +24,7 @@ contacts_vala_sources = [
'contacts-setup-window.vala',
'contacts-store.vala',
'contacts-types.vala',
+ 'contacts-ui-state.vala',
'contacts-utils.vala',
'contacts-window.vala',
'main.vala',
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]