Merge lp:~teemperor/granite/table-view into lp:~elementary-pantheon/granite/granite

Proposed by Raphael Isemann
Status: Work in progress
Proposed branch: lp:~teemperor/granite/table-view
Merge into: lp:~elementary-pantheon/granite/granite
Diff against target: 454 lines (+407/-1)
4 files modified
demo/GraniteDemo.vala (+42/-1)
lib/CMakeLists.txt (+2/-0)
lib/Views/Table.vala (+333/-0)
lib/Views/TableItem.vala (+30/-0)
To merge this branch: bzr merge lp:~teemperor/granite/table-view
Reviewer Review Type Date Requested Status
xapantu (community) Needs Information
Review via email: mp+240380@code.launchpad.net

Description of the change

We have quite a lot of implementations of widgets that do nothing more than displaying a list of items. A good example is the keyboard-plug which has at least 5 of those widgets and all those widgets are reimplemented from scratch.

This is especially annoying since on each review you have to recheck if the lists work correctly to figure out minor problems as seen here: https://code.launchpad.net/~julien-spautz/switchboard-plug-keyboard/custom-shortcuts/+merge/212343

This branch introduces a widget that displays a Gee.List and in a MVC-compatible way.

The advantages are:

* Better performance as we can fine-tune this widget to not fire item_changed events if they are not necessary.

* Less code and less time spent with testing list-implementations!

* Design-changes regarding lists can be implemented easier by just changing the code here in granite. This also allows that all third-party developers are automatically updated to the new design if they use this widget.

See the granite-demo under Static notebook -> "Table"-tab and look at stdout to see it in action.

To post a comment you must log in.
lp:~teemperor/granite/table-view updated
806. By Raphael Isemann

We can now select items from the code

Revision history for this message
xapantu (xapantu) wrote :

Hum, in what way is that better than the classic MVC approach of a GtkListViem/GtkTreeView with a custom renderer?

review: Needs Information

Unmerged revisions

806. By Raphael Isemann

We can now select items from the code

805. By Raphael Isemann

Everything works as expected, but we are still firing too many item-changes

804. By Raphael Isemann

Implemented up-down (only for the underlying list)

803. By Raphael Isemann

We only accept lists now

802. By Raphael Isemann

