[gnome-contacts/nielsdg/prefs-window] Add a preferences window




commit f677414715d5a79c0d88a9833690e5b8f6e5254a
Author: Niels De Graef <nielsdegraef gmail com>
Date:   Sun Jul 24 13:33:04 2022 +0200

    Add a preferences window
    
    This allows us to simplify the main menu, as well as having an easy
    way in the future to add extra options.

 data/contacts.gresource.xml                        |  2 +-
 .../scalable/actions/external-link-symbolic.svg    | 72 +++++++++++++++++---
 data/ui/contacts-accounts-list.ui                  | 16 -----
 data/ui/contacts-main-window.ui                    | 11 +--
 data/ui/contacts-preferences-window.ui             | 10 +++
 po/POTFILES.in                                     |  2 +
 po/POTFILES.skip                                   |  1 +
 src/contacts-accounts-list.vala                    | 47 +++++++------
 src/contacts-addressbook-dialog.vala               |  7 +-
 src/contacts-app.vala                              | 38 ++---------
 src/contacts-preferences-window.vala               | 78 ++++++++++++++++++++++
 src/contacts-utils.vala                            |  6 ++
 src/meson.build                                    |  1 +
 13 files changed, 196 insertions(+), 95 deletions(-)
---
diff --git a/data/contacts.gresource.xml b/data/contacts.gresource.xml
index 533e3d0b..89c80529 100644
--- a/data/contacts.gresource.xml
+++ b/data/contacts.gresource.xml
@@ -14,7 +14,6 @@
     <file preprocess="xml-stripblanks">icons/scalable/actions/website-symbolic.svg</file>
 
     <file compressed="true" preprocess="xml-stripblanks">gtk/help-overlay.ui</file>
-    <file compressed="true" preprocess="xml-stripblanks">ui/contacts-accounts-list.ui</file>
     <file compressed="true" preprocess="xml-stripblanks">ui/contacts-avatar-selector.ui</file>
     <file compressed="true" preprocess="xml-stripblanks">ui/contacts-contact-pane.ui</file>
     <file compressed="true" preprocess="xml-stripblanks">ui/contacts-crop-dialog.ui</file>
@@ -22,6 +21,7 @@
     <file compressed="true" preprocess="xml-stripblanks">ui/contacts-link-suggestion-grid.ui</file>
     <file compressed="true" preprocess="xml-stripblanks">ui/contacts-linked-personas-dialog.ui</file>
     <file compressed="true" preprocess="xml-stripblanks">ui/contacts-main-window.ui</file>
+    <file compressed="true" preprocess="xml-stripblanks">ui/contacts-preferences-window.ui</file>
     <file compressed="true" preprocess="xml-stripblanks">ui/contacts-setup-window.ui</file>
   </gresource>
 </gresources>
