Merge lp:~vikoadi/switchboard/keyboard-navigation into lp:~elementary-pantheon/switchboard/switchboard

Proposed by Viko Adi Rahmawan
Status: Merged
Approved by: Danielle Foré
Approved revision: 539
Merged at revision: 547
Proposed branch: lp:~vikoadi/switchboard/keyboard-navigation
Merge into: lp:~elementary-pantheon/switchboard/switchboard
Diff against target: 288 lines (+193/-10)
2 files modified
src/CategoryView.vala (+165/-8)
src/Switchboard.vala (+28/-2)
To merge this branch: bzr merge lp:~vikoadi/switchboard/keyboard-navigation
Reviewer Review Type Date Requested Status
Danielle Foré ux Approve
Artem Anufrij (community) code style Needs Fixing
David Gomes (community) Needs Fixing
elementary UX ux Pending
Review via email: mp+241757@code.launchpad.net

Commit message

Keyboard navigation throughout switchboard
- using arrow key will move selection
- using Return key to activate selection
- Alt+left key will bring back to CategoryView
- typing other key will start search
- press enter from startbar will activate first result

Description of the change

Keyboard navigation throughout switchboard
- using arrow key will move selection
- using Return key to activate selection
- Alt+left key will bring back to CategoryView
- typing other key will start search
- press enter from startbar will activate first result

please note that current elementary theme doesnt have focus style so either use Adwaita or comment

GtkIconView.view.cell:selected,
GtkIconView.view.cell:selected:focus {
    background-color: @base_color;
    color: @text_color;
}

in gtk-widgets.css theme file

To post a comment you must log in.
533. By Viko Adi Rahmawan

remove debug printing

534. By Viko Adi Rahmawan

remove TODO note

Revision history for this message
David Gomes (davidgomes) wrote :

"c", "d", "sel" and "dist" aren't really good variable names.

Also, this needs design input I'm pretty sure.

review: Needs Fixing
535. By Viko Adi Rahmawan

remove trailing whitespace, rename bad variable

Revision history for this message
Viko Adi Rahmawan (vikoadi) wrote :

variable renamed,
Ux team assigned

536. By Viko Adi Rahmawan

add space arround =>

Revision history for this message
Artem Anufrij (artem-anufrij) wrote :

see line comments

review: Needs Fixing (code style)
Revision history for this message
Danielle Foré (danrabbit) wrote :

Merge conflicts. Needs fixing.

review: Needs Fixing
537. By Viko Adi Rahmawan

merge trunk

538. By Viko Adi Rahmawan

clean code

539. By Viko Adi Rahmawan

clean code

Revision history for this message
Viko Adi Rahmawan (vikoadi) wrote :

merge trunk

Revision history for this message
Cody Garver (codygarver) wrote :

When I down arrow (⬇), the search field loses focus but I don't see where the "cursor" is. I can't see what is selected.

Revision history for this message
Viko Adi Rahmawan (vikoadi) wrote :

Have you try using adwaita theme?
read my note in Description of the Change above.

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

Hm, just added some stuff to make a style for switchboard. For some reason GtkIconView in Switchboard won't accept a background color.

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

It all appears to work correctly here :)

review: Approve (ux)
Revision history for this message
Cody Garver (codygarver) wrote :

Backspace key should close plug and return to categoryview

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