First version of the new Table-class

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'demo/GraniteDemo.vala'
--- demo/GraniteDemo.vala 2014-05-12 02:42:17 +0000
+++ demo/GraniteDemo.vala 2014-11-02 22:04:23 +0000
@@ -152,6 +152,22 @@
152 }152 }
153 }153 }
154154
155 private class Person: GLib.Object, Granite.Widgets.Views.TableItem {
156
157 public string name { get; private set; }
158 public string address { get; private set; }
159
160 public Person(string name, string address) {
161 this.name = name;
162 this.address = address;
163 }
164
165 public string[] get_values () {
166 string[] result = {name, address};
167 return result;
168 }
169 }
170
155 private Gtk.Grid main_layout; // outer-most container171 private Gtk.Grid main_layout; // outer-most container
156 private Granite.Widgets.ModeButton mode_button;172 private Granite.Widgets.ModeButton mode_button;
157 private int dark_mode_index;173 private int dark_mode_index;
@@ -283,6 +299,31 @@
283 staticnotebook.append_page (get_overlay_bar_widget (), new Gtk.Label ("Overlay Bar"));299 staticnotebook.append_page (get_overlay_bar_widget (), new Gtk.Label ("Overlay Bar"));
284 staticnotebook.append_page (new Gtk.Label ("Page 3"), new Gtk.Label ("Page 3"));300 staticnotebook.append_page (new Gtk.Label ("Page 3"), new Gtk.Label ("Page 3"));
285301
302 var test_table = new Granite.Widgets.Views.Table (new string[] {"Person", "Address"});
303
304 var persons = new Gee.ArrayList<Person>();
305 persons.add (new Person("Dan", "elementary Headquarter"));
306 persons.add (new Person("Cody", "Launchpad"));
307 persons.add (new Person("Sergey", "In the forest"));
308
309 test_table.show_list (persons);
310 test_table.selected_item_changed.connect ((person) => {
311 print ((person as Person).name + " got selected!\n");
312 });
313
314 test_table.add_item_clicked.connect (() => {
315 test_table.append_item (
316 new Person(
317 "New User" + Random.next_int ().to_string (),
318 "Limbo"));
319 });
320
321 // Select cody
322 test_table.selected_item = persons.get (1);
323
324 staticnotebook.append_page (test_table, new Gtk.Label ("Table"));
325
326
286 staticnotebook.page_changed.connect (() => {327 staticnotebook.page_changed.connect (() => {
287 pageone.set_text ("Page changed");328 pageone.set_text ("Page changed");
288 });329 });
@@ -551,4 +592,4 @@
551592
552 return application.run (args);593 return application.run (args);
553 }594 }
554}595}
555\ No newline at end of file596\ No newline at end of file
556597
=== modified file 'lib/CMakeLists.txt'
--- lib/CMakeLists.txt 2014-01-18 21:47:46 +0000
+++ lib/CMakeLists.txt 2014-11-02 22:04:23 +0000
@@ -19,6 +19,8 @@
19 Services/ContractorProxy.vala19 Services/ContractorProxy.vala
20 Services/IconFactory.vala20 Services/IconFactory.vala
21 Services/SimpleCommand.vala21 Services/SimpleCommand.vala
22 Views/Table.vala
23 Views/TableItem.vala
22 Widgets/Utils.vala24 Widgets/Utils.vala
23 Widgets/WrapLabel.vala25 Widgets/WrapLabel.vala
24 Widgets/AboutDialog.vala26 Widgets/AboutDialog.vala
2527
=== added directory 'lib/Views'
=== added file 'lib/Views/Table.vala'
--- lib/Views/Table.vala 1970-01-01 00:00:00 +0000
+++ lib/Views/Table.vala 2014-11-02 22:04:23 +0000
@@ -0,0 +1,333 @@
1/***
2 Copyright (C) 2014 Raphael Isemann <raphael@elementaryos.org>
3
4 This program or library is free software; you can redistribute it
5 and/or modify it under the terms of the GNU Lesser General Public
6 License as published by the Free Software Foundation; either
7 version 3 of the License, or (at your option) any later version.
8
9 This library is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 Lesser General Public License for more details.
13
14 You should have received a copy of the GNU Lesser General
15 Public License along with this library; if not, write to the
16 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
17 Boston, MA 02110-1301 USA.
18***/
19
20using Gtk;
21using Gdk;
22
23namespace Granite.Widgets.Views {
24
25 public class Table : Gtk.Grid {
26
27 public signal void selected_item_changed (TableItem selected);
28 public signal void add_item_clicked ();
29
30 bool _allows_remove = true;
31 public bool allows_remove {
32 get {
33 return _allows_remove;
34 }
35 set {
36 _allows_remove = value;
37 if (value) {
38 remove_button.show_all ();
39 } else {
40 remove_button.hide ();
41 }
42 update_toolbar_visibility ();
43 }
44 }
45
46
47 bool _allows_add = true;
48 public bool allows_add {
49 get {
50 return _allows_add;
51 }
52 set {
53 _allows_add = value;
54 if (value) {
55 add_button.show_all ();
56 } else {
57 add_button.hide ();
58 }
59 update_toolbar_visibility ();
60 }
61 }
62
63 bool _allows_reordering = true;
64 public bool allows_reordering {
65 get {
66 return _allows_reordering;
67 }
68 set {
69 _allows_reordering = value;
70 if (value) {
71 up_button.show_all ();
72 down_button.show_all ();
73 } else {
74 up_button.hide ();
75 down_button.hide ();
76 }
77 update_toolbar_visibility ();
78 }
79 }
80
81 TableItem? _selected_item = null;
82 public TableItem? selected_item {
83 get {
84 return selected_item;
85 }
86 set {
87 _selected_item = null;
88 for (int i = 0; i < item_list.size; i++) {
89 if (item_list.get (i) == value) {
90 select (i);
91 break;
92 }
93 }
94 }
95 }
96
97 Gtk.TreeView tree_view;
98 ListStore list_model;
99 Type[] column_types;
100 int columns;
101
102 Gee.List<TableItem> item_list;
103
104 // true if we are currently in progress of changing the internal list
105 // This is necessary to prevent multiple
106 bool list_is_changing = false;
107
108 Gtk.Toolbar toolbar;
109 Gtk.ToolButton up_button;
110 Gtk.ToolButton down_button;
111 Gtk.ToolButton add_button;
112 Gtk.ToolButton remove_button;
113
114 public Table(string[] headers) {
115 tree_view = new Gtk.TreeView ();
116 columns = headers.length;
117 column_types = {};
118
119 foreach (var header in headers) {
120 column_types += typeof (string);
121 }
122
123 list_model = new ListStore.newv(column_types);
124 tree_view.set_model (list_model);
125
126 tree_view.cursor_changed.connect (cursor_changed_handler);
127
128 int attr_index = 0;
129 foreach (string header in headers) {
130 Gtk.CellRendererText cell = new Gtk.CellRendererText ();
131 tree_view.insert_column_with_attributes (-1, headers[attr_index], cell,
132 "text", attr_index);
133 attr_index++;
134 }
135
136 var scroll = new Gtk.ScrolledWindow(null, null);
137 scroll.hscrollbar_policy = Gtk.PolicyType.AUTOMATIC;
138 scroll.vscrollbar_policy = Gtk.PolicyType.AUTOMATIC;
139 scroll.shadow_type = Gtk.ShadowType.IN;
140 scroll.add (tree_view);
141 scroll.expand = true;
142
143 var toolbar = new Gtk.Toolbar();
144 toolbar.set_style(Gtk.ToolbarStyle.ICONS);
145 toolbar.set_icon_size(Gtk.IconSize.SMALL_TOOLBAR);
146 toolbar.set_show_arrow(false);
147 toolbar.hexpand = true;
148
149 scroll.get_style_context().set_junction_sides(Gtk.JunctionSides.BOTTOM);
150 toolbar.get_style_context().add_class(Gtk.STYLE_CLASS_INLINE_TOOLBAR);
151 toolbar.get_style_context().set_junction_sides(Gtk.JunctionSides.TOP);
152
153 add_button = new Gtk.ToolButton (null, _("Add…"));
154 remove_button = new Gtk.ToolButton (null, _("Remove"));
155 up_button = new Gtk.ToolButton (null, _("Move up"));
156 down_button = new Gtk.ToolButton (null, _("Move down"));
157
158 add_button.set_tooltip_text (_("Add…"));
159 remove_button.set_tooltip_text (_("Remove"));
160 up_button.set_tooltip_text (_("Move up"));
161 down_button.set_tooltip_text (_("Move down"));
162
163 add_button.set_icon_name ("list-add-symbolic");
164 remove_button.set_icon_name ("list-remove-symbolic");
165 up_button.set_icon_name ("go-up-symbolic");
166 down_button.set_icon_name ("go-down-symbolic");
167
168 up_button.sensitive = false;
169 down_button.sensitive = false;
170 remove_button.sensitive = false;
171
172 add_button.clicked.connect (() => {
173 add_item_clicked ();
174 });
175 remove_button.clicked.connect (remove_selected);
176 up_button.clicked.connect (up_pressed);
177 down_button.clicked.connect (down_pressed);
178
179 toolbar.insert (add_button, -1);
180 toolbar.insert (remove_button, -1);
181 toolbar.insert (up_button, -1);
182 toolbar.insert (down_button, -1);
183
184 this.attach (scroll, 0, 0, 1, 1);
185 this.attach (toolbar, 0, 1, 1, 1);
186 }
187
188 void fire_item_selected (TableItem item) {
189 if (!list_is_changing) {
190 _selected_item = item;
191 selected_item_changed (item);
192 }
193 }
194
195 void select (int index) {
196 var path = new TreePath.from_indices (index);
197 tree_view.set_cursor (path, null, false);
198 }
199
200 void remove_item (int index) {
201 item_list.remove_at (index);
202 list_model.remove (get_nth_iter (index));
203 select (index - 1);
204
205 if (item_list.size == 0) {
206 _selected_item = null;
207 }
208 }
209
210 void insert_item (int index, TableItem item) {
211 item_list.insert (index, item);
212 TreeIter iter;
213 list_model.insert_before (out iter, get_nth_iter (index));
214 fill_out_iter (iter, item);
215 select (index);
216 }
217
218 void fill_out_iter (TreeIter iter, TableItem item) {
219 var values = item.get_values ();
220 for (int column = 0; column < columns; column++) {
221 list_model.set (iter, column, values[column]);
222 }
223 }
224
225 TreeIter? get_nth_iter (int index) {
226 var path = new TreePath.from_indices (index);
227 TreeIter iter;
228 if (list_model.get_iter (out iter, path)) {
229 return iter;
230 }
231 return null;
232 }
233
234 void remove_selected () {
235 int index = get_current_index ();
236 if (index != -1) {
237 item_list.remove_at (get_current_index ());
238 list_model.remove (get_current_iter ());
239 }
240 }
241
242 void internal_append_item (TableItem item) {
243 Gtk.TreeIter iter;
244 list_model.append (out iter);
245 fill_out_iter (iter, item);
246 }
247
248 void cursor_changed_handler () {
249 int index = get_current_index ();
250
251 remove_button.sensitive = (index != -1);
252
253 if (index != -1) {
254 up_button.sensitive = true;
255 down_button.sensitive = true;
256 if (index == 0) {
257 up_button.sensitive = false;
258 }
259 if (index == item_list.size - 1) {
260 down_button.sensitive = false;
261 }
262 fire_item_selected (item_list.get (index));
263 }
264 }
265
266 void up_pressed () {
267 list_is_changing = true;
268 int index = get_current_index ();
269 var item = item_list[index];
270 remove_item (index);
271 insert_item (index - 1, item);
272 list_is_changing = false;
273 }
274
275 void down_pressed () {
276 list_is_changing = true;
277 int index = get_current_index ();
278 var item = item_list[index];
279 remove_item (index);
280 insert_item (index + 1, item);
281 list_is_changing = false;
282 }
283
284
285 TreeIter? get_current_iter () {
286 Gtk.TreePath current_path;
287 tree_view.get_cursor (out current_path, null);
288
289 TreeIter iter;
290 if (list_model.get_iter (out iter, current_path)) {
291 return iter;
292 }
293 return null;
294 }
295
296 int get_current_index () {
297 Gtk.TreePath path;
298
299 tree_view.get_cursor (out path, null);
300
301 if (path != null)
302 {
303 return (path.get_indices ())[0];
304 }
305 return -1;
306 }
307
308 void update_toolbar_visibility () {
309 if (!allows_add && !allows_remove && !allows_reordering) {
310 toolbar.hide ();
311 } else {
312 toolbar.show ();
313 }
314 }
315
316 public void append_item (TableItem item) {
317 internal_append_item (item);
318 item_list.add (item);
319 }
320
321 public void show_list (Gee.List<TableItem> items) {
322 item_list = items;
323
324 list_model = new ListStore.newv(column_types);
325 tree_view.set_model (list_model);
326
327 foreach (var item in items) {
328 internal_append_item (item);
329 }
330 }
331
332 }
333}
0\ No newline at end of file334\ No newline at end of file
1335
=== added file 'lib/Views/TableItem.vala'
--- lib/Views/TableItem.vala 1970-01-01 00:00:00 +0000
+++ lib/Views/TableItem.vala 2014-11-02 22:04:23 +0000
@@ -0,0 +1,30 @@
1/***
2 Copyright (C) 2014 Raphael Isemann <raphael@elementaryos.org>
3
4 This program or library is free software; you can redistribute it
5 and/or modify it under the terms of the GNU Lesser General Public
6 License as published by the Free Software Foundation; either
7 version 3 of the License, or (at your option) any later version.
8
9 This library is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 Lesser General Public License for more details.
13
14 You should have received a copy of the GNU Lesser General
15 Public License along with this library; if not, write to the
16 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
17 Boston, MA 02110-1301 USA.
18***/
19
20using Gtk;
21using Gdk;
22
23namespace Granite.Widgets.Views {
24
25 public interface TableItem : GLib.Object {
26
27 public abstract string[] get_values ();
28
29 }
30}
0\ No newline at end of file31\ No newline at end of file

Subscribers

People subscribed via source and target branches