diff --git a/data/icons/scalable/actions/external-link-symbolic.svg 
b/data/icons/scalable/actions/external-link-symbolic.svg
index 1ac7ee70..74f85c3a 100644
--- a/data/icons/scalable/actions/external-link-symbolic.svg
+++ b/data/icons/scalable/actions/external-link-symbolic.svg
@@ -1,11 +1,61 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<svg height="16px" viewBox="0 0 16 16" width="16px" xmlns="http://www.w3.org/2000/svg";>
-    <g fill="#2e3436">
-        <path d="m 2 3 v 11 h 11 v -4 h -2 v 2 h -7 v -7 h 2 v -2 z m 0 0"/>
-        <path d="m 9 2 c -0.550781 0 -1 0.449219 -1 1 s 0.449219 1 1 1 h 3 v 3 c 0 0.550781 0.449219 1 1 1 s 
1 -0.449219 1 -1 v -4 c 0 -0.550781 -0.449219 -1 -1 -1 z m 0 0"/>
-        <path d="m 13 2 h 1 v 1 h -1 z m 0 0"/>
-        <path d="m 12.292969 2.289062 l -4.5 4.46875 c -0.390625 0.390626 -0.390625 1.027344 0 1.414063 c 
0.390625 0.394531 1.023437 0.394531 1.414062 0.007813 l 4.5 -4.46875 c 0.390625 -0.390626 0.390625 -1.027344 
0 -1.414063 c -0.386719 -0.394531 -1.019531 -0.394531 -1.414062 -0.007813 z m 0 0"/>
-        <path d="m 13 7 h 1 v 1 h -1 z m 0 0"/>
-        <path d="m 8 2 h 1 v 1 h -1 z m 0 0"/>
-    </g>
-</svg>
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg height="16px" viewBox="0 0 16 16" width="16px" version="1.1" id="svg89887" 
sodipodi:docname="external-link-symbolic.svg" inkscape:version="1.2.1 (9c6d41e410, 2022-07-14)" 
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"; 
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"; xmlns="http://www.w3.org/2000/svg"; 
xmlns:svg="http://www.w3.org/2000/svg";>
+  <defs id="defs89891"/>
+  <sodipodi:namedview id="namedview89889" pagecolor="#ffffff" bordercolor="#666666" borderopacity="1.0" 
inkscape:showpageshadow="2" inkscape:pageopacity="0.0" inkscape:pagecheckerboard="0" 
inkscape:deskcolor="#d1d1d1" showgrid="true" inkscape:zoom="14.75" inkscape:cx="4.4067797" 
inkscape:cy="6.2372881" inkscape:current-layer="svg89887">
+    <inkscape:grid type="xygrid" id="grid90073"/>
+  </sodipodi:namedview>
+  <filter id="a" height="1" width="1" x="0" y="0">
+    <feColorMatrix in="SourceGraphic" type="matrix" values="0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 0 0 0 1 0" 
id="feColorMatrix89780"/>
+  </filter>
+  <mask id="b">
+    <g filter="url(#a)" id="g89785">
+      <rect fill-opacity="0.55" height="16" width="16" id="rect89783"/>
+    </g>
+  </mask>
+  <clipPath id="c">
+    <rect height="152" width="192" id="rect89788"/>
+  </clipPath>
+  <mask id="d">
+    <g filter="url(#a)" id="g89793">
+      <rect fill-opacity="0.55" height="16" width="16" id="rect89791"/>
+    </g>
+  </mask>
+  <clipPath id="e">
+    <rect height="152" width="192" id="rect89796"/>
+  </clipPath>
+  <mask id="f">
+    <g filter="url(#a)" id="g89801">
+      <rect fill-opacity="0.55" height="16" width="16" id="rect89799"/>
+    </g>
+  </mask>
+  <clipPath id="g">
+    <rect height="152" width="192" id="rect89804"/>
+  </clipPath>
+  <mask id="h">
+    <g filter="url(#a)" id="g89809">
+      <rect fill-opacity="0.3" height="16" width="16" id="rect89807"/>
+    </g>
+  </mask>
+  <clipPath id="i">
+    <rect height="152" width="192" id="rect89812"/>
+  </clipPath>
+  <mask id="j">
+    <g filter="url(#a)" id="g89817">
+      <rect fill-opacity="0.3" height="16" width="16" id="rect89815"/>
+    </g>
+  </mask>
+  <clipPath id="k">
+    <rect height="152" width="192" id="rect89820"/>
+  </clipPath>
+  <mask id="l">
+    <g filter="url(#a)" id="g89825">
+      <rect fill-opacity="0.3" height="16" width="16" id="rect89823"/>
+    </g>
+  </mask>
+  <clipPath id="m">
+    <rect height="152" width="192" id="rect89828"/>
+  </clipPath>
+  <path id="path193961" style="fill:#222222;fill-opacity:1" d="M 3,2 C 1.338,2 0,3.338 0,5 v 8 c 0,1.662 
1.338,3 3,3 h 8 c 1.662,0 3,-1.338 3,-3 V 9 C 13.9998,8.4477 13.5522,8 13,8 12.4478,8 12.0002,8.4477 12,9 v 4 
c 0,0.554 -0.446,1 -1,1 H 3 C 2.446,14 2,13.554 2,13 V 5 C 2,4.446 2.446,4 3,4 H 7 C 7.5523,4 8,3.5523 8,3 
8,2.4477 7.5523,2 7,2 Z M 10,0 C 9.4477,0 9,0.4477 9,1 9,1.5523 9.4477,2 10,2 h 2.584 L 7.293,7.291 c 
-0.3917,0.3907 -0.3917,1.0253 0,1.416 0.3905,0.3904 1.0236,0.3904 1.4141,0 L 14,3.4141 V 6 c 0,0.5523 
0.4477,1 1,1 0.5523,0 1,-0.4477 1,-1 V 1 C 15.9996,0.913 15.988,0.8262 15.965,0.7422 15.942,0.6572 
15.909,0.576 15.865,0.5 15.822,0.424 15.768,0.3548 15.7068,0.293 15.6918,0.281 15.6758,0.269 15.6598,0.258 
15.6048,0.208 15.5433,0.165 15.4782,0.1291 c -0.037,-0.019 -0.076,-0.035 -0.1152,-0.049 -0.051,-0.021 
-0.1029,-0.037 -0.1563,-0.049 -0.04,-0.01 -0.08,-0.014 -0.1211,-0.018 -0.029,-0.01 -0.057,-0.01 -0.086,-0.012 
z" sodipodi:nodetypes="sssssscscsssssssssscsscccccsss
 ccscccccccc" inkscape:label="arrow box link external web http diagonal">
