Merge lp:~tombeckmann/switchboard/restructure into lp:~elementary-pantheon/switchboard/switchboard

Proposed by Tom Beckmann
Status: Rejected
Rejected by: David Gomes
Proposed branch: lp:~tombeckmann/switchboard/restructure
Merge into: lp:~elementary-pantheon/switchboard/switchboard
Diff against target: 716 lines (+219/-261)
3 files modified
CMakeLists.txt (+2/-1)
Switchboard/switchboard-app.vala (+132/-126)
Switchboard/switchboard-categoryview.vala (+85/-134)
To merge this branch: bzr merge lp:~tombeckmann/switchboard/restructure
Reviewer Review Type Date Requested Status
Danielle Foré Disapprove
Review via email: mp+127597@code.launchpad.net

Description of the change

Restructure the UI quite a bit to fit to Harvey's mockup http://imagebin.org/230647
Adjust the coding style in quite some places
Readd clutter based animations (this time stable ones as far as tested)

To post a comment you must log in.
Revision history for this message
Tom Beckmann (tombeckmann) wrote :

Also see this video https://www.youtube.com/watch?v=Sf6XwTPNZO4 of the animations

Revision history for this message
Danielle Foré (danrabbit) wrote :

* The vertical layout doesn't really make sense to me.
    * Displays are typically landscape, not portrait. This layout is going to cause unecessary amounts of scrolling and doesn't take advantage of the width of the display.

* Filtering just looks really unclear. Not a fan.

* The animation here isn't meaningful to me. It says "I'm opening a new window" and then it doesn't. The swipe animation we had before made more sense because it says "I'm changing context" and it gives the user a path to follow back. Additionally, the swipe animation implies a multi-touch gesture and I think it would make sense to have a consistent back/forward swipe gesture in elementary future.

* At this point in the development cycle, we need to not introduce such drastic changes that could introduce new problems. The focus should be on stability, the time for new features and experimental was supposed to end with feature freeze.

review: Disapprove
Revision history for this message
Harvey Cabaguio (harveycabaguio) wrote :

> * The vertical layout doesn't really make sense to me.
> * Displays are typically landscape, not portrait. This layout is going to
> cause unecessary amounts of scrolling and doesn't take advantage of the width
> of the display.

There's no need to take advantage of the width of the display, the items displayed on Switchboard are essentially lists and a vertical list is more efficient than a horizontal one.

And there is no unnecessary amounts of scrolling, I've listed all the plugs we're using right now and it even fits a small 800x600 screen resolution.

Unmerged revisions

318. By Tom Beckmann

