Skip to content

Commit accb16f

Browse files
authored
Use GTK focus mechanics for keyboard navigation in the Control Center (#626)
1 parent 4b21efa commit accb16f

3 files changed

Lines changed: 82 additions & 54 deletions

File tree

src/controlCenter/widgets/notifications/notifications.vala

Lines changed: 78 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,6 @@ namespace SwayNotificationCenter.Widgets {
3232
private HashTable<string, unowned NotificationGroup> noti_groups_name =
3333
new HashTable<string, unowned NotificationGroup> (str_hash, str_equal);
3434

35-
private int list_position = 0;
3635
private bool list_reverse = false;
3736

3837
// Default config values
@@ -132,8 +131,15 @@ namespace SwayNotificationCenter.Widgets {
132131
expanded_group = null;
133132
}
134133

134+
// Make sure to change focus to the sibling. Otherwise,
135+
// the ListBox focuses the first notification.
136+
if (list_reverse) {
137+
navigate_up (group, true);
138+
} else {
139+
navigate_down (group, true);
140+
}
135141
list_box_controller.remove (group);
136-
navigate_list (--list_position);
142+
137143
// Switches the stack page depending on the amount of notifications
138144
if (list_box_controller.length < 1) {
139145
stack.set_visible_child_name (STACK_PLACEHOLDER_PAGE);
@@ -202,18 +208,9 @@ namespace SwayNotificationCenter.Widgets {
202208
noti_groups_name.set (param.name_id, group);
203209
}
204210

205-
// Set the new list position when the group receives keyboard focus
206-
Gtk.EventControllerFocus focus_controller = new Gtk.EventControllerFocus ();
207-
group.add_controller (focus_controller);
208-
focus_controller.enter.connect (() => {
209-
int i = list_box_controller.get_children ().index (group);
210-
if (list_position != int.MAX && list_position != i) {
211-
list_position = i;
212-
}
213-
});
214-
215211
// Switches the stack page depending on the amount of notifications
216212
stack.set_visible_child_name (STACK_NOTIFICATIONS_PAGE);
213+
217214
list_box_controller.append (group);
218215
}
219216

@@ -237,9 +234,8 @@ namespace SwayNotificationCenter.Widgets {
237234
stderr.printf (e.message + "\n");
238235
}
239236

240-
// Keep focus on currently focused notification
241-
list_box.grab_focus ();
242-
navigate_list (++list_position);
237+
// Focus the incoming notification
238+
group.grab_focus ();
243239
}
244240

245241
public void set_list_is_reversed (bool reversed) {
@@ -253,6 +249,9 @@ namespace SwayNotificationCenter.Widgets {
253249

254250
public bool key_press_event_cb (uint keyval, uint keycode, Gdk.ModifierType state) {
255251
var children = list_box_controller.get_children ();
252+
if (!(list_box.get_focus_child () is NotificationGroup)) {
253+
focus_first_notification ();
254+
}
256255
var group = (NotificationGroup) list_box.get_focus_child ();
257256
switch (Gdk.keyval_name (keyval)) {
258257
case "Return":
@@ -267,32 +266,14 @@ namespace SwayNotificationCenter.Widgets {
267266
break;
268267
case "Delete":
269268
case "BackSpace":
270-
if (group != null) {
271-
int len = (int) children.length ();
272-
if (len == 0) break;
273-
// Add a delta so that we select the next notification
274-
// due to it not being gone from the list yet due to
275-
// the fade transition
276-
int delta = 2;
277-
if (list_reverse) {
278-
if (children.first ().data != group) {
279-
delta = 0;
280-
}
281-
list_position--;
282-
} else {
283-
if (list_position > 0) list_position--;
284-
if (children.last ().data == group) {
285-
delta = 0;
286-
}
287-
}
269+
if (group != null && !children.is_empty ()) {
288270
var noti = group.get_latest_notification ();
289271
if (group.only_single_notification () && noti != null) {
290272
close_notification (noti.param.applied_id, true);
291273
break;
292274
}
293275
group.close_all_notifications ();
294-
navigate_list (list_position + delta);
295-
return true;
276+
break;
296277
}
297278
break;
298279
case "C":
@@ -306,19 +287,16 @@ namespace SwayNotificationCenter.Widgets {
306287
}
307288
break;
308289
case "Down":
309-
if (list_position + 1 < children.length ()) {
310-
++list_position;
311-
}
290+
navigate_down (group);
312291
break;
313292
case "Up":
314-
if (list_position > 0) --list_position;
293+
navigate_up (group);
315294
break;
316295
case "Home":
317-
list_position = 0;
296+
navigate_to_first_notification ();
318297
break;
319298
case "End":
320-
list_position = ((int) children.length ()) - 1;
321-
if (list_position == uint.MAX) list_position = 0;
299+
navigate_to_last_notification ();
322300
break;
323301
default:
324302
// Pressing 1-9 to activate a notification action
@@ -333,7 +311,6 @@ namespace SwayNotificationCenter.Widgets {
333311
}
334312
break;
335313
}
336-
navigate_list (list_position);
337314
// Override the builtin list navigation
338315
return true;
339316
}
@@ -391,29 +368,80 @@ namespace SwayNotificationCenter.Widgets {
391368
}
392369

393370
private void navigate_list (int i) {
371+
if (list_box_controller.length == 0) {
372+
return;
373+
}
374+
394375
unowned Gtk.ListBoxRow ? widget = list_box.get_row_at_index (i);
395376
if (widget == null) {
396377
// Try getting the last widget
397378
if (list_reverse) {
398379
widget = list_box.get_row_at_index (0);
399380
} else {
400-
int len = ((int) list_box_controller.length) - 1;
381+
int len = list_box_controller.length - 1;
401382
widget = list_box.get_row_at_index (len);
402383
}
403384
}
404385
if (widget != null) {
405386
widget.grab_focus ();
406-
list_box.set_focus_child (widget);
387+
} else {
388+
list_box.grab_focus ();
389+
}
390+
}
391+
392+
private void navigate_up (NotificationGroup ? focused_group,
393+
bool fallback_other_dir = false) {
394+
if (list_box_controller.length == 1) {
395+
focus_first_notification ();
396+
return;
397+
}
398+
if (!(focused_group is NotificationGroup) || list_box_controller.length == 0) {
399+
return;
400+
}
401+
if (list_box.get_first_child () == focused_group) {
402+
if (fallback_other_dir) {
403+
navigate_down (focused_group, false);
404+
}
405+
return;
406+
}
407+
408+
focused_group.move_focus (Gtk.DirectionType.TAB_BACKWARD);
409+
}
410+
411+
private void navigate_down (NotificationGroup ? focused_group,
412+
bool fallback_other_dir = false) {
413+
if (list_box_controller.length == 1) {
414+
focus_first_notification ();
415+
return;
416+
}
417+
if (!(focused_group is NotificationGroup) || list_box_controller.length == 0) {
418+
return;
419+
}
420+
if (list_box.get_last_child () == focused_group) {
421+
if (fallback_other_dir) {
422+
navigate_up (focused_group, false);
423+
}
424+
return;
407425
}
426+
427+
focused_group.move_focus (Gtk.DirectionType.TAB_FORWARD);
428+
}
429+
430+
private void navigate_to_first_notification () {
431+
int i = (list_reverse ? list_box_controller.length - 1 : 0)
432+
.clamp (0, list_box_controller.length);
433+
navigate_list (i);
434+
}
435+
436+
private void navigate_to_last_notification () {
437+
int i = (list_reverse ? 0 : list_box_controller.length - 1)
438+
.clamp (0, list_box_controller.length);
439+
navigate_list (i);
408440
}
409441

410442
private void focus_first_notification () {
411-
// Focus the first notification
412-
list_position = (list_reverse ? (((int) list_box_controller.length) - 1) : 0)
413-
.clamp (0, (int) list_box_controller.length);
443+
navigate_to_first_notification ();
414444

415-
list_box.grab_focus ();
416-
navigate_list (list_position);
417445
foreach (unowned Gtk.Widget w in list_box_controller.get_children ()) {
418446
var group = (NotificationGroup) w;
419447
if (group != null) group.update ();

src/iterHelpers/iterListBoxController.vala

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
public class IterListBoxController : Object {
2-
public uint length { get; private set; default = 0; }
2+
public int length { get; private set; default = 0; }
33

44
private List<Gtk.Widget> children = new List<Gtk.Widget> ();
55
public unowned Gtk.ListBox list_box {
@@ -13,11 +13,10 @@ public class IterListBoxController : Object {
1313

1414
private void on_add (Gtk.Widget child) {
1515
length++;
16-
child.destroy.connect (() => {
17-
children.remove (child);
18-
});
16+
child.destroy.connect (this.remove);
1917
}
2018

19+
// NOTE: Not sorted
2120
public List<weak Gtk.Widget> get_children () {
2221
return children.copy ();
2322
}

src/notificationGroup/notificationGroup.vala

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ namespace SwayNotificationCenter {
3131
set_child (dismissible);
3232

3333
Gtk.Overlay overlay = new Gtk.Overlay ();
34+
overlay.set_can_focus (false);
3435
dismissible.child = overlay;
3536

3637
Gtk.Box box = new Gtk.Box (Gtk.Orientation.VERTICAL, 0);

0 commit comments

Comments
 (0)