Merge lp:~tintou/slingshot/search-listbox into lp:~elementary-pantheon/slingshot/trunk

Proposed by Corentin Noël on 2015-12-18
Status: Merged
Approved by: Cody Garver on 2015-12-21
Approved revision: 618
Merged at revision: 616
Proposed branch: lp:~tintou/slingshot/search-listbox
Merge into: lp:~elementary-pantheon/slingshot/trunk
Diff against target: 710 lines (+202/-394)
3 files modified
src/SlingshotView.vala (+7/-22)
src/Widgets/SearchItem.vala (+36/-27)
src/Widgets/SearchView.vala (+159/-345)
To merge this branch: bzr merge lp:~tintou/slingshot/search-listbox
Reviewer Review Type Date Requested Status
Daniel Fore ux Approve on 2015-12-20
Cody Garver 2015-12-18 Needs Fixing on 2015-12-18
Review via email: mp+280991@code.launchpad.net

Commit message

Replace custom search view with native GtkListBox. Also fixes lp:1525040.

Description of the change

Replaced the custom search view by the native GtkListBox.

To post a comment you must log in.
Rico Tzschichholz (ricotz) wrote :

tintou, hmm, adding two custom bindings to your project should suffice to work around this issue and avoid shipping the whole vapi!

Rico Tzschichholz (ricotz) wrote :