Restructure the UI, rewrite big parts of code, adjust style, readd clutter based animations

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'CMakeLists.txt'
2--- CMakeLists.txt 2012-09-22 12:29:22 +0000
3+++ CMakeLists.txt 2012-10-02 21:23:23 +0000
4@@ -61,7 +61,7 @@
5 #
6
7 find_package(PkgConfig)
8-pkg_check_modules(DEPS REQUIRED granite gio-2.0 gee-1.0 unity)
9+pkg_check_modules(DEPS REQUIRED granite gio-2.0 gee-1.0 unity clutter-gtk-1.0)
10 add_definitions(${DEPS_CFLAGS})
11 link_directories(${DEPS_LIBRARY_DIRS})
12 find_package(Vala REQUIRED)
13@@ -79,6 +79,7 @@
14 gee-1.0
15 gio-2.0
16 unity
17+ clutter-gtk-1.0
18 CUSTOM_VAPIS
19 vapi/config.vapi
20 )
21
22=== modified file 'Switchboard/switchboard-app.vala'
23--- Switchboard/switchboard-app.vala 2012-09-23 22:43:12 +0000
24+++ Switchboard/switchboard-app.vala 2012-10-02 21:23:23 +0000
25@@ -63,10 +63,11 @@
26 Gtk.ScrolledWindow scrollable_view;
27 Switchboard.CategoryView category_view;
28
29+ GtkClutter.Embed clutter;
30+ GtkClutter.Actor actor;
31+
32 // Plug data
33- bool socket_shown;
34- Gee.HashMap<string, string> current_plug = new Gee.HashMap<string, string>();
35- Gee.HashMap<string, string>[] plugs;
36+ Plug current_plug;
37
38 string[] plug_places = {"/usr/share/plugs/",
39 "/usr/lib/plugs/",
40@@ -78,7 +79,7 @@
41
42 public SwitchboardApp () {
43
44- main_window = new Gtk.Window();
45+ main_window = new Gtk.Window ();
46
47 // Set up defaults
48 main_window.title = APP_TITLE;
49@@ -108,12 +109,8 @@
50 });
51 main_window.add_accel_group (accel_group);
52
53- // ??? Why?
54- current_plug["title"] = "";
55- current_plug["executable"] = "";
56-
57 category_view = new Switchboard.CategoryView ();
58- category_view.plug_selected.connect ((title, executable, @extern) => load_plug (title, executable, @extern));
59+ category_view.plug_selected.connect ((title, executable, external) => load_plug (title, executable, external));
60 category_view.margin_top = 12;
61
62 scrollable_view = new Gtk.ScrolledWindow (null, null);
63@@ -129,27 +126,30 @@
64 grid.attach (alert_view, 0, 2, 1, 1);
65
66 main_window.add (grid);
67+
68 scrollable_view.add_with_viewport (category_view);
69 scrollable_view.set_vexpand (true);
70- grid.attach (scrollable_view, 0, 1, 1, 1);
71+
72+ var bg = main_window.get_style_context ().get_background_color (Gtk.StateFlags.NORMAL);
73+
74+ clutter = new GtkClutter.Embed ();
75+ actor = new GtkClutter.Actor ();
76+ actor.scale_gravity = Clutter.Gravity.CENTER;
77+ actor.add_constraint (new Clutter.BindConstraint (clutter.get_stage (),
78+ Clutter.BindCoordinate.WIDTH, 0));
79+ actor.add_constraint (new Clutter.BindConstraint (clutter.get_stage (),
80+ Clutter.BindCoordinate.HEIGHT, 0));
81+ (actor.get_widget () as Gtk.Container).add (scrollable_view);
82+
83+ clutter.get_stage ().add_child (actor);
84+ clutter.get_stage ().background_color = {(uint8)(bg.red * 255), (uint8)(bg.green * 255), (uint8)(bg.blue * 255)};
85+
86+ grid.attach (clutter, 0, 1, 1, 1);
87
88 main_window.set_application (this);
89 main_window.show_all ();
90
91- foreach (var label in category_view.category_labels.values)
92- label.hide ();
93- foreach (var view in category_view.category_views.values)
94- view.hide ();
95-
96- alert_view.hide();
97-
98- loading = new Gtk.Spinner ();
99- loading.set_vexpand(true);
100- loading.halign = Gtk.Align.CENTER;
101- loading.valign = Gtk.Align.CENTER;
102- loading.width_request = 72;
103- loading.height_request = 72;
104- loading.start ();
105+ alert_view.hide ();
106
107 grid.attach (socket, 0, 1, 1, 1);
108 socket.hide ();
109@@ -158,13 +158,6 @@
110
111 var any_plugs = false;
112
113- socket.plug_added.connect (() => {
114- if (loading.visible) {
115- loading.hide ();
116- socket.show_all ();
117- }
118- });
119-
120 foreach (string place in plug_places)
121 if (enumerate_plugs (place))
122 any_plugs = true;
123@@ -174,27 +167,24 @@
124 search_box.sensitive = false;
125 }
126
127+ actor.get_widget ().show_all ();
128+
129 bool found = false;
130 if (plug_to_open != null) {
131- foreach (var plug in plugs) {
132- if (plug["id"] == plug_to_open) {
133- load_plug (plug["title"], plug["exec"], plug["extern"] == "1");
134+ foreach (var child in category_view.get_children ()) {
135+ if (child is Plug && (child as Plug).id == plug_to_open) {
136+ var plug = child as Plug;
137+ load_plug (plug.title, plug.exec, plug.external);
138 found = true;
139 }
140 }
141+
142 if (!found) {
143 critical ("Couldn't find %s among the loaded settings.", plug_to_open);
144 }
145 }
146-
147- foreach (var store in category_view.category_store.values) {
148- store.foreach ((model, path, iter) => {
149- store.set_value (iter, 3, true);
150- return false;
151- });
152- }
153 }
154-
155+
156 void shut_down () {
157 plug_closed ();
158 }
159@@ -210,36 +200,42 @@
160 category_view.hide ();
161 }
162
163- public void load_plug (string title, string executable, bool @extern) {
164- debug ("Selected plug: title %s | executable %s", title, executable);
165+ // we cant pass a Plug to this method as its serialization via dbus is not supported
166+ public void load_plug (string title, string executable, bool external) {
167+
168+ Plug plug = null;
169+ foreach (var child in category_view.get_children ()) {
170+ if (child is Plug && (child as Plug).exec == executable)
171+ plug = child as Plug;
172+ }
173+
174+ if (plug == null)
175+ error ("Could not load plug %s, not loaded", title);
176+
177+ debug ("Selected plug: title %s | executable %s", plug.title, plug.exec);
178
179- // Launch plug's executable
180- if (current_plug["title"] != title || !socket.visible) {
181- try {
182- // The plug is already selected
183- debug(_("Closing plug \"%s\" in Switchboard controller..."), current_plug["title"]);
184+ try {
185+ // The plug is already selected
186+ if (current_plug != null) {
187+ debug(_("Closing plug \"%s\" in Switchboard controller"), current_plug.title);
188 plug_closed ();
189-
190- string[] cmd_exploded = (executable!=null)?executable.split (" "):null;
191- GLib.Process.spawn_async (File.new_for_path (cmd_exploded[0]).get_parent ().
192- get_path (), cmd_exploded, null, SpawnFlags.SEARCH_PATH, null, null);
193-
194- // ensure the button is sensitive; it might be the first plug loaded
195- if (!@extern) {
196- navigation_button.set_sensitive(true);
197- navigation_button.stock_id = Gtk.Stock.HOME;
198- current_plug["title"] = title;
199- current_plug["executable"] = executable;
200- }
201- } catch { warning(_("Failed to launch plug: title %s | executable %s"), title, executable); }
202- } else {
203- navigation_button.set_sensitive(true);
204- navigation_button.stock_id = Gtk.Stock.HOME;
205- }
206+ }
207+
208+ string[] cmd_exploded = (plug.exec != null) ? plug.exec.split (" ") : null;
209+ GLib.Process.spawn_async (File.new_for_path (cmd_exploded[0]).get_parent ().
210+ get_path (), cmd_exploded, null, SpawnFlags.SEARCH_PATH, null, null);
211+
212+ // ensure the button is sensitive; it might be the first plug loaded
213+ if (!plug.external) {
214+ navigation_button.set_sensitive (true);
215+ navigation_button.stock_id = Gtk.Stock.HOME;
216+ current_plug = plug;
217+ }
218+ } catch { warning(_("Failed to launch plug: title %s | executable %s"), plug.title, plug.exec); }
219
220- if (!@extern) {
221+ if (!plug.external) {
222 switch_to_socket ();
223- main_window.title = @"$APP_TITLE - $title";
224+ main_window.title = @"$APP_TITLE - " + plug.title;
225 }
226 }
227
228@@ -254,35 +250,34 @@
229 switch_to_icons();
230 navigation_button.stock_id = Gtk.Stock.GO_BACK;
231 }
232- else {
233- load_plug (current_plug["title"], current_plug["executable"], current_plug["extern"] == "1");
234+ else if (current_plug != null) {
235+ load_plug (current_plug.title, current_plug.exec, current_plug.external);
236 navigation_button.stock_id = Gtk.Stock.HOME;
237 }
238 }
239
240 // Switches to the socket view
241 void switch_to_socket () {
242-
243- socket_shown = true;
244 search_box.sensitive = false;
245
246- category_view.hide ();
247- loading.show_all ();
248+ actor.animate (Clutter.AnimationMode.EASE_IN_CUBIC, 300, scale_x : 2.0f, scale_y : 2.0f, opacity : 0).
249+ completed.connect (() => {
250+ clutter.hide ();
251+ socket.show_all ();
252+ });
253 }
254
255 // Switches back to the icons
256 bool switch_to_icons () {
257 socket.hide ();
258- loading.hide ();
259- category_view.show ();
260-
261- socket_shown = false;
262+ clutter.show ();
263+ actor.animate (Clutter.AnimationMode.EASE_IN_CUBIC, 250, scale_x : 1.0f, scale_y : 1.0f, opacity : 255);
264
265 // Reset state
266 reset_title ();
267- search_box.set_text("");
268- search_box.sensitive = count_plugs() > 0;
269- progress_label.set_text("");
270+ search_box.set_text ("");
271+ search_box.sensitive = count_plugs () > 0;
272+ progress_label.set_text ("");
273 progress_bar.fraction = 0.0;
274 progress_toolitem.visible = false;
275
276@@ -298,36 +293,42 @@
277
278 // <keyfile's absolute path, keyfile's directory>
279 List<string> keyfiles = find_plugs (plug_root_dir);
280- if (keyfiles.length() == 0) {
281+ if (keyfiles.length () == 0) {
282 return false;
283 } else {
284 foreach (string keyfile in keyfiles) {
285 KeyFile kf = new KeyFile();
286
287- string head = File.new_for_path(keyfile).get_basename();
288- string parent = File.new_for_path(keyfile).get_parent().get_path();
289+ string head = File.new_for_path (keyfile).get_basename ();
290+ string parent = File.new_for_path (keyfile).get_parent ().get_path ();
291
292- Gee.HashMap<string, string> plug = new Gee.HashMap<string, string> ();
293- try { kf.load_from_file(keyfile, KeyFileFlags.NONE);
294- } catch (Error e) { warning("Couldn't load this keyfile, %s (path: %s)", e.message, keyfile); }
295- plug["id"] = kf.get_start_group();
296- try {
297- var exec = kf.get_string (head, "exec");
298- //if a path starts with a double slash, we take it as an absolute path
299+ string title="", icon="", category="", exec="", id="";
300+ bool external=false;
301+
302+ try {
303+ kf.load_from_file (keyfile, KeyFileFlags.NONE);
304+ } catch (Error e) { warning ("Couldn't load this keyfile, %s (path: %s)", e.message, keyfile); }
305+
306+ id = kf.get_start_group ();
307+
308+ try {
309+ exec = kf.get_string (head, "exec");
310+ //if a path starts with a double slash, we take it as an absolute path to an extern plug
311 if (exec.substring (0, 2) == "//") {
312 exec = exec.substring (1);
313- plug["extern"] = "1";
314+ external = true;
315 } else {
316- exec = Path.build_filename(parent, exec);
317- plug["extern"] = "0";
318+ exec = Path.build_filename (parent, exec);
319+ external = false;
320 }
321-
322- plug["exec"] = exec;
323 } catch (Error e) { warning("Couldn't read exec field in file %s, %s", keyfile, e.message); }
324- try { plug["icon"] = kf.get_string (head, "icon");
325+
326+ try {
327+ icon = kf.get_string (head, "icon");
328 } catch (Error e) { warning("Couldn't read icon field in file %s, %s", keyfile, e.message); }
329+
330 try {
331- plug["title"] = kf.get_locale_string (head, "title");
332+ title = kf.get_locale_string (head, "title");
333 string? textdomain = null;
334 foreach (var domain_key in SUPPORTED_GETTEXT_DOMAINS_KEYS) {
335 if (kf.has_key (head, domain_key)) {
336@@ -336,32 +337,34 @@
337 }
338 }
339 if (textdomain != null)
340- plug["title"] = GLib.dgettext (textdomain, plug["title"]).dup ();
341+ title = GLib.dgettext (textdomain, title).dup ();
342 } catch (Error e) { warning("Couldn't read title field in file %s, %s", keyfile, e.message); }
343- try { plug["category"] = kf.get_string (head, "category");
344- } catch {
345- plug["category"] = "other";
346- }
347- category_view.add_plug (plug);
348- plugs += plug;
349+
350+ try {
351+ category = kf.get_string (head, "category");
352+ } catch { category = "other"; }
353+
354+ category_view.add_plug (new Plug (title, exec, external, icon, category, id));
355 }
356+
357 return true;
358 }
359 }
360
361 // Checks if the file is a .plug file
362 bool is_plug_file (string filename) {
363- return (filename.down().has_suffix(".plug"));
364+ return filename.down ().has_suffix (".plug");
365 }
366
367 // Find all .plug files
368- List<string> find_plugs (string path, List<string>? keyfiles_list = null) {
369+ List<string>? find_plugs (string path, List<string>? keyfiles_list = null) {
370+
371 List<string>? keyfiles;
372 if(keyfiles_list == null) {
373 keyfiles = new List<string> ();
374 } else {
375 keyfiles = new List<string> ();
376- foreach(var keyfile in keyfiles_list) {
377+ foreach (var keyfile in keyfiles_list) {
378 keyfiles.append(keyfile);
379 }
380 }
381@@ -370,18 +373,19 @@
382 if (!directory.query_exists ()) {
383 return null;
384 }
385+
386 try {
387 var enumerator = directory.enumerate_children (
388 FileAttribute.STANDARD_NAME + "," + FileAttribute.STANDARD_TYPE, 0);
389 FileInfo file_info;
390+
391 while ((file_info = enumerator.next_file ()) != null) {
392- string file_path = Path.build_filename(path, file_info.get_name());
393- if (file_info.get_file_type() == GLib.FileType.REGULAR &&
394- is_plug_file(file_info.get_name())) {
395- keyfiles.append(file_path);
396- } else if(file_info.get_file_type() == GLib.FileType.DIRECTORY) {
397- keyfiles = find_plugs(file_path, keyfiles);
398- }
399+ string file_path = Path.build_filename (path, file_info.get_name ());
400+
401+ if (file_info.get_file_type() == GLib.FileType.REGULAR && is_plug_file (file_info.get_name()))
402+ keyfiles.append (file_path);
403+ else if(file_info.get_file_type () == GLib.FileType.DIRECTORY)
404+ keyfiles = find_plugs (file_path, keyfiles);
405 }
406 } catch { warning(_(@"Unable to iterate over enumerated plug directory \"$path\"'s contents")); }
407
408@@ -391,10 +395,12 @@
409 // Counts how many plugs exist at the moment
410 int count_plugs () {
411
412- uint count = 0;
413- foreach (string place in plug_places)
414- count += find_plugs (place).length ();
415- return (int) count;
416+ int count = 0;
417+ foreach (var child in category_view.get_children ())
418+ if (child is Plug)
419+ count ++;
420+
421+ return count;
422 }
423
424 /*
425@@ -463,7 +469,7 @@
426 rspace.set_expand(true);
427
428 // Progressbar
429- var progress_vbox = new Gtk.VBox (true, 0);
430+ var progress_vbox = new Gtk.Box (Gtk.Orientation.VERTICAL, 0);
431 progress_label = new Gtk.Label ("");
432 progress_label.set_use_markup(true);
433 progress_bar = new Gtk.ProgressBar ();
434@@ -477,18 +483,19 @@
435 search_box = new Granite.Widgets.SearchBar (_("Search Settings"));
436 search_box.primary_icon_stock = "gtk-find";
437 search_box.activate.connect(() => search_box_activated());
438+ search_box.margin_right = 6;
439 search_box.changed.connect(() => {
440 category_view.filter_plugs(search_box.get_text (), this);
441- search_box_text_changed();
442+ search_box_text_changed ();
443 });
444- search_box.sensitive = (count_plugs () > 0);
445+
446 var find_toolitem = new Gtk.ToolItem ();
447- find_toolitem.add(search_box);
448+ find_toolitem.add (search_box);
449
450 // Nav button
451 navigation_button = new Gtk.ToolButton.from_stock(Gtk.Stock.GO_BACK);
452 navigation_button.clicked.connect (handle_navigation_button_clicked);
453- navigation_button.set_sensitive(false);
454+ navigation_button.set_sensitive (false);
455
456 // Add everything to the toolbar
457 toolbar.insert (navigation_button, -1);
458@@ -496,7 +503,6 @@
459 toolbar.insert (progress_toolitem, -1);
460 toolbar.insert (rspace, -1);
461 toolbar.insert (find_toolitem, -1);
462- toolbar.insert (create_appmenu (new Gtk.Menu ()), -1);
463 }
464
465 public override void activate () {}
466@@ -516,7 +522,7 @@
467 message(_(@"Version: $VERSION"));
468 message(_("Report any issues/bugs you mind find to lp:switchboard"));
469
470- Gtk.init (ref args);
471+ GtkClutter.init (ref args);
472
473 var context = new OptionContext("");
474 context.add_main_entries(entries, "switchboard ");
475
476=== modified file 'Switchboard/switchboard-categoryview.vala'
477--- Switchboard/switchboard-categoryview.vala 2012-09-22 17:33:26 +0000
478+++ Switchboard/switchboard-categoryview.vala 2012-10-02 21:23:23 +0000
479@@ -17,152 +17,103 @@
480
481 namespace Switchboard {
482
483+ public class Plug : Gtk.EventBox {
484+
485+ public string title;
486+ public string exec;
487+ public bool external;
488+ public string category;
489+ public string id;
490+
491+ public signal void selected ();
492+
493+ Gtk.Box box;
494+
495+ public Plug (string _title, string _exec, bool _extern, string _icon, string _category, string _id)
496+ {
497+ title = _title;
498+ exec = _exec;
499+ external = _extern;
500+ id = _id;
501+ category = _category;
502+
503+ margin = 6;
504+
505+ Gdk.Pixbuf icon_pixbuf = null;
506+ try {
507+ icon_pixbuf = Gtk.IconTheme.get_default ().load_icon (_icon, 32, Gtk.IconLookupFlags.GENERIC_FALLBACK);
508+ } catch { warning (_("Unable to load plug %s's icon: %s"), title, _icon); }
509+
510+ var lbl = new Gtk.Label (title);
511+ lbl.halign = Gtk.Align.START;
512+ lbl.ellipsize = Pango.EllipsizeMode.END;
513+ box = new Gtk.Box (Gtk.Orientation.HORIZONTAL, 6);
514+
515+ box.pack_start (new Gtk.Image.from_pixbuf (icon_pixbuf), false);
516+ box.pack_start (lbl);
517+
518+ add (box);
519+ }
520+
521+ public override bool button_press_event (Gdk.EventButton event) {
522+ selected ();
523+
524+ return false;
525+ }
526+ }
527+
528 public class CategoryView : Gtk.Grid {
529
530- public Gee.HashMap<string, Gtk.Grid> category_labels = new Gee.HashMap<string, Gtk.Grid> ();
531- public Gee.HashMap<string, Gtk.ListStore> category_store = new Gee.HashMap<string, Gtk.ListStore> ();
532- public Gee.HashMap<string, Gtk.IconView> category_views = new Gee.HashMap<string, Gtk.IconView> ();
533- Gtk.IconTheme theme = Gtk.IconTheme.get_default ();
534-
535- public signal void plug_selected (string title, string executable, bool @extern);
536+ public signal void plug_selected (string title, string executable, bool external);
537+
538 string [] category_ids = { "personal", "hardware", "network", "system" };
539 string [] category_names = { N_("Personal"), N_("Hardware"), N_("Network and Wireless"), N_("System") };
540+ int [] current_indices = {1, 1, 1, 1};
541
542 public CategoryView () {
543- for (int i = 0; i < category_ids.length; i++) {
544- var store = new Gtk.ListStore (5, typeof (Gdk.Pixbuf), typeof (string),
545- typeof(string), typeof(bool), typeof(string));
546- store.set_sort_column_id (1, Gtk.SortType.ASCENDING);
547-
548- var category_label = new Gtk.Label ("<big><b>" + _(category_names[i]) + "</b></big>");
549- category_label.margin_left = 12;
550- var filtered = new Gtk.TreeModelFilter(store, null);
551- filtered.set_visible_column(3);
552- filtered.refilter();
553-
554- var category_plugs = new Gtk.IconView.with_model (filtered);
555- category_plugs.set_item_width(96);
556- category_plugs.set_text_column (1);
557- category_plugs.set_pixbuf_column (0);
558- category_plugs.set_hexpand (true);
559- category_plugs.selection_changed.connect(() => on_selection_changed(category_plugs, filtered));
560-
561- (category_plugs.get_cells ().nth_data (0) as Gtk.CellRendererText).wrap_mode = Pango.WrapMode.WORD;
562- (category_plugs.get_cells ().nth_data (0) as Gtk.CellRendererText).ellipsize_set = true;
563-
564- var bg_css = new Gtk.CssProvider ();
565- try {
566- bg_css.load_from_data ("*{background-color:@background_color;}", -1);
567- } catch (Error e) { warning (e.message); }
568- category_plugs.get_style_context ().add_provider (bg_css, 20000);
569- category_label.xalign = (float) 0.02;
570-
571- var grid = new Gtk.Grid ();
572- category_label.use_markup = true;
573-
574- // Always add a Seperator
575- var h_separator = new Gtk.Separator (Gtk.Orientation.HORIZONTAL);
576- h_separator.set_hexpand (true);
577- grid.attach (category_label, 0, 0, 1, 1);
578- grid.attach (h_separator, 1, 0, 1, 1); // expand, fill, padding´
579-
580- grid.attach(category_plugs, 0, 1, 2, 1);
581-
582- category_labels[category_ids[i]] = grid;
583- category_store[category_ids[i]] = store;
584- category_views[category_ids[i]] = category_plugs;
585-
586- attach (grid, 0, i, 1, 1);
587+
588+ column_homogeneous = true;
589+ margin = 12;
590+
591+ for (var i = 0;i < category_names.length; i++) {
592+ var lbl = new Gtk.Label ("<b><big>"+category_names[i]+"</big></b>");
593+ lbl.halign = Gtk.Align.START;
594+ lbl.use_markup = true;
595+ attach (lbl, i, 0, 1, 1);
596 }
597- }
598-
599- public void add_plug (Gee.HashMap<string, string> plug) {
600-
601- Gtk.TreeIter root;
602- string plug_down = plug["category"].down();
603-
604- if (!(plug_down in category_ids)) {
605- warning (_("Keyfile \"%s\" contains an invalid category: \"%s\", and will not be added"),
606- plug["title"], plug["category"]);
607- return;
608- }
609-
610- Gdk.Pixbuf icon_pixbuf = null;
611- try {
612- icon_pixbuf = theme.load_icon (plug["icon"], 32, Gtk.IconLookupFlags.GENERIC_FALLBACK);
613- } catch {
614- warning(_("Unable to load plug %s's icon: %s"), plug["title"], plug["icon"]);
615- return; // FIXME: if we get no icon, we probably dont want that one..
616- }
617- category_store[plug_down].append(out root);
618-
619- category_store[plug_down].set(root, 0, icon_pixbuf, 1, plug["title"], 2, plug["exec"],
620- 3, true, 4, plug["extern"]);
621- category_labels[plug_down].show_all ();
622- category_views[plug_down].show_all ();
623-
624- }
625-
626- public void filter_plugs (string filter, SwitchboardApp switchboard) {
627
628- var any_found = false;
629- foreach (string category in category_ids) {
630-
631- var store = category_store[category];
632- var container = category_labels[category];
633-
634- int shown = 0;
635-
636- store.foreach((model, path, iter) => {
637- string title;
638-
639- store.get (iter, 1, out title);
640-
641- if (filter.down () in title.down ()) {
642- store.set_value (iter, 3, true);
643- shown ++;
644- } else {
645- store.set_value (iter, 3, false);
646+ }
647+
648+ public void add_plug (Plug plug) {
649+ string plug_down = plug.category.down ();
650+
651+ int column = -1;
652+ for (var i=0;i<category_ids.length; i++) {
653+ if (plug_down == category_ids[i]) {
654+ column = i;
655+ break;
656 }
657-
658- return false;
659- });
660-
661- if (shown == 0) {
662- container.hide ();
663- } else {
664- any_found = true;
665- container.show ();
666 }
667- }
668- if (!any_found) {
669- switchboard.show_alert(_("No settings found"), _("Try changing your search terms"), Gtk.MessageType.INFO);
670- } else {
671- switchboard.hide_alert();
672- }
673+
674+ if (column == -1)
675+ error (_("Keyfile \"%s\" contains an invalid category: \"%s\", and will not be added"),
676+ plug.title, plug.category);
677+
678+ attach (plug, column, current_indices[column], 1, 1);
679+ current_indices[column]++;
680+
681+ plug.selected.connect (() => plug_selected (plug.title, plug.exec, plug.external));
682 }
683
684- private void on_selection_changed (Gtk.IconView view, Gtk.TreeModelFilter store) {
685-
686- GLib.Value title;
687- GLib.Value executable;
688- GLib.Value @extern;
689- Gtk.TreeIter selected_plug;
690-
691- var selected = view.get_selected_items ();
692- var item = selected.nth_data(0);
693-
694- if (item == null)
695- return;
696-
697- store.get_iter (out selected_plug, item);
698- store.get_value (selected_plug, 1, out title);
699- store.get_value (selected_plug, 2, out executable);
700- store.get_value (selected_plug, 4, out @extern);
701-
702- plug_selected (title.get_string(), executable.get_string(), @extern.get_string () == "1");
703-
704- view.unselect_path (item);
705+ public void filter_plugs (string filter, SwitchboardApp switchboard) {
706+ var searching = filter.down ();
707+
708+ foreach (var child in get_children ()) {
709+ if (!(child is Plug))
710+ continue;
711+
712+ child.visible = searching in (child as Plug).title.down ();
713+ }
714 }
715 }
716 }

Subscribers

People subscribed via source and target branches

to all changes: