[gnome-contacts: 4/8] Use Egg.ListBox for Contacts.View, not TreeView
- From: Alexander Larsson <alexl src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gnome-contacts: 4/8] Use Egg.ListBox for Contacts.View, not TreeView
- Date: Tue, 15 May 2012 12:30:41 +0000 (UTC)
commit 2432efa967b555bd297f203cd48b99f0753b3a57
Author: Alexander Larsson <alexl redhat com>
Date: Mon Feb 20 01:11:49 2012 +0100
Use Egg.ListBox for Contacts.View, not TreeView
This lets us do e.g. separators, and we can drop a bunch of custom cell renderers
and general fighting with TreeView
src/Makefile.am | 1 +
src/contacts-link-dialog.vala | 3 +-
src/contacts-list-pane.vala | 4 +-
src/contacts-view.vala | 553 +++++++++++++----------------------------
4 files changed, 173 insertions(+), 388 deletions(-)
---
diff --git a/src/Makefile.am b/src/Makefile.am
index 3fc3dfd..bea426a 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -22,6 +22,7 @@ endif
bin_PROGRAMS = gnome-contacts
vala_sources = \
+ listbox/egg-list-box.vala \
contacts-app.vala \
contacts-cell-renderer-shape.vala \
contacts-contact.vala \
diff --git a/src/contacts-link-dialog.vala b/src/contacts-link-dialog.vala
index 03a61d4..7024168 100644
--- a/src/contacts-link-dialog.vala
+++ b/src/contacts-link-dialog.vala
@@ -220,8 +220,9 @@ public class Contacts.LinkDialog : Dialog {
scrolled.set_vexpand (true);
scrolled.set_hexpand (true);
scrolled.set_shadow_type (ShadowType.NONE);
- scrolled.add (view);
+ scrolled.add_with_viewport (view);
list_grid.add (scrolled);
+ view.set_focus_vadjustment (scrolled.get_vadjustment ());
view.selection_changed.connect ( (c) => {
selected_contact = c;
diff --git a/src/contacts-list-pane.vala b/src/contacts-list-pane.vala
index 50ae1fe..a0fdcd1 100644
--- a/src/contacts-list-pane.vala
+++ b/src/contacts-list-pane.vala
@@ -138,12 +138,14 @@ public class Contacts.ListPane : Frame {
grid.set_orientation (Orientation.VERTICAL);
this.add (grid);
+ contacts_view.set_focus_vadjustment (scrolled.get_vadjustment ());
+
contacts_view.selection_changed.connect( (l, contact) => {
if (!ignore_selection_change)
selection_changed (contact);
});
- scrolled.add (contacts_view);
+ scrolled.add_with_viewport (contacts_view);
contacts_view.show_all ();
scrolled.set_no_show_all (true);
diff --git a/src/contacts-view.vala b/src/contacts-view.vala
index 6f07fe0..3eec896 100644
--- a/src/contacts-view.vala
+++ b/src/contacts-view.vala
@@ -20,13 +20,16 @@ using Gtk;
using Folks;
using Gee;
-public class Contacts.View : TreeView {
+public class Contacts.View : Egg.ListBox {
private class ContactData {
public Contact contact;
- public TreeIter iter;
- public bool visible;
- public bool is_first;
+ public Grid grid;
+ public Label label;
+ public ContactFrame image_frame;
public int sort_prio;
+ public string display_name;
+ public unichar initial_letter;
+ public bool filtered;
}
public enum Subset {
@@ -36,46 +39,44 @@ public class Contacts.View : TreeView {
ALL
}
+ public enum TextDisplay {
+ NONE,
+ PRESENCE,
+ STORES
+ }
+
+ public signal void selection_changed (Contact? contact);
+
Store contacts_store;
Subset show_subset;
- ListStore list_store;
+ HashMap<Contact,ContactData> contacts;
HashSet<Contact> hidden_contacts;
+
string []? filter_values;
- int custom_visible_count;
- ContactData suggestions_header_data;
- ContactData padding_data;
- ContactData other_header_data;
+ private TextDisplay text_display;
public View (Store store, TextDisplay text_display = TextDisplay.PRESENCE) {
+ set_selection_mode (SelectionMode.BROWSE);
contacts_store = store;
hidden_contacts = new HashSet<Contact>();
show_subset = Subset.ALL;
+ this.text_display = text_display;
- list_store = new ListStore (2, typeof (Contact), typeof (ContactData *));
- suggestions_header_data = new ContactData ();
- suggestions_header_data.sort_prio = int.MAX;
- padding_data = new ContactData ();
- padding_data.sort_prio = 1;
-
- other_header_data = new ContactData ();
- other_header_data.sort_prio = -1;
-
- list_store.set_sort_func (0, (model, iter_a, iter_b) => {
- ContactData *aa, bb;
- model.get (iter_a, 1, out aa);
- model.get (iter_b, 1, out bb);
+ contacts = new HashMap<Contact,ContactData> ();
- return compare_data (aa, bb);
+ this.set_sort_func ((widget_a, widget_b) => {
+ var a = widget_a.get_data<ContactData> ("data");
+ var b = widget_b.get_data<ContactData> ("data");
+ return compare_data (a, b);
});
- list_store.set_sort_column_id (0, SortType.ASCENDING);
+ this.set_filter_func (filter);
+ this.set_separator_funcs (update_separator);
contacts_store.added.connect (contact_added_cb);
contacts_store.removed.connect (contact_removed_cb);
contacts_store.changed.connect (contact_changed_cb);
foreach (var c in store.get_contacts ())
contact_added_cb (store, c);
-
- init_view (text_display);
}
private int compare_data (ContactData a_data, ContactData b_data) {
@@ -87,16 +88,13 @@ public class Contacts.View : TreeView {
if (a_prio < b_prio)
return 1;
- var a = a_data.contact;
- var b = b_data.contact;
-
- if (is_set (a.display_name) && is_set (b.display_name))
- return a.display_name.collate (b.display_name);
+ if (is_set (a_data.display_name) && is_set (b_data.display_name))
+ return a_data.display_name.collate (b_data.display_name);
// Sort empty names last
- if (is_set (a.display_name))
+ if (is_set (a_data.display_name))
return -1;
- if (is_set (b.display_name))
+ if (is_set (b_data.display_name))
return 1;
return 0;
@@ -111,425 +109,208 @@ public class Contacts.View : TreeView {
}
/* The hardcoded prio if set, otherwise 0 for the
- main/combined group, or -2 for the separated other group */
+ main/combined group, or -1 for the separated other group */
private int get_sort_prio (ContactData *data) {
if (data->sort_prio != 0)
return data->sort_prio;
if (is_other (data))
- return -2;
+ return -1;
return 0;
}
- public string get_header_text (TreeIter iter) {
- ContactData *data;
- list_store.get (iter, 1, out data);
- if (data == suggestions_header_data) {
- /* Translators: This is the header for the list of suggested contacts to
- link to the current contact */
- return ngettext ("Suggestion", "Suggestions", custom_visible_count);
- }
- if (data == other_header_data) {
- /* Translators: This is the header for the list of suggested contacts to
- link to the current contact */
- return _("Other Contacts");
- }
- return "";
- }
-
public void set_show_subset (Subset subset) {
show_subset = subset;
-
- bool new_visible = show_subset == Subset.ALL_SEPARATED;
- if (new_visible && !other_header_data.visible) {
- other_header_data.visible = true;
- list_store.append (out other_header_data.iter);
- list_store.set (other_header_data.iter, 1, other_header_data);
- }
- if (!new_visible && other_header_data.visible) {
- other_header_data.visible = false;
- list_store.remove (other_header_data.iter);
- }
-
+ update_all_filtered ();
refilter ();
+ resort ();
}
public void set_custom_sort_prio (Contact c, int prio) {
/* We use negative prios internally */
assert (prio >= 0);
- var data = lookup_data (c);
-
+ var data = contacts.get (c);
if (data == null)
return;
-
- // We insert a priority between 0 and 1 for the padding
- if (prio > 0)
- prio += 1;
data.sort_prio = prio;
- contact_changed_cb (contacts_store, c);
-
- if (data.visible) {
- if (prio > 0) {
- if (custom_visible_count++ == 0)
- add_custom_headers ();
- } else {
- if (custom_visible_count-- == 1)
- remove_custom_headers ();
- }
- }
+ child_changed (data.grid);
+ }
+
+ public void hide_contact (Contact contact) {
+ hidden_contacts.add (contact);
+ update_all_filtered ();
+ refilter ();
+ }
+
+ public void set_filter_values (string []? values) {
+ filter_values = values;
+ update_all_filtered ();
+ refilter ();
}
- private bool apply_filter (Contact contact) {
- if (contact.is_hidden)
+ private bool calculate_filtered (Contact c) {
+ if (c.is_hidden)
return false;
- if (contact in hidden_contacts)
+ if (c in hidden_contacts)
return false;
if ((show_subset == Subset.MAIN &&
- !contact.is_main) ||
+ !c.is_main) ||
(show_subset == Subset.OTHER &&
- contact.is_main))
+ c.is_main))
return false;
if (filter_values == null || filter_values.length == 0)
return true;
- return contact.contains_strings (filter_values);
- }
-
- public bool is_first (TreeIter iter) {
- ContactData *data;
- list_store.get (iter, 1, out data);
- if (data != null)
- return data->is_first;
- return false;
- }
-
- private ContactData? get_previous (ContactData data) {
- ContactData *previous = null;
- TreeIter iter = data.iter;
- if (list_store.iter_previous (ref iter))
- list_store.get (iter, 1, out previous);
- return previous;
- }
-
- private ContactData? get_next (ContactData data) {
- ContactData *next = null;
- TreeIter iter = data.iter;
- if (list_store.iter_next (ref iter))
- list_store.get (iter, 1, out next);
- return next;
- }
-
- private void row_changed_no_resort (ContactData data) {
- var path = list_store.get_path (data.iter);
- list_store.row_changed (path, data.iter);
- }
-
- private void row_changed_resort (ContactData data) {
- list_store.set (data.iter, 0, data.contact);
- }
-
- private bool update_is_first (ContactData data, ContactData? previous) {
- bool old_is_first = data.is_first;
-
- bool is_custom = data.sort_prio != 0;
- bool previous_is_custom = previous != null && (previous.sort_prio != 0) ;
-
- if (is_custom) {
- data.is_first = false;
- } else if (previous != null && !previous_is_custom) {
- unichar previous_initial = previous.contact.initial_letter;
- unichar initial = data.contact.initial_letter;
- data.is_first = previous_initial != initial;
- } else {
- data.is_first = true;
- }
-
- if (old_is_first != data.is_first) {
- row_changed_no_resort (data);
- return true;
- }
-
- return false;
+ return c.contains_strings (filter_values);
}
- private void add_custom_headers () {
- suggestions_header_data.visible = true;
- list_store.append (out suggestions_header_data.iter);
- list_store.set (suggestions_header_data.iter, 1, suggestions_header_data);
- padding_data.visible = true;
- list_store.append (out padding_data.iter);
- list_store.set (padding_data.iter, 1, padding_data);
- }
+ private void update_data (ContactData data) {
+ var c = data.contact;
+ data.display_name = c.display_name;
+ data.initial_letter = c.initial_letter;
+ data.filtered = calculate_filtered (c);
- private void remove_custom_headers () {
- suggestions_header_data.visible = false;
- list_store.remove (suggestions_header_data.iter);
- padding_data.visible = false;
- list_store.remove (padding_data.iter);
+ data.label.set_markup ("<span font='16px'>" + data.display_name + "</span>");
+ data.image_frame.set_image (c.individual, c);
}
- private void add_to_model (ContactData data) {
- list_store.append (out data.iter);
- list_store.set (data.iter, 0, data.contact, 1, data);
-
- if (data.sort_prio > 0) {
- if (custom_visible_count++ == 0)
- add_custom_headers ();
- }
-
- if (update_is_first (data, get_previous (data)) && data.is_first) {
- /* The newly added row is first, the next one might not be anymore */
- var next = get_next (data);
- if (next != null)
- update_is_first (next, data);
- }
- }
-
- private void remove_from_model (ContactData data) {
- if (data.sort_prio > 0) {
- if (custom_visible_count-- == 1)
- remove_custom_headers ();
+ private void update_all_filtered () {
+ foreach (var data in contacts.values) {
+ data.filtered = calculate_filtered (data.contact);
}
-
- ContactData? next = null;
- if (data.is_first)
- next = get_next (data);
-
- list_store.remove (data.iter);
- data.is_first = false;
-
- if (next != null)
- update_is_first (next, get_previous (next));
}
- private void update_visible (ContactData data) {
- bool was_visible = data.visible;
- data.visible = apply_filter (data.contact);
-
- if (was_visible && !data.visible)
- remove_from_model (data);
-
- if (!was_visible && data.visible)
- add_to_model (data);
+ private void contact_changed_cb (Store store, Contact c) {
+ var data = contacts.get (c);
+ update_data (data);
+ child_changed (data.grid);
}
- private void refilter () {
- foreach (var c in contacts_store.get_contacts ()) {
- update_visible (lookup_data (c));
+ private void contact_added_cb (Store store, Contact c) {
+ var data = new ContactData();
+ data.contact = c;
+ data.grid = new Grid ();
+ data.grid.margin = 12;
+ data.grid.set_column_spacing (10);
+ data.image_frame = new ContactFrame (Contact.SMALL_AVATAR_SIZE);
+ data.label = new Label ("");
+ data.label.set_ellipsize (Pango.EllipsizeMode.END);
+ data.label.set_valign (Align.START);
+ data.label.set_halign (Align.START);
+
+ data.grid.attach (data.image_frame, 0, 0, 1, 2);
+ data.grid.attach (data.label, 1, 0, 1, 1);
+
+ if (text_display == TextDisplay.PRESENCE) {
+ var merged_presence = c.create_merged_presence_widget ();
+ merged_presence.set_halign (Align.START);
+ merged_presence.set_valign (Align.END);
+ merged_presence.set_vexpand (false);
+ merged_presence.set_margin_bottom (4);
+
+ data.grid.attach (merged_presence, 1, 1, 1, 1);
}
- }
-
- public void hide_contact (Contact contact) {
- hidden_contacts.add (contact);
- refilter ();
- }
-
- public void set_filter_values (string []? values) {
- filter_values = values;
- refilter ();
- }
- private void contact_changed_cb (Store store, Contact c) {
- ContactData data = lookup_data (c);
-
- bool was_visible = data.visible;
-
- ContactData? next = null;
- if (data.visible)
- next = get_next (data);
-
- update_visible (data);
-
- if (was_visible && data.visible) {
- /* We just moved position in the list while visible */
+ if (text_display == TextDisplay.STORES) {
+ var stores = new Label ("");
+ stores.set_markup (Markup.printf_escaped ("<span font='12px'>%s</span>",
+ c.format_persona_stores ()));
- row_changed_resort (data);
-
- /* Update the is_first on the previous next row */
- if (next != null)
- update_is_first (next, get_previous (next));
-
- /* Update the is_first on the new next row */
- next = get_next (data);
- if (next != null)
- update_is_first (next, data);
+ stores.set_ellipsize (Pango.EllipsizeMode.END);
+ stores.set_halign (Align.START);
+ data.grid.attach (stores, 1, 1, 1, 1);
}
- }
- private ContactData lookup_data (Contact c) {
- return c.lookup<ContactData> (this);
- }
+ update_data (data);
- private void contact_added_cb (Store store, Contact c) {
- ContactData data = new ContactData();
- data.contact = c;
- data.visible = false;
-
- c.set_lookup (this, data);
-
- update_visible (data);
+ data.grid.set_data<ContactData> ("data", data);
+ data.grid.show_all ();
+ contacts.set (c, data);
+ this.add (data.grid);
}
private void contact_removed_cb (Store store, Contact c) {
- var data = lookup_data (c);
-
- if (data.visible)
- remove_from_model (data);
-
- c.remove_lookup<ContactData> (this);
+ var data = contacts.get (c);
+ data.grid.destroy ();
+ data.label.destroy ();
+ data.image_frame.destroy ();
+ contacts.unset (c);
}
- public bool lookup_iter (Contact c, out TreeIter iter) {
- var data = lookup_data (c);
- iter = data.iter;
- return data.visible;
+ public override void child_selected (Widget? child) {
+ var data = child.get_data<ContactData> ("data");
+ var contact = data != null ? data.contact : null;
+ selection_changed (contact);
+ if (contact != null)
+ contact.fetch_contact_info ();
}
+ private bool filter (Widget child) {
+ var data = child.get_data<ContactData> ("data");
-
- private CellRendererShape shape;
- public enum TextDisplay {
- NONE,
- PRESENCE,
- STORES
+ return data.filtered;
}
- private TextDisplay text_display;
-
- public signal void selection_changed (Contact? contact);
-
- private void init_view (TextDisplay text_display) {
- this.text_display = text_display;
-
- set_model (list_store);
- set_headers_visible (false);
-
- var row_padding = 12;
-
- var selection = get_selection ();
- selection.set_mode (SelectionMode.BROWSE);
- selection.set_select_function ( (selection, model, path, path_currently_selected) => {
- Contact contact;
- TreeIter iter;
- model.get_iter (out iter, path);
- model.get (iter, 0, out contact);
- return contact != null;
- });
- selection.changed.connect (contacts_selection_changed);
-
- var column = new TreeViewColumn ();
- column.set_spacing (8);
-
- var icon = new CellRendererPixbuf ();
- icon.set_padding (0, row_padding);
- icon.xalign = 1.0f;
- icon.yalign = 0.0f;
- icon.width = Contact.SMALL_AVATAR_SIZE + 12;
- column.pack_start (icon, false);
- column.set_cell_data_func (icon, (column, cell, model, iter) => {
- Contact contact;
-
- model.get (iter, 0, out contact);
-
- if (contact == null) {
- cell.set ("pixbuf", null);
- cell.visible = false;
- return;
- }
- cell.visible = true;
-
- if (contact != null)
- cell.set ("pixbuf", contact.small_avatar);
- else
- cell.set ("pixbuf", null);
- });
-
- shape = new CellRendererShape ();
- shape.set_padding (0, row_padding);
-
- Pango.cairo_context_set_shape_renderer (get_pango_context (), shape.render_shape);
-
- column.pack_start (shape, true);
- column.set_cell_data_func (shape, (column, cell, model, iter) => {
- Contact contact;
-
- model.get (iter, 0, out contact);
-
- if (contact == null) {
- cell.visible = false;
- return;
- }
- cell.visible = true;
-
- var name = contact.display_name;
- switch (text_display) {
- default:
- case TextDisplay.NONE:
- cell.set ("name", name,
- "show_presence", false,
- "message", "");
- break;
- case TextDisplay.PRESENCE:
- cell.set ("name", name,
- "show_presence", true,
- "presence", contact.presence_type,
- "message", contact.presence_message,
- "is_phone", contact.is_phone);
- break;
- case TextDisplay.STORES:
- string stores = contact.format_persona_stores ();
- cell.set ("name", name,
- "show_presence", false,
- "message", stores);
- break;
- }
- });
- var text = new CellRendererText ();
- text.set_alignment (0, 0);
- column.pack_start (text, true);
- text.set ("weight", Pango.Weight.BOLD);
- column.set_cell_data_func (text, (column, cell, model, iter) => {
- Contact contact;
-
- model.get (iter, 0, out contact);
- cell.visible = contact == null;
- if (cell.visible) {
- string header = get_header_text (iter);
- cell.set ("text", header);
- if (header == "")
- cell.height = 6; // PADDING
- else
- cell.height = -1;
- }
- });
+ private void update_separator (ref Widget? separator,
+ Widget widget,
+ Widget? before_widget) {
+ var w_data = widget.get_data<ContactData> ("data");
+ ContactData? before_data = null;
+ if (before_widget != null)
+ before_data = before_widget.get_data<ContactData> ("data");
- append_column (column);
- }
+ if (before_data == null && w_data.sort_prio > 0) {
+ if (separator == null ||
+ !(separator.get_data<bool> ("contacts-suggestions-header"))) {
+ var l = new Label ("");
+ l.set_data ("contacts-suggestions-header", true);
+ l.set_markup (Markup.printf_escaped ("<b>%s</b>", _("Suggestions")));
+ l.set_halign (Align.START);
+ separator = l;
+ }
+ return;
+ }
- private void contacts_selection_changed (TreeSelection selection) {
- TreeIter iter;
- TreeModel model;
+ if (before_data != null && before_data.sort_prio > 0 &&
+ w_data.sort_prio == 0) {
+ if (separator == null ||
+ !(separator.get_data<bool> ("contacts-rest-header"))) {
+ var l = new Label ("");
+ l.set_data ("contacts-rest-header", true);
+ l.set_halign (Align.START);
+ separator = l;
+ }
+ return;
+ }
- Contact? contact = null;
- if (selection.get_selected (out model, out iter)) {
- model.get (iter, 0, out contact);
+ if (is_other (w_data) &&
+ (before_data == null || !is_other (before_data))) {
+ if (separator == null ||
+ !(separator.get_data<bool> ("contacts-other-header"))) {
+ var l = new Label ("");
+ l.set_data ("contacts-other-header", true);
+ l.set_markup (Markup.printf_escaped ("<b>%s</b>", _("Other Contacts")));
+ l.set_halign (Align.START);
+ separator = l;
+ }
+ return;
}
- selection_changed (contact);
- if (contact != null)
- contact.fetch_contact_info ();
+ if (before_data != null &&
+ w_data.initial_letter != before_data.initial_letter) {
+ if (separator == null || !(separator is Separator))
+ separator = new Separator (Orientation.HORIZONTAL);
+ return;
+ }
+ separator = null;
}
public void select_contact (Contact contact) {
- TreeIter iter;
- if (lookup_iter (contact, out iter)) {
- get_selection ().select_iter (iter);
- scroll_to_cell (list_store.get_path (iter),
- null, true, 0.0f, 0.0f);
- }
+ var data = contacts.get (contact);
+ select_child (data.grid);
}
}
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]