diff --git a/fractal-gtk/res/ui/main_window.ui b/fractal-gtk/res/ui/main_window.ui index e4932e73..1cac9479 100644 --- a/fractal-gtk/res/ui/main_window.ui +++ b/fractal-gtk/res/ui/main_window.ui @@ -51,7 +51,7 @@ - + 200 True True diff --git a/fractal-gtk/src/appop/mod.rs b/fractal-gtk/src/appop/mod.rs index af5fd931..7553db6b 100644 --- a/fractal-gtk/src/appop/mod.rs +++ b/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(), diff --git a/fractal-gtk/src/appop/room.rs b/fractal-gtk/src/appop/room.rs index d652dce6..418d413d 100644 --- a/fractal-gtk/src/appop/room.rs +++ b/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::().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()); diff --git a/fractal-gtk/src/widgets/roomlist.rs b/fractal-gtk/src/widgets/roomlist.rs index 0cb9a2f9..c1158cbc 100644 --- a/fractal-gtk/src/widgets/roomlist.rs +++ b/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, pub baseu: Url, @@ -396,6 +403,7 @@ impl RGroup { pub struct RoomList { pub baseu: Url, widget: gtk::Box, + adj: Option, inv: RGroup, fav: RGroup, @@ -415,7 +423,7 @@ macro_rules! run_in_group { } impl RoomList { - pub fn new(url: Option) -> RoomList { + pub fn new(adj: Option, url: Option) -> 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) { 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: >k::ListBox, + fav_lb: >k::ListBox, + room_lb: >k::ListBox, + adj: Option, + type_: RoomListType, +) -> gtk::Inhibit { + let next: Option<>k::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) + } +} diff --git a/fractal-gtk/src/widgets/scroll_widget.rs b/fractal-gtk/src/widgets/scroll_widget.rs index 031d8e07..f848384b 100644 --- a/fractal-gtk/src/widgets/scroll_widget.rs +++ b/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::().map(|container| { + container.set_focus_vadjustment(&adj); + }); + }); + }); + /* add a load more Spinner */ let spinner = gtk::Spinner::new(); messages.add(&create_load_more_spn(&spinner));