+    <title id="title8267">external-link</title>
+  </path>
+</svg>
\ No newline at end of file
diff --git a/data/ui/contacts-main-window.ui b/data/ui/contacts-main-window.ui
index 2e20f972..289c56c8 100644
--- a/data/ui/contacts-main-window.ui
+++ b/data/ui/contacts-main-window.ui
@@ -16,16 +16,9 @@
     </section>
     <section>
       <item>
-        <attribute name="label" translatable="yes">Change Address Book…</attribute>
-        <attribute name="action">app.change-book</attribute>
+        <attribute name="label" translatable="yes">Preferences</attribute>
+        <attribute name="action">app.show-preferences</attribute>
       </item>
-      <item>
-        <attribute name="label" translatable="yes">Online Accounts &lt;sup&gt;↗&lt;/sup&gt;</attribute>
-        <attribute name="action">app.online-accounts</attribute>
-        <attribute name="use-markup">True</attribute>
-      </item>
-    </section>
-    <section>
       <item>
         <attribute name="label" translatable="yes">Keyboard Shortcuts</attribute>
         <attribute name="action">win.show-help-overlay</attribute>
diff --git a/data/ui/contacts-preferences-window.ui b/data/ui/contacts-preferences-window.ui
new file mode 100644
index 00000000..fc4d2b49
--- /dev/null
+++ b/data/ui/contacts-preferences-window.ui
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface>
+  <template class="ContactsPreferencesWindow" parent="AdwPreferencesWindow">
+    <child>
+      <object class="AdwPreferencesPage" id="address_books_page">
+        <property name="title" translatable="yes">Address Books</property>
+      </object>
+    </child>
+  </template>
+</interface>
diff --git a/po/POTFILES.in b/po/POTFILES.in
index 36e9a368..4ce720ea 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -11,6 +11,7 @@ data/ui/contacts-editor-menu.ui
 data/ui/contacts-link-suggestion-grid.ui
 data/ui/contacts-linked-personas-dialog.ui
 data/ui/contacts-main-window.ui
+data/ui/contacts-preferences-window.ui
 data/ui/contacts-setup-window.ui
 src/contacts-accounts-list.vala
 src/contacts-addressbook-dialog.vala
@@ -32,6 +33,7 @@ src/contacts-link-suggestion-grid.vala
 src/contacts-linked-personas-dialog.vala
 src/contacts-main-window.vala
 src/contacts-operation.vala
