Unverified Commit accb16fb authored by Erik Reider's avatar Erik Reider Committed by GitHub

Use GTK focus mechanics for keyboard navigation in the Control Center (#626)

parent 4b21efa6
...@@ -32,7 +32,6 @@ namespace SwayNotificationCenter.Widgets { ...@@ -32,7 +32,6 @@ namespace SwayNotificationCenter.Widgets {
private HashTable<string, unowned NotificationGroup> noti_groups_name = private HashTable<string, unowned NotificationGroup> noti_groups_name =
new HashTable<string, unowned NotificationGroup> (str_hash, str_equal); new HashTable<string, unowned NotificationGroup> (str_hash, str_equal);
private int list_position = 0;
private bool list_reverse = false; private bool list_reverse = false;
// Default config values // Default config values
...@@ -132,8 +131,15 @@ namespace SwayNotificationCenter.Widgets { ...@@ -132,8 +131,15 @@ namespace SwayNotificationCenter.Widgets {
expanded_group = null; expanded_group = null;
} }
// Make sure to change focus to the sibling. Otherwise,
// the ListBox focuses the first notification.
if (list_reverse) {
navigate_up (group, true);
} else {
navigate_down (group, true);
}
list_box_controller.remove (group); list_box_controller.remove (group);
navigate_list (--list_position);
// Switches the stack page depending on the amount of notifications // Switches the stack page depending on the amount of notifications
if (list_box_controller.length < 1) { if (list_box_controller.length < 1) {
stack.set_visible_child_name (STACK_PLACEHOLDER_PAGE); stack.set_visible_child_name (STACK_PLACEHOLDER_PAGE);
...@@ -202,18 +208,9 @@ namespace SwayNotificationCenter.Widgets { ...@@ -202,18 +208,9 @@ namespace SwayNotificationCenter.Widgets {
noti_groups_name.set (param.name_id, group); noti_groups_name.set (param.name_id, group);
} }
// Set the new list position when the group receives keyboard focus
Gtk.EventControllerFocus focus_controller = new Gtk.EventControllerFocus ();
group.add_controller (focus_controller);
focus_controller.enter.connect (() => {
int i = list_box_controller.get_children ().index (group);
if (list_position != int.MAX && list_position != i) {
list_position = i;
}
});
// Switches the stack page depending on the amount of notifications // Switches the stack page depending on the amount of notifications
stack.set_visible_child_name (STACK_NOTIFICATIONS_PAGE); stack.set_visible_child_name (STACK_NOTIFICATIONS_PAGE);
list_box_controller.append (group); list_box_controller.append (group);
} }
...@@ -237,9 +234,8 @@ namespace SwayNotificationCenter.Widgets { ...@@ -237,9 +234,8 @@ namespace SwayNotificationCenter.Widgets {
stderr.printf (e.message + "\n"); stderr.printf (e.message + "\n");
} }
// Keep focus on currently focused notification // Focus the incoming notification
list_box.grab_focus (); group.grab_focus ();
navigate_list (++list_position);
} }
public void set_list_is_reversed (bool reversed) { public void set_list_is_reversed (bool reversed) {
...@@ -253,6 +249,9 @@ namespace SwayNotificationCenter.Widgets { ...@@ -253,6 +249,9 @@ namespace SwayNotificationCenter.Widgets {
public bool key_press_event_cb (uint keyval, uint keycode, Gdk.ModifierType state) { public bool key_press_event_cb (uint keyval, uint keycode, Gdk.ModifierType state) {
var children = list_box_controller.get_children (); var children = list_box_controller.get_children ();
if (!(list_box.get_focus_child () is NotificationGroup)) {
focus_first_notification ();
}
var group = (NotificationGroup) list_box.get_focus_child (); var group = (NotificationGroup) list_box.get_focus_child ();
switch (Gdk.keyval_name (keyval)) { switch (Gdk.keyval_name (keyval)) {
case "Return": case "Return":
...@@ -267,32 +266,14 @@ namespace SwayNotificationCenter.Widgets { ...@@ -267,32 +266,14 @@ namespace SwayNotificationCenter.Widgets {
break; break;
case "Delete": case "Delete":
case "BackSpace": case "BackSpace":
if (group != null) { if (group != null && !children.is_empty ()) {
int len = (int) children.length ();
if (len == 0) break;
// Add a delta so that we select the next notification
// due to it not being gone from the list yet due to
// the fade transition
int delta = 2;
if (list_reverse) {
if (children.first ().data != group) {
delta = 0;
}
list_position--;
} else {
if (list_position > 0) list_position--;
if (children.last ().data == group) {
delta = 0;
}
}
var noti = group.get_latest_notification (); var noti = group.get_latest_notification ();
if (group.only_single_notification () && noti != null) { if (group.only_single_notification () && noti != null) {
close_notification (noti.param.applied_id, true); close_notification (noti.param.applied_id, true);
break; break;
} }
group.close_all_notifications (); group.close_all_notifications ();
navigate_list (list_position + delta); break;
return true;
} }
break; break;
case "C": case "C":
...@@ -306,19 +287,16 @@ namespace SwayNotificationCenter.Widgets { ...@@ -306,19 +287,16 @@ namespace SwayNotificationCenter.Widgets {
} }
break; break;
case "Down": case "Down":
if (list_position + 1 < children.length ()) { navigate_down (group);
++list_position;
}
break; break;
case "Up": case "Up":
if (list_position > 0) --list_position; navigate_up (group);
break; break;
case "Home": case "Home":
list_position = 0; navigate_to_first_notification ();
break; break;
case "End": case "End":
list_position = ((int) children.length ()) - 1; navigate_to_last_notification ();
if (list_position == uint.MAX) list_position = 0;
break; break;
default: default:
// Pressing 1-9 to activate a notification action // Pressing 1-9 to activate a notification action
...@@ -333,7 +311,6 @@ namespace SwayNotificationCenter.Widgets { ...@@ -333,7 +311,6 @@ namespace SwayNotificationCenter.Widgets {
} }
break; break;
} }
navigate_list (list_position);
// Override the builtin list navigation // Override the builtin list navigation
return true; return true;
} }
...@@ -391,29 +368,80 @@ namespace SwayNotificationCenter.Widgets { ...@@ -391,29 +368,80 @@ namespace SwayNotificationCenter.Widgets {
} }
private void navigate_list (int i) { private void navigate_list (int i) {
if (list_box_controller.length == 0) {
return;
}
unowned Gtk.ListBoxRow ? widget = list_box.get_row_at_index (i); unowned Gtk.ListBoxRow ? widget = list_box.get_row_at_index (i);
if (widget == null) { if (widget == null) {
// Try getting the last widget // Try getting the last widget
if (list_reverse) { if (list_reverse) {
widget = list_box.get_row_at_index (0); widget = list_box.get_row_at_index (0);
} else { } else {
int len = ((int) list_box_controller.length) - 1; int len = list_box_controller.length - 1;
widget = list_box.get_row_at_index (len); widget = list_box.get_row_at_index (len);
} }
} }
if (widget != null) { if (widget != null) {
widget.grab_focus (); widget.grab_focus ();
list_box.set_focus_child (widget); } else {
list_box.grab_focus ();
}
} }
private void navigate_up (NotificationGroup ? focused_group,
bool fallback_other_dir = false) {
if (list_box_controller.length == 1) {
focus_first_notification ();
return;
}
if (!(focused_group is NotificationGroup) || list_box_controller.length == 0) {
return;
}
if (list_box.get_first_child () == focused_group) {
if (fallback_other_dir) {
navigate_down (focused_group, false);
}
return;
}
focused_group.move_focus (Gtk.DirectionType.TAB_BACKWARD);
}
private void navigate_down (NotificationGroup ? focused_group,
bool fallback_other_dir = false) {
if (list_box_controller.length == 1) {
focus_first_notification ();
return;
}
if (!(focused_group is NotificationGroup) || list_box_controller.length == 0) {
return;
}
if (list_box.get_last_child () == focused_group) {
if (fallback_other_dir) {
navigate_up (focused_group, false);
}
return;
}
focused_group.move_focus (Gtk.DirectionType.TAB_FORWARD);
}
private void navigate_to_first_notification () {
int i = (list_reverse ? list_box_controller.length - 1 : 0)
.clamp (0, list_box_controller.length);
navigate_list (i);
}
private void navigate_to_last_notification () {
int i = (list_reverse ? 0 : list_box_controller.length - 1)
.clamp (0, list_box_controller.length);
navigate_list (i);
} }
private void focus_first_notification () { private void focus_first_notification () {
// Focus the first notification navigate_to_first_notification ();
list_position = (list_reverse ? (((int) list_box_controller.length) - 1) : 0)
.clamp (0, (int) list_box_controller.length);
list_box.grab_focus ();
navigate_list (list_position);
foreach (unowned Gtk.Widget w in list_box_controller.get_children ()) { foreach (unowned Gtk.Widget w in list_box_controller.get_children ()) {
var group = (NotificationGroup) w; var group = (NotificationGroup) w;
if (group != null) group.update (); if (group != null) group.update ();
......
public class IterListBoxController : Object { public class IterListBoxController : Object {
public uint length { get; private set; default = 0; } public int length { get; private set; default = 0; }
private List<Gtk.Widget> children = new List<Gtk.Widget> (); private List<Gtk.Widget> children = new List<Gtk.Widget> ();
public unowned Gtk.ListBox list_box { public unowned Gtk.ListBox list_box {
...@@ -13,11 +13,10 @@ public class IterListBoxController : Object { ...@@ -13,11 +13,10 @@ public class IterListBoxController : Object {
private void on_add (Gtk.Widget child) { private void on_add (Gtk.Widget child) {
length++; length++;
child.destroy.connect (() => { child.destroy.connect (this.remove);
children.remove (child);
});
} }
// NOTE: Not sorted
public List<weak Gtk.Widget> get_children () { public List<weak Gtk.Widget> get_children () {
return children.copy (); return children.copy ();
} }
......
...@@ -31,6 +31,7 @@ namespace SwayNotificationCenter { ...@@ -31,6 +31,7 @@ namespace SwayNotificationCenter {
set_child (dismissible); set_child (dismissible);
Gtk.Overlay overlay = new Gtk.Overlay (); Gtk.Overlay overlay = new Gtk.Overlay ();
overlay.set_can_focus (false);
dismissible.child = overlay; dismissible.child = overlay;
Gtk.Box box = new Gtk.Box (Gtk.Orientation.VERTICAL, 0); Gtk.Box box = new Gtk.Box (Gtk.Orientation.VERTICAL, 0);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment