Merge lp:~victored/granite/source-list-tweaks into lp:~elementary-pantheon/granite/granite

Proposed by Victor Martinez
Status: Merged
Approved by: Cody Garver
Approved revision: 602
Merged at revision: 659
Proposed branch: lp:~victored/granite/source-list-tweaks
Merge into: lp:~elementary-pantheon/granite/granite
Diff against target: 300 lines (+85/-44)
2 files modified
demo/GraniteDemo.vala (+14/-2)
lib/Widgets/SourceList.vala (+71/-42)
To merge this branch: bzr merge lp:~victored/granite/source-list-tweaks
Reviewer Review Type Date Requested Status
Julien Spautz Pending
elementary Pantheon team Pending
Review via email: mp+196056@code.launchpad.net

Commit message

[SourceList] [API] Allow every ExpandableItem to define its own sorting method.

The previous global sorting API has been deprecated but it's still supported, so sorting is done on a per-expandable-item basis whenever the global sort function is set to "null".

Fixes lp:1188694 and lp:1182400.

Description of the change

[SourceList] [API] Allow every ExpandableItem to define its own sorting method.

The previous global sorting API has been deprecated but it's still supported, so sorting is done on a per-expandable-item basis whenever the global sort function is set to "null".

Fixes lp:1188694 and lp:1182400.

To post a comment you must log in.
Revision history for this message
Julián Unrrein (junrrein) wrote :

Commit 600 looks good to me code-wise, and I confirm it fixes bug #1182400 using Julien's example in the bug report.

Revision history for this message
Julián Unrrein (junrrein) wrote :

Commit 601 looks good to me too.

Revision history for this message
Julián Unrrein (junrrein) wrote :

Commit 602 looks good to me code-wise, but you'd better ask Julien for both a test case and to confirm this API works well for him.

Revision history for this message
Victor Martinez (victored) wrote :

Thank you for the review Julian.

