Browse Source

fractal-gtk: Improve list keyboard navigation

Previously on both the room list and the message list,
our GtkScrolledWindows would not scroll with our focus.
This meant users that relied on keyboard navigation were
stuck at the top of our room list and the bottom of our
message list.

Now we scroll both lists as focus moves, improving
navigation for keyboard users.
environments/review-bilelmouss-0wa3h6/deployments/78
Christopher Davis 7 years ago committed by Daniel Garcia Moreno
parent
commit
408c7ce2da
  1. 2
      fractal-gtk/res/ui/main_window.ui
  2. 2
      fractal-gtk/src/appop/mod.rs
  3. 14
      fractal-gtk/src/appop/room.rs
  4. 101
      fractal-gtk/src/widgets/roomlist.rs
  5. 8
      fractal-gtk/src/widgets/scroll_widget.rs

2
fractal-gtk/res/ui/main_window.ui

@ -51,7 +51,7 @@
</packing>
</child>
<child>
<object class="GtkScrolledWindow">
<object class="GtkScrolledWindow" id="roomlist_scroll">
<property name="width_request">200</property>
<property name="visible">True</property>
<property name="can_focus">True</property>

2
fractal-gtk/src/appop/mod.rs

@ -102,7 +102,7 @@ impl AppOp {
msg_queue: vec![],
sending_message: false,
state: AppState::Login,
roomlist: widgets::RoomList::new(None),
roomlist: widgets::RoomList::new(None, None),
since: None,
unsent_messages: HashMap::new(),

14
fractal-gtk/src/appop/room.rs

@ -90,7 +90,19 @@ impl AppOp {
container.remove(ch);
}
self.roomlist = widgets::RoomList::new(Some(self.server_url.clone()));
let scrolledwindow: gtk::ScrolledWindow = self
.ui
.builder
.get_object("roomlist_scroll")
.expect("Couldn't find room_container in ui file.");
let adj = scrolledwindow.get_vadjustment();
scrolledwindow.get_child().map(|child| {
child.downcast_ref::<gtk::Container>().map(|container| {
adj.clone().map(|a| container.set_focus_vadjustment(&a));
});
});
self.roomlist = widgets::RoomList::new(adj, Some(self.server_url.clone()));
self.roomlist.add_rooms(roomlist);
container.add(self.roomlist.widget());

101
fractal-gtk/src/widgets/roomlist.rs

@ -50,6 +50,13 @@ impl RoomUpdated {
}
}
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum RoomListType {
Invites,
Rooms,
Favorites,
}
pub struct RoomListGroup {
pub rooms: HashMap<String, RoomRow>,
pub baseu: Url,
@ -396,6 +403,7 @@ impl RGroup {
pub struct RoomList {
pub baseu: Url,
widget: gtk::Box,
adj: Option<gtk::Adjustment>,
inv: RGroup,
fav: RGroup,
@ -415,7 +423,7 @@ macro_rules! run_in_group {
}
impl RoomList {
pub fn new(url: Option<String>) -> RoomList {
pub fn new(adj: Option<gtk::Adjustment>, url: Option<String>) -> RoomList {
let widget = gtk::Box::new(gtk::Orientation::Vertical, 6);
let baseu = get_url(url);
@ -438,6 +446,7 @@ impl RoomList {
let rl = RoomList {
baseu,
widget,
adj,
inv,
fav,
rooms,
@ -556,6 +565,7 @@ impl RoomList {
self.widget.add(self.fav.get().widget());
self.widget.add(self.rooms.get().widget());
self.connect_select();
self.connect_keynav();
self.show_and_hide();
@ -627,9 +637,98 @@ impl RoomList {
});
}
pub fn connect_keynav(&self) {
let weak_inv_lb = self.inv.get().list.downgrade();
let weak_fav_lb = self.fav.get().list.downgrade();
let weak_room_lb = self.rooms.get().list.downgrade();
let adj = self.adj.clone();
let type_ = RoomListType::Invites;
self.inv.get().list.connect_keynav_failed(move |_, d| {
let inv_lb = upgrade_weak!(weak_inv_lb, gtk::Inhibit(false));
let fav_lb = upgrade_weak!(weak_fav_lb, gtk::Inhibit(false));
let room_lb = upgrade_weak!(weak_room_lb, gtk::Inhibit(false));
keynav_cb(d, &inv_lb, &fav_lb, &room_lb, adj.clone(), type_)
});
let weak_fav_lb = self.fav.get().list.downgrade();
let weak_inv_lb = self.inv.get().list.downgrade();
let weak_room_lb = self.rooms.get().list.downgrade();
let adj = self.adj.clone();
let type_ = RoomListType::Favorites;
self.fav.get().list.connect_keynav_failed(move |_, d| {
let fav_lb = upgrade_weak!(weak_fav_lb, gtk::Inhibit(false));
let inv_lb = upgrade_weak!(weak_inv_lb, gtk::Inhibit(false));
let room_lb = upgrade_weak!(weak_room_lb, gtk::Inhibit(false));
keynav_cb(d, &inv_lb, &fav_lb, &room_lb, adj.clone(), type_)
});
let weak_rooms_lb = self.rooms.get().list.downgrade();
let weak_inv_lb = self.inv.get().list.downgrade();
let weak_fav_lb = self.fav.get().list.downgrade();
let adj = self.adj.clone();
let type_ = RoomListType::Rooms;
self.rooms.get().list.connect_keynav_failed(move |_, d| {
let rooms_lb = upgrade_weak!(weak_rooms_lb, gtk::Inhibit(false));
let inv_lb = upgrade_weak!(weak_inv_lb, gtk::Inhibit(false));
let fav_lb = upgrade_weak!(weak_fav_lb, gtk::Inhibit(false));
keynav_cb(d, &inv_lb, &fav_lb, &rooms_lb, adj.clone(), type_)
});
}
pub fn filter_rooms(&self, term: Option<String>) {
self.inv.get().filter_rooms(&term);
self.fav.get().filter_rooms(&term);
self.rooms.get().filter_rooms(&term);
}
}
/// Navigates between the different room
/// lists seamlessly with widget focus,
/// while keeping the `gtk::ScrolledWindow` in
/// the proper position.
///
/// Translated from https://gitlab.gnome.org/GNOME/gtk/blob/d3ad6425/gtk/inspector/general.c#L655
fn keynav_cb(
direction: gtk::DirectionType,
inv_lb: &gtk::ListBox,
fav_lb: &gtk::ListBox,
room_lb: &gtk::ListBox,
adj: Option<gtk::Adjustment>,
type_: RoomListType,
) -> gtk::Inhibit {
let next: Option<&gtk::ListBox>;
next = match (direction, type_) {
(gtk::DirectionType::Down, RoomListType::Invites) => Some(fav_lb),
(gtk::DirectionType::Down, RoomListType::Favorites) => Some(room_lb),
(gtk::DirectionType::Up, RoomListType::Rooms) => Some(fav_lb),
(gtk::DirectionType::Up, RoomListType::Favorites) => Some(inv_lb),
_ => None,
};
if let Some(widget) = next {
widget.child_focus(direction);
gtk::Inhibit(true)
} else if let Some(adjustment) = adj {
let value = adjustment.get_value();
let lower = adjustment.get_lower();
let upper = adjustment.get_upper();
let page = adjustment.get_page_size();
match direction {
gtk::DirectionType::Up if value > lower => {
adjustment.set_value(lower);
gtk::Inhibit(true)
}
gtk::DirectionType::Down if value < upper - page => {
adjustment.set_value(upper - page);
gtk::Inhibit(true)
}
_ => gtk::Inhibit(false),
}
} else {
gtk::Inhibit(false)
}
}

8
fractal-gtk/src/widgets/scroll_widget.rs

@ -100,6 +100,14 @@ impl Widgets {
.add_class("messages-box");
container.add(&column);
view.get_vadjustment().map(|adj| {
view.get_child().map(|child| {
child.downcast_ref::<gtk::Container>().map(|container| {
container.set_focus_vadjustment(&adj);
});
});
});
/* add a load more Spinner */
let spinner = gtk::Spinner::new();
messages.add(&create_load_more_spn(&spinner));

Loading…
Cancel
Save