[fractal] Add keyboard shortcuts for easier navigation
- From: Daniel Garcia Moreno <danigm src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [fractal] Add keyboard shortcuts for easier navigation
- Date: Tue, 14 May 2019 11:43:09 +0000 (UTC)
commit 5cd2c51b14a09ab24711a424888cf18d9fc921ba
Author: Baptiste Gelez <baptiste gelez xyz>
Date: Sat May 11 14:44:53 2019 +0100
Add keyboard shortcuts for easier navigation
- Ctrl+PageUp and Ctrl+PageDown to go to the next/previous room of the list
- Ctrl+Shift+PageUp and Ctrl+Shift+PageUp to go to the next/previous room with unread messages
- Ctrl+Home and Ctrl+End to go to the first/last room of the list
- PageUp and PageDown to go up/down in the history
Fixes #49
fractal-gtk/src/actions/global.rs | 79 ++++++++++++++++++-
fractal-gtk/src/widgets/room_history.rs | 8 ++
fractal-gtk/src/widgets/roomlist.rs | 127 +++++++++++++++++++++++++++++++
fractal-gtk/src/widgets/scroll_widget.rs | 16 +++-
4 files changed, 228 insertions(+), 2 deletions(-)
---
diff --git a/fractal-gtk/src/actions/global.rs b/fractal-gtk/src/actions/global.rs
index edd93395..f3c27afb 100644
--- a/fractal-gtk/src/actions/global.rs
+++ b/fractal-gtk/src/actions/global.rs
@@ -78,7 +78,7 @@ pub fn new(app: >k::Application, op: &Arc<Mutex<AppOp>>) {
let shortcuts = SimpleAction::new("shortcuts", None);
let about = SimpleAction::new("about", None);
- let quit = gio::SimpleAction::new("quit", None);
+ let quit = SimpleAction::new("quit", None);
let open_room = SimpleAction::new("open-room", glib::VariantTy::new("s").ok());
let back = SimpleAction::new("back", None);
@@ -90,6 +90,15 @@ pub fn new(app: >k::Application, op: &Arc<Mutex<AppOp>>) {
// TODO: send file should be a room_history action
let send_file = SimpleAction::new("send-file", None);
+ let previous_room = SimpleAction::new("previous-room", None);
+ let next_room = SimpleAction::new("next-room", None);
+ let prev_unread_room = SimpleAction::new("prev-unread-room", None);
+ let next_unread_room = SimpleAction::new("next-unread-room", None);
+ let first_room = SimpleAction::new("first-room", None);
+ let last_room = SimpleAction::new("last-room", None);
+ let older_messages = SimpleAction::new("older-messages", None);
+ let newer_messages = SimpleAction::new("newer-messages", None);
+
app.add_action(&settings);
app.add_action(&account);
app.add_action(&chat);
@@ -113,6 +122,15 @@ pub fn new(app: >k::Application, op: &Arc<Mutex<AppOp>>) {
app.add_action(&send_file);
+ app.add_action(&previous_room);
+ app.add_action(&next_room);
+ app.add_action(&prev_unread_room);
+ app.add_action(&next_unread_room);
+ app.add_action(&first_room);
+ app.add_action(&last_room);
+ app.add_action(&older_messages);
+ app.add_action(&newer_messages);
+
// When activated, shuts down the application
let app_weak = app.downgrade();
quit.connect_activate(move |_action, _parameter| {
@@ -134,6 +152,57 @@ pub fn new(app: >k::Application, op: &Arc<Mutex<AppOp>>) {
newr.connect_activate(clone!(op => move |_, _| op.lock().unwrap().new_room_dialog() ));
joinr.connect_activate(clone!(op => move |_, _| op.lock().unwrap().join_to_room_dialog() ));
+ previous_room.connect_activate(clone!(op => move |_, _| {
+ let mut op = op.lock().unwrap();
+ if let Some(id) = op.roomlist.prev_id() {
+ op.set_active_room_by_id(id);
+ }
+ }));
+ next_room.connect_activate(clone!(op => move |_, _| {
+ let mut op = op.lock().unwrap();
+ if let Some(id) = op.roomlist.next_id() {
+ op.set_active_room_by_id(id);
+ }
+ }));
+ prev_unread_room.connect_activate(clone!(op => move |_, _| {
+ let mut op = op.lock().unwrap();
+ if let Some(id) = op.roomlist.prev_unread_id() {
+ op.set_active_room_by_id(id);
+ }
+ }));
+ next_unread_room.connect_activate(clone!(op => move |_, _| {
+ let mut op = op.lock().unwrap();
+ if let Some(id) = op.roomlist.next_unread_id() {
+ op.set_active_room_by_id(id);
+ }
+ }));
+ first_room.connect_activate(clone!(op => move |_, _| {
+ let mut op = op.lock().unwrap();
+ if let Some(id) = op.roomlist.first_id() {
+ op.set_active_room_by_id(id);
+ }
+ }));
+ last_room.connect_activate(clone!(op => move |_, _| {
+ let mut op = op.lock().unwrap();
+ if let Some(id) = op.roomlist.last_id() {
+ op.set_active_room_by_id(id);
+ }
+ }));
+ older_messages.connect_activate(clone!(op => move |_, _| {
+ let mut op = op.lock().unwrap();
+ if let Some(ref mut hist) = op.history {
+ // println!("page up");
+ hist.page_up();
+ }
+ }));
+ newer_messages.connect_activate(clone!(op => move |_, _| {
+ let mut op = op.lock().unwrap();
+ if let Some(ref mut hist) = op.history {
+ // println!("page down");
+ hist.page_down();
+ }
+ }));
+
/* Store the history of views so we can go back to it, this will be kept alive by the back
* callback */
let back_history: Rc<RefCell<Vec<AppState>>> = Rc::new(RefCell::new(vec![]));
@@ -225,6 +294,14 @@ pub fn new(app: >k::Application, op: &Arc<Mutex<AppOp>>) {
/* Add Keybindings to actions */
app.set_accels_for_action("app.quit", &["<Ctrl>Q"]);
+ app.set_accels_for_action("app.previous-room", &["<Ctrl>Page_Up"]);
+ app.set_accels_for_action("app.next-room", &["<Ctrl>Page_Down"]);
+ app.set_accels_for_action("app.prev-unread-room", &["<Ctrl><Shift>Page_Up"]);
+ app.set_accels_for_action("app.next-unread-room", &["<Ctrl><Shift>Page_Down"]);
+ app.set_accels_for_action("app.first-room", &["<Ctrl>Home"]);
+ app.set_accels_for_action("app.last-room", &["<Ctrl>End"]);
+ app.set_accels_for_action("app.older-messages", &["Page_Up"]);
+ app.set_accels_for_action("app.newer-messages", &["Page_Down"]);
app.set_accels_for_action("app.back", &["Escape"]);
// connect mouse back button to app.back action
diff --git a/fractal-gtk/src/widgets/room_history.rs b/fractal-gtk/src/widgets/room_history.rs
index 6e4223a4..897bd26f 100644
--- a/fractal-gtk/src/widgets/room_history.rs
+++ b/fractal-gtk/src/widgets/room_history.rs
@@ -276,6 +276,14 @@ impl RoomHistory {
pub fn typing_notification(&mut self, typing_str: &str) {
self.rows.borrow().view.typing_notification(typing_str);
}
+
+ pub fn page_up(&mut self) {
+ self.rows.borrow_mut().view.page_up();
+ }
+
+ pub fn page_down(&mut self) {
+ self.rows.borrow_mut().view.page_down();
+ }
}
/* This function creates the content for a Row based on the conntent of msg */
diff --git a/fractal-gtk/src/widgets/roomlist.rs b/fractal-gtk/src/widgets/roomlist.rs
index c1158cbc..c9132444 100644
--- a/fractal-gtk/src/widgets/roomlist.rs
+++ b/fractal-gtk/src/widgets/roomlist.rs
@@ -318,6 +318,71 @@ impl RoomListGroup {
}
}
+ /// Find the ID of a room after or before the current one in the list
+ ///
+ /// # Parameters
+ ///
+ /// - `unread_only`: true to only look for rooms with unread messages
+ /// - `direction`: `-1` for the previous room, `+1` for the next
+ ///
+ /// # Return value
+ ///
+ /// `(Room id if found, go to previous group, go to next group)`
+ fn sibling_id(&self, unread_only: bool, direction: i32) -> (Option<String>, bool, bool) {
+ match self.list.get_selected_row() {
+ Some(row) => {
+ let rv = self.roomvec.lock().unwrap();
+ let mut idx = row.get_index() + direction;
+ while unread_only
+ && 0 <= idx
+ && (idx as usize) < rv.len()
+ && rv[idx as usize].room.notifications == 0
+ {
+ idx += direction;
+ }
+
+ if 0 <= idx && (idx as usize) < rv.len() {
+ (Some(rv[idx as usize].room.id.clone()), false, false)
+ } else {
+ (None, idx < 0, idx >= 0)
+ }
+ }
+ None => (None, false, false),
+ }
+ }
+
+ fn first_id(&self, unread_only: bool) -> Option<String> {
+ self.roomvec
+ .lock()
+ .unwrap()
+ .iter()
+ .filter(|r| {
+ if unread_only {
+ r.room.notifications > 0
+ } else {
+ true
+ }
+ })
+ .next()
+ .map(|r| r.room.id.clone())
+ }
+
+ fn last_id(&self, unread_only: bool) -> Option<String> {
+ self.roomvec
+ .lock()
+ .unwrap()
+ .iter()
+ .filter(|r| {
+ if unread_only {
+ r.room.notifications > 0
+ } else {
+ true
+ }
+ })
+ .last()
+ .map(|r| r.room.id.clone())
+ }
+
pub fn add_rooms(&mut self, mut array: Vec<Room>) {
array.sort_by_key(|ref x| match x.messages.last() {
Some(l) => l.date,
@@ -460,6 +525,68 @@ impl RoomList {
run_in_group!(self, &r.to_string(), set_selected, Some(r.to_string()));
}
+ fn sibling_id(&self, unread_only: bool, direction: i32) -> Option<String> {
+ let (room, _, next) = self.inv.get().sibling_id(unread_only, direction);
+
+ if let Some(room) = room {
+ Some(room)
+ } else if next {
+ self.fav.get().first_id(unread_only)
+ } else {
+ let (room, prev, next) = self.fav.get().sibling_id(unread_only, direction);
+
+ if let Some(room) = room {
+ Some(room)
+ } else if prev {
+ self.inv.get().last_id(unread_only)
+ } else if next {
+ self.rooms.get().first_id(unread_only)
+ } else {
+ let (room, prev, _) = self.rooms.get().sibling_id(unread_only, direction);
+
+ if let Some(room) = room {
+ Some(room)
+ } else if prev {
+ self.fav.get().last_id(unread_only)
+ } else {
+ None
+ }
+ }
+ }
+ }
+
+ pub fn next_id(&self) -> Option<String> {
+ self.sibling_id(false, 1)
+ }
+
+ pub fn prev_id(&self) -> Option<String> {
+ self.sibling_id(false, -1)
+ }
+
+ pub fn next_unread_id(&self) -> Option<String> {
+ self.sibling_id(true, 1)
+ }
+
+ pub fn prev_unread_id(&self) -> Option<String> {
+ self.sibling_id(true, -1)
+ }
+
+ pub fn first_id(&self) -> Option<String> {
+ self.inv
+ .get()
+ .first_id(false)
+ .or_else(|| self.fav.get().first_id(false))
+ .or_else(|| self.rooms.get().first_id(false))
+ }
+
+ pub fn last_id(&self) -> Option<String> {
+ self.rooms
+ .get()
+ .last_id(false)
+ .or_else(|| self.fav.get().last_id(false))
+ .or_else(|| self.inv.get().last_id(false))
+ }
+
pub fn unselect(&self) {
self.inv.get().set_selected(None);
self.fav.get().set_selected(None);
diff --git a/fractal-gtk/src/widgets/scroll_widget.rs b/fractal-gtk/src/widgets/scroll_widget.rs
index f848384b..f3b710f4 100644
--- a/fractal-gtk/src/widgets/scroll_widget.rs
+++ b/fractal-gtk/src/widgets/scroll_widget.rs
@@ -20,7 +20,7 @@ enum Position {
#[allow(dead_code)]
pub struct ScrollWidget {
upper: Rc<Cell<f64>>,
- value: Rc<Cell<f64>>,
+ value: Rc<Cell<f64>>, // FIXME: is it really used anywhere?
balance: Rc<Cell<Option<Position>>>,
autoscroll: Rc<Cell<bool>>,
/* whether a request for more messages has been send or not */
@@ -287,6 +287,20 @@ impl ScrollWidget {
self.widgets.typing_label.set_markup(typing_str);
}
}
+
+ pub fn page_up(&mut self) {
+ if let Some(adj) = self.widgets.view.get_vadjustment() {
+ adj.set_value(adj.get_value() - adj.get_page_size());
+ self.upper.set(adj.get_upper());
+ }
+ }
+
+ pub fn page_down(&mut self) {
+ if let Some(adj) = self.widgets.view.get_vadjustment() {
+ adj.set_value(adj.get_value() + adj.get_page_size());
+ self.upper.set(adj.get_upper());
+ }
+ }
}
/* Functions to animate the scroll */
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]