If you have any objection or suggestion for the API proposed here, please let me know before it gets merged.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'demo/GraniteDemo.vala'
2--- demo/GraniteDemo.vala 2013-09-16 23:14:20 +0000
3+++ demo/GraniteDemo.vala 2013-11-21 05:06:14 +0000
4@@ -51,6 +51,18 @@
5 }
6 }
7
8+ private class SourceListExpandableItem : Granite.Widgets.SourceList.ExpandableItem {
9+ public SourceListExpandableItem (string name) {
10+ base (name);
11+ }
12+
13+ public override int compare (Granite.Widgets.SourceList.Item a,
14+ Granite.Widgets.SourceList.Item b)
15+ {
16+ return strcmp (a.name, b.name);
17+ }
18+ }
19+
20 /**
21 * SourceList item. It stores the number of the corresponding page in the notebook widget.
22 */
23@@ -134,8 +146,8 @@
24 });
25
26 // Main sidebar categories
27- var widgets_category = new Granite.Widgets.SourceList.ExpandableItem ("Widgets");
28- var services_category = new Granite.Widgets.SourceList.ExpandableItem ("Services");
29+ var widgets_category = new SourceListExpandableItem ("Widgets");
30+ var services_category = new SourceListExpandableItem ("Services");
31
32 // Add and expand categories
33 sidebar.root.add (widgets_category);
34
35=== modified file 'lib/Widgets/SourceList.vala'
36--- lib/Widgets/SourceList.vala 2013-08-16 22:19:48 +0000
37+++ lib/Widgets/SourceList.vala 2013-11-21 05:06:14 +0000
38@@ -139,7 +139,6 @@
39 * - icon | DataModel::on_item_prop_changed | Tree::icon_cell_data_func
40 * - activatable | Same as @icon | Same as @icon
41 * + ExpandableItem | |
42- * - no_caption | DataModel::on_item_prop_changed | Tree::name_cell_data_func
43 * - collapsible | DataModel::on_item_prop_changed | Tree::update_expansion
44 * | | Tree::expander_cell_data_func
45 * - expanded | Same as @collapsible | Same as @collapsible
46@@ -391,7 +390,7 @@
47 * @since 0.2
48 */
49 public uint n_children {
50- get { return children_set.size; }
51+ get { return children_list.size; }
52 }
53
54 /**
55@@ -401,17 +400,11 @@
56 */
57 public Gee.Collection<Item> children {
58 owned get {
59- // Create a copy of the children so that it's safe to iterate it
60- // (e.g. by using foreach) while removing items. See clear() for
61- // an example of such case.
62- var copy = new Gee.LinkedList<Item> ();
63- foreach (var item in children_set)
64- copy.add (item);
65- return copy;
66+ return children_list.read_only_view;
67 }
68 }
69
70- private Gee.Set<Item> children_set = new Gee.HashSet<Item> ();
71+ private Gee.Collection<Item> children_list = new Gee.ArrayList<Item> ();
72
73 /**
74 * Creates a new {@link Granite.Widgets.SourceList.ExpandableItem}
75@@ -426,6 +419,27 @@
76 }
77
78 /**
79+ * Should return a negative integer, zero, or a positive integer if ''a'' sorts //before//
80+ * ''b'', ''a'' sorts //with// ''b'', or ''a'' sorts //after// ''b'' respectively. If two
81+ * items compare as equal, their order in the sorted source list is undefined.
82+ *
83+ * In order to ensure that the source list behaves as expected, this method must define a
84+ * partial order on the source list tree; i.e. it must be reflexive, antisymmetric and
85+ * transitive.
86+ *
87+ * (Same description as {@link Gtk.TreeIterCompareFunc}.)
88+ *
89+ * @param a First item.
90+ * @param b Second item.
91+ * @return A //negative// integer if //a// sorts after //b//, //zero// if //a// equals //b//,
92+ * or a //positive// integer if //a// sorts before //b//.
93+ * @since 0.2
94+ */
95+ public virtual int compare (Item a, Item b) {
96+ return 0;
97+ }
98+
99+ /**
100 * Checks whether the item contains the specified child.
101 *
102 * This method only considers the item's immediate children.
103@@ -435,7 +449,7 @@
104 * @since 0.2
105 */
106 public bool contains (Item item) {
107- return item in children_set;
108+ return item in children_list;
109 }
110
111 /**
112@@ -458,9 +472,9 @@
113 * @see Granite.Widgets.SourceList.ExpandableItem.remove
114 * @since 0.2
115 */
116- public void add (Item item) requires (item.parent == null) requires (!contains (item)) {
117+ public void add (Item item) requires (item.parent == null) {
118 item.parent = this;
119- children_set.add (item);
120+ children_list.add (item);
121 child_added (item);
122 }
123
124@@ -478,8 +492,8 @@
125 * @see Granite.Widgets.SourceList.ExpandableItem.clear
126 * @since 0.2
127 */
128- public void remove (Item item) requires (item.parent == this) requires (contains (item)) {
129- children_set.remove (item);
130+ public void remove (Item item) requires (item.parent == this) {
131+ children_list.remove (item);
132 child_removed (item);
133 item.parent = null;
134 }
135@@ -493,7 +507,12 @@
136 * @since 0.2
137 */
138 public void clear () {
139- foreach (var item in children)
140+ // Create a copy of the children so that it's safe to iterate it
141+ // (e.g. by using foreach) while removing items
142+ var children_list_copy = new Gee.ArrayList<Item> ();
143+ children_list_copy.add_all (children_list);
144+
145+ foreach (var item in children_list_copy)
146 remove (item);
147 }
148
149@@ -675,11 +694,6 @@
150 }
151 }
152
153- private enum SortColumn {
154- UNSORTED = Gtk.SortColumn.UNSORTED,
155- SORTED = Gtk.SortColumn.DEFAULT + 1
156- }
157-
158 public signal void item_updated (Item item);
159
160 /**
161@@ -692,7 +706,7 @@
162 get { return sort_dir; }
163 set {
164 sort_dir = value;
165- child_tree.set_sort_column_id (this.sort_column, sort_dir);
166+ resort ();
167 }
168 }
169
170@@ -731,12 +745,15 @@
171 private Gtk.TreeStore child_tree;
172 private SourceList.SortFunc? sort_func;
173 private unowned SourceList.VisibleFunc? filter_func;
174- private SortColumn sort_column = SortColumn.UNSORTED;
175
176 public DataModel () {
177 var child_tree = new Gtk.TreeStore (Column.N_COLUMNS, Column.ITEM.type ());
178 Object (child_model: child_tree, virtual_root: null);
179 this.child_tree = child_tree;
180+
181+ child_tree.set_default_sort_func (child_model_sort_func);
182+ resort ();
183+
184 set_visible_func (filter_visible_func);
185 }
186
187@@ -967,10 +984,7 @@
188 */
189 public void set_sort_func (owned SourceList.SortFunc? sort_func) {
190 this.sort_func = (owned) sort_func;
191- this.sort_column = this.sort_func != null ? SortColumn.SORTED : SortColumn.UNSORTED;
192-
193- child_tree.set_sort_func (SortColumn.SORTED, child_model_sort_func);
194- sort_direction = sort_dir;
195+ resort ();
196 }
197
198 /**
199@@ -1013,20 +1027,31 @@
200 return path.get_depth () == 1;
201 }
202
203- /**
204- * Actual sort function. It simply returns zero if sort_func is null.
205- */
206+ private void resort () {
207+ child_tree.set_sort_column_id (Gtk.SortColumn.UNSORTED, sort_direction);
208+ child_tree.set_sort_column_id (Gtk.SortColumn.DEFAULT, sort_direction);
209+ }
210+
211 private int child_model_sort_func (Gtk.TreeModel model, Gtk.TreeIter a, Gtk.TreeIter b) {
212- // Return zero by default, since a different value would not be reflexive nor symmetric when
213- // sort_func is null.
214 int sort = 0;
215
216 Item? item_a, item_b;
217 child_tree.get (a, Column.ITEM, out item_a, -1);
218 child_tree.get (b, Column.ITEM, out item_b, -1);
219
220- if (sort_func != null && item_a != null && item_b != null)
221- sort = sort_func (item_a, item_b);
222+ // If the sort function is not null use old sorting API. Otherwise, use each
223+ // item's compare() method.
224+ if (sort_func != null) {
225+ if (item_a != null && item_b != null)
226+ sort = sort_func (item_a, item_b);
227+ } else {
228+ // code should only compare items on same hierarchy level
229+ assert (item_a.parent == item_b.parent);
230+
231+ var parent = item_a.parent;
232+ if (parent != null)
233+ sort = parent.compare (item_a, item_b);
234+ }
235
236 return sort;
237 }
238@@ -1063,7 +1088,7 @@
239 if (item != null) {
240 item_visible = item.visible;
241
242- // If the item is a category, also query the number of visible child items
243+ // If the item is a category, also query the number of visible children
244 // because empty categories should not be displayed.
245 var expandable = item as ExpandableItem;
246 if (expandable != null && child_tree.iter_depth (iter) == 0) {
247@@ -1936,6 +1961,7 @@
248 * or a //positive// integer if //a// sorts before //b//.
249 * @since 0.2
250 */
251+ [Deprecated (replacement = "ExpandableItem.compare", since = "0.2")]
252 public delegate int SortFunc (Item a, Item b);
253
254 /**
255@@ -1970,7 +1996,10 @@
256 *
257 * @since 0.2
258 */
259- public ExpandableItem root { get; private set; default = new ExpandableItem (); }
260+ public ExpandableItem root {
261+ get { return data_model.root; }
262+ set { data_model.root = value; }
263+ }
264
265 /**
266 * The current selected item.
267@@ -2022,20 +2051,19 @@
268 }
269
270 private Tree tree;
271- private DataModel data_model { get { return tree.data_model; } }
272+ private DataModel data_model = new DataModel ();
273
274 /**
275 * Creates a new {@link Granite.Widgets.SourceList}.
276 *
277- * @return (transfer full) a new {@link Granite.Widgets.SourceList}.
278+ * @return A new {@link Granite.Widgets.SourceList}.
279 * @since 0.2
280 */
281- public SourceList () {
282- var model = new DataModel ();
283- model.root = root;
284+ public SourceList (ExpandableItem root = new ExpandableItem ()) {
285+ this.root = root;
286
287 push_composite_child ();
288- tree = new Tree (model);
289+ tree = new Tree (data_model);
290 tree.set_composite_name ("treeview");
291 pop_composite_child ();
292
293@@ -2064,6 +2092,7 @@
294 * @see Granite.Widgets.SourceList.SortFunc
295 * @since 0.2
296 */
297+ [Deprecated (replacement = "ExpandableItem.compare", since = "0.2")]
298 public void set_sort_func (owned SortFunc? sort_func) {
299 data_model.set_sort_func ((owned) sort_func);
300 }

Subscribers

People subscribed via source and target branches