+src/contacts-preferences-window.vala
 src/contacts-settings.vala
 src/contacts-setup-window.vala
 src/contacts-type-combo.vala
diff --git a/po/POTFILES.skip b/po/POTFILES.skip
index f5fcf7ad..627134e0 100644
--- a/po/POTFILES.skip
+++ b/po/POTFILES.skip
@@ -22,6 +22,7 @@ src/contacts-link-suggestion-grid.c
 src/contacts-linked-personas-dialog.c
 src/contacts-main-window.c
 src/contacts-operation.c
+src/contacts-preferences-window.c
 src/contacts-settings.c
 src/contacts-setup-window.c
 src/contacts-type-combo.c
diff --git a/src/contacts-accounts-list.vala b/src/contacts-accounts-list.vala
index 202c1607..db04cc9a 100644
--- a/src/contacts-accounts-list.vala
+++ b/src/contacts-accounts-list.vala
@@ -25,14 +25,12 @@ using Folks;
  *
  * Internally, each "address book" is a {@link Folks.PersonaStore}.
  */
-[GtkTemplate (ui = "/org/gnome/Contacts/ui/contacts-accounts-list.ui")]
-public class Contacts.AccountsList : Adw.Bin {
-
-  [GtkChild]
-  private unowned Gtk.ListBox listbox;
+public class Contacts.AccountsList : Adw.PreferencesGroup {
 
   private Gtk.SingleSelection selection;
 
+  private GenericArray<AddressbookRow> rows = new GenericArray<AddressbookRow> ();
+
   /** The selected PersonaStore (or null if none) */
   public PersonaStore? selected_store {
     get { return (PersonaStore) this.selection.selected_item; }
@@ -47,32 +45,20 @@ public class Contacts.AccountsList : Adw.Bin {
     var model = new Gtk.FilterListModel (contacts_store.address_books,
                                          (owned) filter);
 
+    model.items_changed.connect (on_model_items_changed);
+    on_model_items_changed (model, 0, 0, model.get_n_items ());
+
     // Setup the selection model
     this.selection = new Gtk.SingleSelection (model);
 
     // Update the row when the selection model changes
     this.selection.selection_changed.connect ((sel, pos, n_items) => {
       for (uint i = pos; i < pos + n_items; i++) {
-        var row = (AddressbookRow?) this.listbox.get_row_at_index ((int) i);
-        if (row != null)
-          row.selected = this.selection.is_selected (i);
+        this.rows[i].selected = this.selection.is_selected (i);
       }
       notify_property ("selected-store");
     });
 
-    // Now bind the listbox to it
-    this.listbox.bind_model (model, (item) => {
-      unowned var persona_store = (PersonaStore) item;
-      var row = new AddressbookRow (persona_store);
-
-      // Update the selection model when the row is activated
-      row.activated.connect ((row) => {
-        this.selection.set_selected ((uint) row.get_index ());
-      });
-
-      return row;
-    });
-
     // Initially, the primary store (if set) is selected
     for (uint i = 0; i < model.get_n_items (); i++) {
       var persona_store = (PersonaStore) model.get_item (i);
@@ -81,6 +67,25 @@ public class Contacts.AccountsList : Adw.Bin {
     }
   }
 
+  private void on_model_items_changed (ListModel model, uint pos, uint removed, uint added) {
+      for (uint i = pos; i < pos + removed; i++) {
+        remove (this.rows[i]);
+        this.rows.remove_index (i);
+      }
+
+      for (uint i = pos; i < pos + added; i++) {
+        var persona_store = (PersonaStore) model.get_item(i);
+        var row = new AddressbookRow (persona_store);
+        add (row);
+        this.rows.add (row);
+
+        // Update the selection model when the row is activated
+        row.activated.connect ((row) => {
+          this.selection.set_selected ((uint) row.get_index ());
+        });
+      }
+  }
+
   private class AddressbookRow : Adw.ActionRow {
 
     public PersonaStore persona_store { get; construct set; }
diff --git a/src/contacts-addressbook-dialog.vala b/src/contacts-addressbook-dialog.vala
index 03b13b0e..14d71535 100644
--- a/src/contacts-addressbook-dialog.vala
+++ b/src/contacts-addressbook-dialog.vala
@@ -74,10 +74,7 @@ public class Contacts.AddressbookDialog : Gtk.Dialog {
       return;
 
     unowned var e_store = (Edsf.PersonaStore) this.accounts_list.selected_store;
-    if (e_store != null) {
-      eds_source_registry.set_default_address_book (e_store.source);
-      var settings = new GLib.Settings ("org.freedesktop.folks");
-      settings.set_string ("primary-store", "eds:%s".printf (e_store.id));
-    }
+    if (e_store != null)
+      Utils.set_primary_store (e_store);
   }
 }
diff --git a/src/contacts-app.vala b/src/contacts-app.vala
index dcf3e6e4..b127554c 100644
--- a/src/contacts-app.vala
+++ b/src/contacts-app.vala
@@ -36,8 +36,7 @@ public class Contacts.App : Adw.Application {
     { "quit",             quit_action         },
     { "help",             show_help           },
     { "about",            show_about          },
-    { "change-book",      change_address_book },
-    { "online-accounts",  online_accounts     },
+    { "show-preferences", show_preferences },
     { "show-contact",     on_show_contact, "s"}
   };
 
@@ -121,30 +120,9 @@ public class Contacts.App : Adw.Application {
     dialog.show ();
   }
 
-  public void online_accounts () {
-    try {
-      var proxy = new DBusProxy.for_bus_sync (BusType.SESSION,
-                                              DBusProxyFlags.NONE,
-                                              null,
-                                              "org.gnome.Settings",
-                                              "/org/gnome/Settings",
-                                              "org.gtk.Actions");
-
-      var builder = new VariantBuilder (new VariantType ("av"));
-      builder.add ("v", new Variant.string (""));
-      var param = new Variant.tuple ({
-        new Variant.string ("launch-panel"),
-        new Variant.array (new VariantType ("v"), {
-          new Variant ("v", new Variant ("(sav)", "online-accounts", builder))
-        }),
-        new Variant.array (new VariantType ("{sv}"), {})
-      });
-
-      proxy.call_sync ("Activate", param, DBusCallFlags.NONE, -1);
-    } catch (Error e) {
-      // TODO: Show error dialog
-      warning ("Couldn't open online-accounts: %s", e.message);
-    }
+  public void show_preferences () {
+    var prefs_window = new PreferencesWindow (this.contacts_store, this.window);
+    prefs_window.show ();
   }
 
   public void show_help () {
@@ -278,19 +256,15 @@ public class Contacts.App : Adw.Application {
   private void run_setup () {
     debug ("Running initial setup");
 
-    // Disable change-book action (don't want the user to do that during setup)
-    unowned var change_book_action = lookup_action ("change-book") as SimpleAction;
-    change_book_action.set_enabled (false);
-
     // Create and show the setup window
     var setup_window = new SetupWindow (this, this.contacts_store);
     setup_window.setup_done.connect ((selected_store) => {
       setup_window.destroy ();
 
-      eds_source_registry.set_default_address_book (selected_store.source);
+      unowned var edsf_store = (Edsf.PersonaStore) selected_store;
+      Utils.set_primary_store (edsf_store);
       this.settings.did_initial_setup = true;
 
-      change_book_action.set_enabled (true);   // re-enable change-book action
       create_window ();
     });
     setup_window.show ();
diff --git a/src/contacts-preferences-window.vala b/src/contacts-preferences-window.vala
new file mode 100644
index 00000000..e6abbd29
--- /dev/null
+++ b/src/contacts-preferences-window.vala
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2022 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/>.
+ */
+
+using Folks;
+
+[GtkTemplate (ui = "/org/gnome/Contacts/ui/contacts-preferences-window.ui")]
+public class Contacts.PreferencesWindow : Adw.PreferencesWindow {
+
+  [GtkChild]
+  private unowned Adw.PreferencesPage address_books_page;
+
+  public PreferencesWindow (Store contacts_store, Gtk.Window? transient_for) {
+    Object (transient_for: transient_for, search_enabled: false);
+
+    var acc_list = new AccountsList (contacts_store);
+    acc_list.title = _("Primary Address Book");
+    acc_list.description = _("New contacts will be added to the selected address book. You are able to view 
and edit contacts from other address books.");
+    this.address_books_page.add (acc_list);
+
+    acc_list.notify["selected-store"].connect ((obj, pspec) => {
+      var edsf_store = (Edsf.PersonaStore) acc_list.selected_store;
+      Utils.set_primary_store (edsf_store);
+    });
+
+    var goa_button_content = new Adw.ButtonContent ();
+    goa_button_content.label = _("_Online Accounts");
+    goa_button_content.use_underline = true;
+    goa_button_content.icon_name = "external-link-symbolic";
+    var goa_button = new Gtk.Button ();
+    goa_button.set_child (goa_button_content);
+    goa_button.tooltip_text = _("Opens the Online Accounts panel in GNOME Settings");
+    goa_button.margin_top = 36;
+    goa_button.halign = Gtk.Align.CENTER;
+    goa_button.add_css_class ("pill");
+    goa_button.clicked.connect (on_goa_button_clicked);
+    acc_list.add (goa_button);
+  }
+
+  private void on_goa_button_clicked (Gtk.Button goa_button) {
+    try {
+      var proxy = new DBusProxy.for_bus_sync (BusType.SESSION,
+                                              DBusProxyFlags.NONE,
+                                              null,
+                                              "org.gnome.Settings",
+                                              "/org/gnome/Settings",
+                                              "org.gtk.Actions");
+
+      var builder = new VariantBuilder (new VariantType ("av"));
+      builder.add ("v", new Variant.string (""));
+      var param = new Variant.tuple ({
+        new Variant.string ("launch-panel"),
+        new Variant.array (new VariantType ("v"), {
+          new Variant ("v", new Variant ("(sav)", "online-accounts", builder))
+        }),
+        new Variant.array (new VariantType ("{sv}"), {})
+      });
+
+      proxy.call_sync ("Activate", param, DBusCallFlags.NONE, -1);
+    } catch (Error e) {
+      // TODO: Show error dialog
+      warning ("Couldn't open online-accounts: %s", e.message);
+    }
+  }
+}
diff --git a/src/contacts-utils.vala b/src/contacts-utils.vala
index a5c28c05..f5d6a665 100644
--- a/src/contacts-utils.vala
+++ b/src/contacts-utils.vala
@@ -29,6 +29,12 @@ namespace Contacts {
 
 namespace Contacts.Utils {
 
+  public void set_primary_store (Edsf.PersonaStore e_store) {
+    eds_source_registry.set_default_address_book (e_store.source);
+    var settings = new GLib.Settings ("org.freedesktop.folks");
+    settings.set_string ("primary-store", "eds:%s".printf (e_store.id));
+  }
+
   public void compose_mail (string email) {
     var mailto_uri = "mailto:"; + Uri.escape_string (email, "@" , false);
     Gtk.show_uri (null, mailto_uri, 0);
diff --git a/src/meson.build b/src/meson.build
index 1f3890e6..c246b612 100644
--- a/src/meson.build
+++ b/src/meson.build
@@ -87,6 +87,7 @@ contacts_vala_sources = files(
   'contacts-link-suggestion-grid.vala',
   'contacts-linked-personas-dialog.vala',
   'contacts-main-window.vala',
+  'contacts-preferences-window.vala',
   'contacts-settings.vala',
   'contacts-setup-window.vala',
   'contacts-type-combo.vala',


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