=== modified file 'src/Widgets/SearchView.vala'
--- src/Widgets/SearchView.vala 2015-12-18 18:45:47 +0000
+++ src/Widgets/SearchView.vala 2015-12-18 20:05:05 +0000
@@ -41,7 +41,7 @@ namespace Slingshot.Widgets {
             list_box = new Gtk.ListBox ();
             list_box.activate_on_single_click = true;
             list_box.set_sort_func ((row1, row2) => update_sort (row1, row2));
- list_box.set_header_func ((row, before) => update_header (row, before));
+ list_box.set_header_func ((Gtk.ListBoxUpdateHeaderFunc) update_header);
             list_box.row_activated.connect ((row) => {
                 var search_item = row as SearchItem;
                 if (search_item.result_type == SearchItem.ResultType.APP_ACTIONS) {
@@ -121,6 +121,7 @@ namespace Slingshot.Widgets {
             return 0;
         }

+ [CCode (instance_pos = -1)]
         private void update_header (Gtk.ListBoxRow row, Gtk.ListBoxRow? before) {
             var item = row as SearchItem;
             if (before != null && ((SearchItem) before).result_type == item.result_type) {

Rico Tzschichholz (ricotz) wrote :

Redo this branch and avoid adding/removing the vapi.

Corentin Noël (tintou) wrote :

ricotz: done

Cody Garver (codygarver) wrote :

Regression: top result does not get grey highlight

review: Needs Fixing
Daniel Fore (danrabbit) wrote :

For me, the top result is highlighted, but pressing enter doesn't activate it

lp:~tintou/slingshot/search-listbox updated on 2015-12-19
616. By Corentin Noël on 2015-12-19

Replaced the custom SearchView with the native GtkListBox.

617. By Rico Tzschichholz on 2015-12-19

Workaround the wrong Gtk.ListBox binding, and removed the wrapper.

618. By Corentin Noël on 2015-12-19

Allow to search words with the "v" character.

Corentin Noël (tintou) wrote :

Okay I fixed everything and rebased the merge request

Daniel Fore (danrabbit) wrote :

Can confirm it works as expected :)

review: Approve (ux)

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'src/SlingshotView.vala'
2--- src/SlingshotView.vala 2015-09-24 16:19:19 +0000
3+++ src/SlingshotView.vala 2015-12-19 14:16:00 +0000
4@@ -195,8 +195,7 @@
5 stack.add_named (category_view, "category");
6
7 // Create the "SEARCH_VIEW"
8- search_view = new Widgets.SearchView (this);
9- search_view.margin_end = 6;
10+ search_view = new Widgets.SearchView ();
11 search_view.start_search.connect ((match, target) => {
12 search.begin (search_entry.text, match, target);
13 });
14@@ -342,13 +341,7 @@
15
16 private void search_entry_activated () {
17 if (modality == Modality.SEARCH_VIEW) {
18- if (search_view.launch_selected ())
19- close_indicator ();
20- } else {
21-/* TODO
22- if (get_focus () as Widgets.AppEntry != null) // checking the selected widget is an AppEntry
23- ((Widgets.AppEntry) get_focus ()).launch_app ();
24-*/
25+ search_view.activate_selection ();
26 }
27 }
28
29@@ -393,17 +386,7 @@
30 case "Enter": // "KP_Enter"
31 case "Return":
32 case "KP_Enter":
33- if (modality == Modality.SEARCH_VIEW) {
34- if (search_view.launch_selected ())
35- close_indicator ();
36- } else {
37-/* TODO
38- if (get_focus () as Widgets.AppEntry != null) // checking the selected widget is an AppEntry
39- ((Widgets.AppEntry)get_focus ()).launch_app ();
40-*/
41- }
42- return true;
43-
44+ return false;
45
46 case "Alt_L":
47 case "Alt_R":
48@@ -491,7 +474,7 @@
49 category_move_focus (0, -1);
50 }
51 } else if (modality == Modality.SEARCH_VIEW) {
52- search_view.up ();
53+ return false;
54 }
55 break;
56
57@@ -508,7 +491,7 @@
58 category_move_focus (0, +1);
59 }
60 } else if (modality == Modality.SEARCH_VIEW) {
61- search_view.down ();
62+ return false;
63 }
64 break;
65
66@@ -572,6 +555,8 @@
67 case "V":
68 if ((event.state & (Gdk.ModifierType.CONTROL_MASK | Gdk.ModifierType.SHIFT_MASK)) != 0) {
69 search_entry.paste_clipboard ();
70+ } else {
71+ return false;
72 }
73 break;
74
75
76=== modified file 'src/Widgets/SearchItem.vala'
77--- src/Widgets/SearchItem.vala 2015-09-20 17:45:17 +0000
78+++ src/Widgets/SearchItem.vala 2015-12-19 14:16:00 +0000
79@@ -17,33 +17,42 @@
80 //
81
82 namespace Slingshot.Widgets {
83-
84- public class SearchItem : Gtk.Button {
85+ public class SearchItem : Gtk.ListBoxRow {
86+ public enum ResultType {
87+ UNKNOWN = 0,
88+ TEXT,
89+ APPLICATION,
90+ GENERIC_URI,
91+ ACTION,
92+ SEARCH,
93+ CONTACT,
94+ INTERNET,
95+ SETTINGS,
96+ APP_ACTIONS
97+ }
98
99 const int ICON_SIZE = 32;
100
101+ public signal bool launch_app ();
102+
103 public Backend.App app { get; construct; }
104+ public ResultType result_type { public get; construct; }
105+
106+ public bool dragging = false; //prevent launching
107
108 private Gtk.Label name_label;
109 private Gtk.Image icon;
110-
111 private Cancellable? cancellable = null;
112- public bool dragging = false; //prevent launching
113- public bool action = false;
114-
115- public signal bool launch_app ();
116-
117- public SearchItem (Backend.App app, string search_term = "", bool action = false, string action_title = "") {
118- Object (app: app);
119-
120- this.action = action;
121- get_style_context ().add_class (Gtk.STYLE_CLASS_FLAT);
122+
123+ public SearchItem (Backend.App app, string search_term = "", ResultType result_type = ResultType.UNKNOWN) {
124+ Object (app: app, result_type: result_type);
125
126 string markup;
127- if (action)
128- markup = action_title;
129- else
130+ if (result_type == SearchItem.ResultType.APP_ACTIONS) {
131+ markup = app.match.title;
132+ } else {
133 markup = Backend.SynapseSearch.markup_string_with_search (app.name, search_term);
134+ }
135
136 name_label = new Gtk.Label (markup);
137 name_label.set_ellipsize (Pango.EllipsizeMode.END);
138@@ -65,15 +74,17 @@
139 });
140 }
141
142- var box = new Gtk.Box (Gtk.Orientation.HORIZONTAL, 12);
143- box.pack_start (icon, false);
144- box.pack_start (name_label, true);
145- box.margin_start = 12;
146- box.margin_top = box.margin_bottom = 3;
147-
148- add (box);
149-
150- if (!action)
151+ var grid = new Gtk.Grid ();
152+ grid.orientation = Gtk.Orientation.HORIZONTAL;
153+ grid.column_spacing = 12;
154+ grid.add (icon);
155+ grid.add (name_label);
156+ grid.margin = 6;
157+ grid.margin_start = 18;
158+
159+ add (grid);
160+
161+ if (result_type != SearchItem.ResultType.APP_ACTIONS)
162 launch_app.connect (app.launch);
163
164 var app_match = app.match as Synapse.ApplicationMatch;
165@@ -95,9 +106,7 @@
166 }
167
168 public override void destroy () {
169-
170 base.destroy ();
171-
172 if (cancellable != null)
173 cancellable.cancel ();
174 }
175
176=== modified file 'src/Widgets/SearchView.vala'
177--- src/Widgets/SearchView.vala 2015-09-20 17:45:17 +0000
178+++ src/Widgets/SearchView.vala 2015-12-19 14:16:00 +0000
179@@ -1,372 +1,186 @@
180-// -*- Mode: vala; indent-tabs-mode: nil; tab-width: 4 -*-
181-//
182-// Copyright (C) 2011-2012 Giulio Collura
183-//
184-// This program is free software: you can redistribute it and/or modify
185-// it under the terms of the GNU General Public License as published by
186-// the Free Software Foundation, either version 3 of the License, or
187-// (at your option) any later version.
188-//
189-// This program is distributed in the hope that it will be useful,
190-// but WITHOUT ANY WARRANTY; without even the implied warranty of
191-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
192-// GNU General Public License for more details.
193-//
194-// You should have received a copy of the GNU General Public License
195-// along with this program. If not, see <http://www.gnu.org/licenses/>.
196-//
197+/*
198+ * Copyright (c) 2011-2015 elementary LLC. (http://elementary.io)
199+ *
200+ * This program is free software; you can redistribute it and/or
201+ * modify it under the terms of the GNU General Public
202+ * License as published by the Free Software Foundation; either
203+ * version 2 of the License, or (at your option) any later version.
204+ *
205+ * This program is distributed in the hope that it will be useful,
206+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
207+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
208+ * General Public License for more details.
209+ *
210+ * You should have received a copy of the GNU General Public
211+ * License along with this program; if not, write to the
212+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
213+ * Boston, MA 02111-1307, USA.
214+ *
215+ * Authored by: Corentin Noël <corentin@elementary.io>
216+ * Giulio Collura
217+ */
218
219 namespace Slingshot.Widgets {
220
221 public class SearchView : Gtk.ScrolledWindow {
222- const int MAX_RESULTS = 20;
223- const int MAX_RESULTS_BEFORE_LIMIT = 10;
224+ const int MAX_RESULTS = 10;
225
226 public signal void start_search (Synapse.SearchMatch search_match, Synapse.Match? target);
227-
228- public bool in_context_view { get; private set; default = false; }
229-
230- private Gee.HashMap<Backend.App, SearchItem> items;
231- private SearchItem selected_app = null;
232- private Gtk.Box main_box;
233-
234- private Gtk.Box context_box;
235- private Gtk.Fixed context_fixed;
236- private int context_selected_y;
237-
238- private int n_results = 0;
239-
240- private int _selected = 0;
241- public int selected {
242- get {
243- return _selected;
244- }
245- set {
246- _selected = value;
247- var max_index = (int)n_results - 1;
248-
249- // cycle
250- if (_selected < 0)
251- _selected = max_index;
252- else if (_selected > max_index)
253- _selected = 0;
254-
255- select_nth (main_box, _selected);
256-
257- if (in_context_view)
258- toggle_context (false);
259- }
260- }
261-
262- private int _context_selected = 0;
263- public int context_selected {
264- get {
265- return _context_selected;
266- }
267- set {
268- _context_selected = value;
269- var max_index = (int)context_box.get_children ().length () - 1;
270-
271- // cycle
272- if (_context_selected < 0)
273- _context_selected = max_index;
274- else if (_context_selected > max_index)
275- _context_selected = 0;
276-
277- select_nth (context_box, _context_selected);
278- }
279- }
280-
281 public signal void app_launched ();
282
283- private SlingshotView view;
284-
285- public SearchView (SlingshotView parent) {
286- view = parent;
287+ private Gtk.ListBox list_box;
288+ Gee.HashMap<SearchItem.ResultType, uint> limitator;
289+
290+ public SearchView () {
291+
292+ }
293+
294+ construct {
295 hscrollbar_policy = Gtk.PolicyType.NEVER;
296- items = new Gee.HashMap<Backend.App, SearchItem> ();
297-
298- main_box = new Gtk.Box (Gtk.Orientation.VERTICAL, 0);
299- main_box.margin_start = 12;
300-
301- context_box = new Gtk.Box (Gtk.Orientation.VERTICAL, 0);
302- context_fixed = new Gtk.Fixed ();
303- context_fixed.margin_start = 12;
304- context_fixed.put (context_box, 0, 0);
305-
306- var box = new Gtk.Box (Gtk.Orientation.HORIZONTAL, 0);
307- box.pack_start (main_box, true);
308- box.pack_start (context_fixed, false);
309-
310- add_with_viewport (box);
311-
312- parent.search_entry.key_press_event.connect ((e) => {
313- if (parent.search_entry.text == "")
314- _selected = 0;
315-
316- return false;
317+ limitator = new Gee.HashMap<SearchItem.ResultType, uint> ();
318+ list_box = new Gtk.ListBox ();
319+ list_box.activate_on_single_click = true;
320+ list_box.set_sort_func ((row1, row2) => update_sort (row1, row2));
321+ list_box.set_header_func ((Gtk.ListBoxUpdateHeaderFunc) update_header);
322+ list_box.row_activated.connect ((row) => {
323+ var search_item = row as SearchItem;
324+ if (search_item.result_type == SearchItem.ResultType.APP_ACTIONS) {
325+ ((Synapse.DesktopFilePlugin.ActionMatch) search_item.app.match).execute (null);
326+ } else {
327+ search_item.app.launch ();
328+ }
329+
330+ app_launched ();
331 });
332+
333+ add (list_box);
334 }
335
336 public void set_results (Gee.List<Synapse.Match> matches, string search_term) {
337- // we have a hashmap of the categories with their matches and keep
338- // their order in a separate list, as the keys list of the map does
339- // not always keep the same order in which the keys were added
340- var categories = new HashTable<int,Gee.LinkedList<Synapse.Match>> (null, null);
341- var categories_order = new Gee.LinkedList<int> ();
342-
343+ clear ();
344 foreach (var match in matches) {
345- Gee.LinkedList<Synapse.Match> list = null;
346-
347- // we're cheating here to give remote results a separate category. We assign 8 as
348- // the id for internet results, which currently is the lowest undefined MatchType
349- int type = match.match_type;
350- if (type == Synapse.MatchType.GENERIC_URI) {
351+ Backend.App app = new Backend.App.from_synapse_match (match);
352+ SearchItem.ResultType result_type = (SearchItem.ResultType) match.match_type;
353+ if (match is Synapse.DesktopFilePlugin.ActionMatch) {
354+ result_type = SearchItem.ResultType.APP_ACTIONS;
355+ } else if (match is Synapse.SwitchboardPlugin.SwitchboardObject) {
356+ result_type = SearchItem.ResultType.SETTINGS;
357+ } else if (match.match_type == Synapse.MatchType.GENERIC_URI) {
358 var uri = (match as Synapse.UriMatch).uri;
359- if (uri.has_prefix ("http://")
360- || uri.has_prefix ("ftp://")
361- || uri.has_prefix ("https://"))
362- type = 8;
363- }
364- // we're cheating again and assign switchboard plugs to a separate category.
365- // We assign 9 as the id for settings results
366- if (match is Synapse.SwitchboardPlugin.SwitchboardObject)
367- type = 9;
368- else if (match is Synapse.DesktopFilePlugin.ActionMatch)
369- type = 10;
370-
371- if ((list = categories.get (type)) == null) {
372- list = new Gee.LinkedList<Synapse.Match> ();
373- categories.set (type, list);
374- categories_order.add (type);
375- }
376-
377- list.add (match);
378- }
379-
380- n_results = 0;
381- clear ();
382-
383- // if we're showing more than about 10 results and we have more than
384- // categories, we limit the results per category to the most relevant
385- // ones.
386- var limit = MAX_RESULTS;
387- if (matches.size + 3 > MAX_RESULTS_BEFORE_LIMIT && categories_order.size > 2)
388- limit = 5;
389-
390- foreach (var type in categories_order) {
391- string label = "";
392-
393- switch (type) {
394- case Synapse.MatchType.UNKNOWN:
395- label = _("Other");
396- break;
397- case Synapse.MatchType.TEXT:
398- label = _("Text");
399- break;
400- case Synapse.MatchType.APPLICATION:
401- label = _("Applications");
402- break;
403- case Synapse.MatchType.GENERIC_URI:
404- label = _("Files");
405- break;
406- case Synapse.MatchType.ACTION:
407- label = _("Actions");
408- break;
409- case Synapse.MatchType.SEARCH:
410- label = _("Search");
411- break;
412- case Synapse.MatchType.CONTACT:
413- label = _("Contacts");
414- break;
415- case 8:
416- label = _("Internet");
417- break;
418- case 9:
419- label = _("Settings");
420- break;
421- case 10:
422- label = _("Application Actions");
423- break;
424- }
425-
426- var header = new Gtk.Label (label);
427- ((Gtk.Misc) header).xalign = 0.0f;
428- header.margin_start = 8;
429- header.margin_bottom = 4;
430- header.use_markup = true;
431- header.get_style_context ().add_class ("h4");
432- header.show ();
433- main_box.pack_start (header, false);
434-
435- var list = categories.get (type);
436- var old_selected = selected;
437- for (var i = 0; i < limit && i < list.size; i++) {
438- var match = list.get (i);
439-
440- if (type == 10) {
441- show_action (new Backend.App.from_synapse_match (match));
442- n_results++;
443- continue;
444- }
445-
446- // expand the actions we get for UNKNOWN
447- if (match.match_type == Synapse.MatchType.UNKNOWN) {
448- var actions = Backend.SynapseSearch.find_actions_for_match (match);
449- foreach (var action in actions) {
450- show_app (new Backend.App.from_synapse_match (action, match), search_term);
451- n_results++;
452- }
453- } else {
454- show_app (new Backend.App.from_synapse_match (match), search_term);
455- n_results++;
456- }
457- }
458- selected = old_selected;
459- }
460- }
461-
462- private void show_app (Backend.App app, string search_term) {
463-
464- var search_item = new SearchItem (app, search_term);
465- app.start_search.connect ((search, target) => start_search (search, target));
466- search_item.button_release_event.connect (() => {
467- if (!search_item.dragging) {
468- app.launch ();
469- app_launched ();
470- }
471- return true;
472- });
473-
474- main_box.pack_start (search_item, false, false);
475- search_item.show_all ();
476-
477- items[app] = search_item;
478-
479- }
480-
481- private void show_action (Backend.App app) {
482- var search_item = new SearchItem (app, "", true, app.match.title);
483- app.start_search.connect ((search, target) => start_search (search, target));
484- search_item.button_release_event.connect (() => {
485- if (!search_item.dragging) {
486- ((Synapse.DesktopFilePlugin.ActionMatch) app.match).execute (null);
487- app_launched ();
488- }
489-
490- return true;
491- });
492-
493- main_box.pack_start (search_item, false, false);
494- search_item.show_all ();
495-
496- items[app] = search_item;
497- }
498-
499- public void toggle_context (bool show) {
500- var prev_y = vadjustment.value;
501-
502- if (show && in_context_view == false) {
503- if (selected_app.app.match.match_type == Synapse.MatchType.ACTION)
504+ if (uri.has_prefix ("http://") || uri.has_prefix ("ftp://") || uri.has_prefix ("https://")) {
505+ result_type = SearchItem.ResultType.INTERNET;
506+ }
507+ }
508+
509+ if (result_type == SearchItem.ResultType.UNKNOWN) {
510+ var actions = Backend.SynapseSearch.find_actions_for_match (match);
511+ foreach (var action in actions) {
512+ app = new Backend.App.from_synapse_match (action, match);
513+ create_item (app, search_term, (SearchItem.ResultType) app.match.match_type);
514+ }
515+
516+ continue;
517+ }
518+
519+ create_item (app, search_term, result_type);
520+ }
521+
522+ weak Gtk.ListBoxRow? first = list_box.get_row_at_index (0);
523+ if (first != null) {
524+ list_box.select_row (first);
525+ }
526+ }
527+
528+ private void create_item (Backend.App app, string search_term, SearchItem.ResultType result_type) {
529+ if (limitator.has_key (result_type)) {
530+ var amount = limitator.get (result_type);
531+ if (amount >= MAX_RESULTS) {
532 return;
533-
534- in_context_view = true;
535-
536- foreach (var child in context_box.get_children ())
537- context_box.remove (child);
538-
539- var actions = Backend.SynapseSearch.find_actions_for_match (selected_app.app.match);
540- foreach (var action in actions) {
541- var app = new Backend.App.from_synapse_match (action, selected_app.app.match);
542- app.start_search.connect ((search, target) => start_search (search, target));
543- context_box.pack_start (new SearchItem (app));
544+ } else {
545+ limitator.set (result_type, amount + 1);
546 }
547- context_box.show_all ();
548-
549- Gtk.Allocation alloc;
550- selected_app.get_allocation (out alloc);
551-
552- context_fixed.move (context_box, 0, alloc.y);
553- context_selected_y = alloc.y;
554-
555- context_selected = 0;
556 } else {
557- in_context_view = false;
558-
559- // trigger update of selection
560- selected = selected;
561+ limitator.set (result_type, 1);
562 }
563
564- vadjustment.value = prev_y;
565+ var search_item = new SearchItem (app, search_term, result_type);
566+ app.start_search.connect ((search, target) => start_search (search, target));
567+
568+ list_box.add (search_item);
569+ search_item.show_all ();
570 }
571
572 public void clear () {
573- if (in_context_view)
574- toggle_context (false);
575-
576- foreach (var child in main_box.get_children ())
577+ limitator.clear ();
578+ list_box.get_children ().foreach ((child) => {
579 child.destroy ();
580- }
581-
582- public void down () {
583- if (in_context_view)
584- context_selected ++;
585- else
586- selected++;
587- }
588-
589- public void up () {
590- if (in_context_view)
591- context_selected--;
592- else
593- selected--;
594- }
595-
596- private void select_nth (Gtk.Box box, int index) {
597-
598- if (selected_app != null)
599- // enable to make main item stay blue
600- // && !(box == context_box && selected_app.get_parent () == main_box))
601- selected_app.unset_state_flags (Gtk.StateFlags.PRELIGHT);
602-
603- if (box == main_box)
604- selected_app = get_nth_main_item (index) as SearchItem;
605- else
606- selected_app = box.get_children ().nth_data (index) as SearchItem;
607-
608- selected_app.set_state_flags (Gtk.StateFlags.PRELIGHT, true);
609-
610- Gtk.Allocation alloc;
611- selected_app.get_allocation (out alloc);
612-
613- vadjustment.value = double.max (alloc.y - vadjustment.page_size / 2, 0);
614- }
615-
616- private Gtk.Widget? get_nth_main_item (int n) {
617- var i = 0;
618- foreach (var child in main_box.get_children ()) {
619- if (i == n && child is SearchItem)
620- return child;
621-
622- if (child is SearchItem)
623- i++;
624- }
625-
626- return null;
627- }
628-
629- /**
630- * Launch selected app
631- *
632- * @return indicates whether slingshot should now be hidden
633- */
634- public bool launch_selected () {
635- if (selected_app.action) {
636- ((Synapse.DesktopFilePlugin.ActionMatch) selected_app.app.match).execute (null);
637- return true;
638- }
639-
640- return selected_app.launch_app ();
641+ });
642+ }
643+
644+ public void activate_selection () {
645+ var selection = list_box.get_selected_row ();
646+ if (selection != null) {
647+ list_box.row_activated (selection);
648+ }
649+ }
650+
651+ private int update_sort (Gtk.ListBoxRow row1, Gtk.ListBoxRow row2) {
652+ var item1 = row1 as SearchItem;
653+ var item2 = row2 as SearchItem;
654+ if (item1.result_type != item2.result_type) {
655+ return item1.result_type - item2.result_type;
656+ }
657+
658+ return 0;
659+ }
660+
661+ [CCode (instance_pos = -1)]
662+ private void update_header (Gtk.ListBoxRow row, Gtk.ListBoxRow? before) {
663+ var item = row as SearchItem;
664+ if (before != null && ((SearchItem) before).result_type == item.result_type) {
665+ row.set_header (null);
666+ return;
667+ }
668+
669+ string label;
670+ switch (item.result_type) {
671+ case SearchItem.ResultType.TEXT:
672+ label = _("Text");
673+ break;
674+ case SearchItem.ResultType.APPLICATION:
675+ label = _("Applications");
676+ break;
677+ case SearchItem.ResultType.GENERIC_URI:
678+ label = _("Files");
679+ break;
680+ case SearchItem.ResultType.ACTION:
681+ label = _("Actions");
682+ break;
683+ case SearchItem.ResultType.SEARCH:
684+ label = _("Search");
685+ break;
686+ case SearchItem.ResultType.CONTACT:
687+ label = _("Contacts");
688+ break;
689+ case SearchItem.ResultType.INTERNET:
690+ label = _("Internet");
691+ break;
692+ case SearchItem.ResultType.SETTINGS:
693+ label = _("Settings");
694+ break;
695+ case SearchItem.ResultType.APP_ACTIONS:
696+ label = _("Application Actions");
697+ break;
698+ default:
699+ label = _("Other");
700+ break;
701+ }
702+
703+ var header = new Gtk.Label (label);
704+ header.margin_start = 6;
705+ ((Gtk.Misc) header).xalign = 0;
706+ header.get_style_context ().add_class ("h4");
707+ row.set_header (header);
708 }
709
710 }

Subscribers

People subscribed via source and target branches