No, no backspace! We removed that before because it causes issues.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'src/CategoryView.vala'
2--- src/CategoryView.vala 2014-08-07 09:48:26 +0000
3+++ src/CategoryView.vala 2015-03-10 10:08:38 +0000
4@@ -102,6 +102,76 @@
5 break;
6 }
7
8+ category_plugs.focus_out_event.connect ((e) => {
9+ category_plugs.unselect_all ();
10+
11+ return false;
12+ });
13+
14+ category_plugs.focus_in_event.connect ((e) => {
15+ Gtk.TreePath path;
16+
17+ if (!category_plugs.get_cursor (out path, null)) {
18+ path = new Gtk.TreePath.from_indices (0, -1);
19+ }
20+ category_plugs.select_path (path);
21+
22+ return false;
23+ });
24+
25+ category_plugs.keynav_failed.connect ((direction) => {
26+ Gtk.IconView new_view = null;
27+ Gtk.TreePath path = null;
28+ Gtk.TreePath selected_path = null;
29+ int smallest_distance = 1000;
30+ Gtk.TreeIter iter;
31+ int distance;
32+
33+ if (direction == Gtk.DirectionType.UP) {
34+ new_view = get_prev_icon_view (category);
35+ if (new_view != null && category_plugs.get_cursor (out path, null)) {
36+ var current_column = category_plugs.get_item_column (path);
37+ var model = new_view.get_model ();
38+
39+ model.get_iter_first (out iter);
40+ do {
41+ path = model.get_path (iter);
42+ var next_column = new_view.get_item_column (path);
43+ distance = (next_column - current_column).abs ();
44+ if (distance <= smallest_distance) {
45+ selected_path = path;
46+ smallest_distance = distance;
47+ }
48+ } while (model.iter_next (ref iter));
49+ }
50+ } else if (direction == Gtk.DirectionType.DOWN) {
51+ new_view = get_next_icon_view (category);
52+ if (new_view != null && category_plugs.get_cursor (out path, null)) {
53+ var current_column = category_plugs.get_item_column (path);
54+ var model = new_view.get_model ();
55+
56+ model.get_iter_first (out iter);
57+ do {
58+ path = model.get_path (iter);
59+ var next_column = new_view.get_item_column (path);
60+ distance = (next_column - current_column).abs ();
61+ if (distance < smallest_distance) {
62+ selected_path = path;
63+ smallest_distance = distance;
64+ }
65+ } while (model.iter_next (ref iter));
66+ }
67+ }
68+
69+ if (new_view != null) {
70+ new_view.set_cursor (selected_path, null, false);
71+ new_view.grab_focus ();
72+ return true;
73+ }
74+
75+ return false;
76+ });
77+
78 attach (grid, 0, i, 1, 1);
79 }
80
81@@ -123,9 +193,9 @@
82 return false;
83 });
84 }
85-
86+
87 private Gtk.IconView setup_icon_view () {
88- var store = new Gtk.ListStore (Columns.N_COLUMNS, typeof (Gdk.Pixbuf), typeof (string),
89+ var store = new Gtk.ListStore (Columns.N_COLUMNS, typeof (Gdk.Pixbuf), typeof (string),
90 typeof(string), typeof(bool), typeof(Switchboard.Plug));
91 store.set_sort_column_id (1, Gtk.SortType.ASCENDING);
92 store.set_sort_column_id (1, Gtk.SortType.ASCENDING);
93@@ -142,7 +212,7 @@
94 category_plugs.set_hexpand (true);
95 category_plugs.set_selection_mode (Gtk.SelectionMode.SINGLE);
96 category_plugs.set_activate_on_single_click (true);
97-
98+
99 category_plugs.item_activated.connect (on_item_activated);
100 var cellrenderer = (Gtk.CellRendererText)category_plugs.get_cells ().nth_data (0);
101 cellrenderer.wrap_mode = Pango.WrapMode.WORD;
102@@ -195,7 +265,7 @@
103 }
104
105 store.append (out root);
106- store.set (root, Columns.ICON, icon_pixbuf, Columns.TEXT, plug.display_name,
107+ store.set (root, Columns.ICON, icon_pixbuf, Columns.TEXT, plug.display_name,
108 Columns.DESCRIPTION, plug.description, Columns.VISIBLE, true, Columns.PLUG, plug);
109 unowned SwitchboardApp app = (SwitchboardApp) GLib.Application.get_default ();
110 app.search_box.sensitive = true;
111@@ -211,6 +281,43 @@
112 }
113 }
114
115+
116+ // focus to first visible item
117+ public void grab_focus_first_icon_view () {
118+ Gtk.TreePath first_path = null;
119+
120+ if (get_first_visible_path (personal_iconview, out first_path)) {
121+ personal_iconview.grab_focus ();
122+ } else if (get_first_visible_path (hardware_iconview, out first_path)) {
123+ hardware_iconview.grab_focus ();
124+ } else if (get_first_visible_path (network_iconview, out first_path)) {
125+ network_iconview.grab_focus ();
126+ } else if (get_first_visible_path (system_iconview, out first_path)) {
127+ system_iconview.grab_focus ();
128+ }
129+ }
130+
131+ // activate first visible item
132+ public void activate_first_item () {
133+ Gtk.TreePath first_path = null;
134+
135+ if (get_first_visible_path (personal_iconview, out first_path)) {
136+ personal_iconview.item_activated (first_path);
137+ } else if (get_first_visible_path (hardware_iconview, out first_path)){
138+ hardware_iconview.item_activated (first_path);
139+ } else if (get_first_visible_path (network_iconview, out first_path)){
140+ network_iconview.item_activated (first_path);
141+ } else if (get_first_visible_path (system_iconview, out first_path)){
142+ system_iconview.item_activated (first_path);
143+ }
144+ }
145+
146+ private bool get_first_visible_path (Gtk.IconView iv, out Gtk.TreePath path) {
147+ Gtk.TreePath end;
148+
149+ return (iv.get_visible_range (out path, out end));
150+ }
151+
152 public void filter_plugs (string filter) {
153
154 var any_found = false;
155@@ -231,12 +338,12 @@
156 app.hide_alert ();
157 }
158 }
159-
160+
161 private bool search_by_category (string filter, Plug.Category category) {
162-
163+
164 Gtk.TreeModelFilter model_filter;
165 Gtk.Widget grid;
166-
167+
168 switch (category) {
169 case Switchboard.Plug.Category.PERSONAL:
170 model_filter = (Gtk.TreeModelFilter)personal_iconview.get_model ();
171@@ -274,7 +381,7 @@
172
173 return false;
174 });
175-
176+
177 if (shown == 0) {
178 grid.hide ();
179 return false;
180@@ -322,5 +429,55 @@
181
182 return null;
183 }
184+
185+ private Gtk.IconView? get_next_icon_view (Switchboard.Plug.Category category) {
186+ if (category == Plug.Category.PERSONAL) {
187+ if (hardware_iconview.is_visible ())
188+ return hardware_iconview;
189+ else
190+ category = Plug.Category.HARDWARE;
191+ }
192+
193+ if (category == Plug.Category.HARDWARE) {
194+ if (network_iconview.is_visible ())
195+ return network_iconview;
196+ else
197+ category = Plug.Category.NETWORK;
198+ }
199+
200+ if (category == Plug.Category.NETWORK) {
201+ if (system_iconview.is_visible ())
202+ return system_iconview;
203+ else
204+ category = Plug.Category.SYSTEM;
205+ }
206+
207+ return null;
208+ }
209+
210+ private Gtk.IconView? get_prev_icon_view (Switchboard.Plug.Category category) {
211+ if (category == Plug.Category.SYSTEM) {
212+ if (network_iconview.is_visible ())
213+ return network_iconview;
214+ else
215+ category = Plug.Category.NETWORK;
216+ }
217+
218+ if (category == Plug.Category.NETWORK) {
219+ if (hardware_iconview.is_visible ())
220+ return hardware_iconview;
221+ else
222+ category = Plug.Category.HARDWARE;
223+ }
224+
225+ if (category == Plug.Category.HARDWARE) {
226+ if (personal_iconview.is_visible ())
227+ return personal_iconview;
228+ else
229+ category = Plug.Category.SYSTEM;
230+ }
231+
232+ return null;
233+ }
234 }
235 }
236
237=== modified file 'src/Switchboard.vala'
238--- src/Switchboard.vala 2015-02-22 15:30:10 +0000
239+++ src/Switchboard.vala 2015-03-10 10:08:38 +0000
240@@ -59,6 +59,13 @@
241 private static string? plug_to_open = null;
242 private static bool opened_directly = false;
243 private static bool should_animate_next_transition = true;
244+ private const uint[] NAVIGATION_KEYS = {
245+ Gdk.Key.Up,
246+ Gdk.Key.Down,
247+ Gdk.Key.Left,
248+ Gdk.Key.Right,
249+ Gdk.Key.Return
250+ };
251
252 static const OptionEntry[] entries = {
253 { "open-plug", 'o', 0, OptionArg.STRING, ref plug_to_open, N_("Open a plug"), "PLUG_NAME" },
254@@ -402,13 +409,32 @@
255 search_box.changed.connect(() => {
256 category_view.filter_plugs(search_box.get_text ());
257 });
258+ search_box.key_press_event.connect ((event) => {
259+ if (event.keyval == Gdk.Key.Return) {
260+ category_view.activate_first_item ();
261+ return true;
262+ }
263+
264+ return false;
265+ });
266
267 // Focus typing to the search bar
268 main_window.key_press_event.connect ((event) => {
269 // alt+left should go back to all settings
270- if (Gdk.ModifierType.MOD1_MASK in event.state && event.keyval == Gdk.Key.Left) {
271+ if ((event.state & Gdk.ModifierType.MOD1_MASK) != 0 && event.keyval == Gdk.Key.Left) {
272 navigation_button.clicked ();
273- }
274+ return false;
275+ }
276+
277+ // Down key from search_bar should move focus to CategoryVIew
278+ if (search_box.has_focus && event.keyval == Gdk.Key.Down) {
279+ category_view.grab_focus_first_icon_view ();
280+ return false;
281+ }
282+
283+ // arrow key is being used by CategoryView to navigate
284+ if (event.keyval in NAVIGATION_KEYS)
285+ return false;
286
287 // Don't focus if it is a modifier or if search_box is already focused
288 if ((event.is_modifier == 0) && !search_box.has_focus)

Subscribers

People subscribed via source and target branches