Skip to content

Commit

Permalink
feat(chatview-ux): multiple UX improvements (#138)
Browse files Browse the repository at this point in the history
* Notifications when a contact goes `online` and `offline`.
* Keyboard shortcut to select a messages selection:
  * `Ctrl+C`: Copy the messages selected into Clipboard.
  * `Ctrl+Shift+Q`: Quote the selection.
* Keyboard shortcut for insering a new line:
  * `Shift+Return`: Insert a newline at the cursor position.
* Some bug fixes.
  • Loading branch information
SkyzohKey authored Jul 6, 2016
1 parent 6c87496 commit 68aee5f
Show file tree
Hide file tree
Showing 8 changed files with 131 additions and 52 deletions.
1 change: 1 addition & 0 deletions res/ui/chat-view.ui
Original file line number Diff line number Diff line change
Expand Up @@ -1446,6 +1446,7 @@
<property name="visible">True</property>
<property name="sensitive">False</property>
<property name="can_focus">True</property>
<property name="events">GDK_KEY_PRESS_MASK | GDK_STRUCTURE_MASK</property>
<property name="max_width_chars">5</property>
<signal name="activate" handler="send_message" swapped="no"/>
<style>
Expand Down
66 changes: 48 additions & 18 deletions src/ChatView.vala
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ class Ricin.ChatView : Gtk.Box {

[Signal (action = true)] private signal void copy_messages_selection ();
[Signal (action = true)] private signal void quote_messages_selection ();
[Signal (action = true)] private signal void entry_insert_newline ();

public Tox.Friend fr;
private weak Tox.Tox handle;
Expand All @@ -59,7 +60,7 @@ class Ricin.ChatView : Gtk.Box {
private Settings settings;

private Tox.UserStatus last_status;
private string last_message_sender;
private string last_message_sender { get; set; default = "ricin"; }
private string last_message = null;
private bool is_bottom = true;

Expand Down Expand Up @@ -401,21 +402,21 @@ class Ricin.ChatView : Gtk.Box {
string txt = "";

if (item is MessageListRow) {
name = ((MessageListRow) item).author;
name = "[" + ((MessageListRow) item).author + "]";
txt = ((MessageListRow) item).label_message.get_text ();
} else if (item is SystemMessageListRow) {
name = "* ";
txt = ((SystemMessageListRow) item).label_message.get_text ();
} else if (item is QuoteMessageListRow) {
name = ((QuoteMessageListRow) item).author;
name = "[" + ((QuoteMessageListRow) item).author + "]";
txt = ((QuoteMessageListRow) item).get_quote ();
}

if (as_quote) {
sb.append_c ('>');
}
if (include_names) {
sb.append (@"[$name] ");
sb.append (@"$name ");
}

sb.append (txt);
Expand All @@ -442,6 +443,8 @@ class Ricin.ChatView : Gtk.Box {
string selection = this.get_selected_messages (false, true);
Gtk.Clipboard.get (Gdk.SELECTION_CLIPBOARD).set_text (selection, -1);
this.messages_list.unselect_all ();
this.entry.grab_focus_without_selecting ();
this.entry.set_position (-1);
});
menu_copy_quote.activate.connect (() => {
if (this.messages_selected () == false) {
Expand All @@ -451,6 +454,8 @@ class Ricin.ChatView : Gtk.Box {
string quote = this.get_selected_messages (true, true);
Gtk.Clipboard.get (Gdk.SELECTION_CLIPBOARD).set_text (quote, -1);
this.messages_list.unselect_all ();
this.entry.grab_focus_without_selecting ();
this.entry.set_position (-1);
});
menu_quote_selection.activate.connect (() => {
if (this.messages_selected () == false) {
Expand All @@ -461,6 +466,7 @@ class Ricin.ChatView : Gtk.Box {
this.entry.set_text (quote);
this.messages_list.unselect_all ();
this.entry.grab_focus_without_selecting ();
this.entry.set_position (-1);
});

menu.append (menu_copy_selection);
Expand All @@ -484,13 +490,21 @@ class Ricin.ChatView : Gtk.Box {
});
}

/*private MainWindow get_top () {
}*/

private void init_messages_shortcuts () {
var main_window = ((MainWindow) this.get_toplevel ());
Gtk.AccelGroup accel_group = new Gtk.AccelGroup ();
main_window.add_accel_group (accel_group);
//var main_window = ((MainWindow) this.get_toplevel ().get_toplevel ());

/**
* Keyboard shortcut for copying or quoting selected messages.
* Shortcut for Ctrl+C: Copy selected messages if selection > 0
**/
this.add_accelerator (
"copy-messages-selection", MainWindow.accel_group, Gdk.keyval_from_name("C"),
Gdk.ModifierType.CONTROL_MASK, Gtk.AccelFlags.VISIBLE
);

this.copy_messages_selection.connect (() => {
if (this.messages_selected () == false) {
return;
Expand All @@ -499,8 +513,18 @@ class Ricin.ChatView : Gtk.Box {
string selection = this.get_selected_messages (false, true);
Gtk.Clipboard.get (Gdk.SELECTION_CLIPBOARD).set_text (selection, -1);
this.messages_list.unselect_all ();
this.entry.grab_focus_without_selecting ();
this.entry.set_position (-1);
});

/**
* Shortcut for Ctrl+Shift+Q: Quote selected messages if selection > 0
**/
this.add_accelerator (
"quote-messages-selection", MainWindow.accel_group, Gdk.keyval_from_name("Q"),
Gdk.ModifierType.CONTROL_MASK | Gdk.ModifierType.SHIFT_MASK, Gtk.AccelFlags.VISIBLE
);

this.quote_messages_selection.connect (() => {
if (this.messages_selected () == false) {
return;
Expand All @@ -510,23 +534,29 @@ class Ricin.ChatView : Gtk.Box {
this.entry.set_text (quote);
this.messages_list.unselect_all ();
this.entry.grab_focus_without_selecting ();
this.entry.set_position (-1);
});

/**
* Shortcut for Ctrl+C: Copy selected messages if selection > 0
* Shortcut for Shift+Enter in this.entry: Add a newline (\n).
**/
this.add_accelerator (
"copy-messages-selection", accel_group, Gdk.keyval_from_name("C"),
Gdk.ModifierType.CONTROL_MASK, Gtk.AccelFlags.VISIBLE
"entry-insert-newline", MainWindow.accel_group, Gdk.Key.Return,
Gdk.ModifierType.SHIFT_MASK, Gtk.AccelFlags.VISIBLE
);

/**
* Shortcut for Ctrl+Shift+Q: Quote selected messages if selection > 0
**/
this.add_accelerator (
"quote-messages-selection", accel_group, Gdk.keyval_from_name("Q"),
Gdk.ModifierType.CONTROL_MASK | Gdk.ModifierType.SHIFT_MASK, Gtk.AccelFlags.VISIBLE
);
this.entry_insert_newline.connect (() => {
debug ("entry_insert_newline: Called.");

int cursor_position = this.entry.get_position ();
string text = this.entry.get_text ();
string newline = "\n";

this.entry.insert_at_cursor (newline);
this.entry.grab_focus_without_selecting ();
this.entry.set_position (cursor_position + newline.length);

});
}

public void show_notice (string text, string icon_name = "help-info-symbolic") {
Expand Down
18 changes: 16 additions & 2 deletions src/FriendListRow.vala
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,15 @@ class Ricin.FriendListRow : Gtk.ListBoxRow {
});

fr.notify["status"].connect ((obj, prop) => {
if (fr.status == Tox.UserStatus.ONLINE || fr.status == Tox.UserStatus.OFFLINE) {
string status_str = Util.status_to_string (this.fr.status);
Notification.notify (
this.fr.name + _(" is now ") + status_str,
this.fr.status_message,
3000
);
}

string icon = Util.status_to_icon (this.fr.status, 0);
this.userstatus.set_from_resource (@"/chat/tox/ricin/images/status/$icon.png");
this.changed (); // we sort by user status
Expand All @@ -99,11 +108,13 @@ class Ricin.FriendListRow : Gtk.ListBoxRow {
fr.action.connect (this.notify_new_messages);

this.activate.connect (() => {
var main_window = ((MainWindow) this.get_toplevel ());
main_window.global_unread_counter -= this.unreadCount;

this.unreadCount = 0;
this.update_icon ();
this.changed ();

var main_window = ((MainWindow) this.get_toplevel ());
main_window.friendlist.invalidate_filter ();
});
}
Expand All @@ -115,7 +126,9 @@ class Ricin.FriendListRow : Gtk.ListBoxRow {
if (this.unreadCount == 0) {
this.label_unread_count.visible = false;
} else {
this.label_unread_count.set_text (@"$(this.unreadCount)");
string count_str = this.unreadCount > 10 ? "<b>10+</b>" : @"$(this.unreadCount)";

this.label_unread_count.set_markup (count_str);
this.label_unread_count.visible = true;
}
}
Expand All @@ -127,6 +140,7 @@ class Ricin.FriendListRow : Gtk.ListBoxRow {
}

this.unreadCount++;
main_window.global_unread_counter += this.unreadCount;
this.update_icon ();
}

Expand Down
67 changes: 41 additions & 26 deletions src/MainWindow.vala
Original file line number Diff line number Diff line change
Expand Up @@ -51,18 +51,20 @@ public class Ricin.MainWindow : Gtk.ApplicationWindow {

public Tox.Tox tox;
public string focused_view;
public int global_unread_counter = 0;
public static Gtk.AccelGroup accel_group;

private Gtk.ListBoxRow selected_row;
private Gtk.Menu menu_statusicon_main;
private Gtk.StatusIcon statusicon_main;
private Settings settings;
private string window_title;
private string profile;

public signal void notify_message (string message, int timeout = 5000);
[Signal (action = true)] private signal void change_chat_up ();
[Signal (action = true)] private signal void change_chat_down ();

public signal void notify_message (string message, int timeout = 5000);

public MainWindow (Gtk.Application app, string profile, string? password = null, bool is_new = false) {
Object (application: app);
this.settings = Settings.instance;
Expand All @@ -81,6 +83,13 @@ public class Ricin.MainWindow : Gtk.ApplicationWindow {
this.set_icon (app_icon);

this.init_keyboard_shortcuts ();
this.notify["global-unread-counter"].connect ((obj, prop) => {
if (this.global_unread_counter == 0) {
this.set_urgency_hint (false);
} else {
this.set_urgency_hint (true);
}
});

var opts = Tox.Options.create ();
opts.ipv6_enabled = this.settings.network_ipv6;
Expand Down Expand Up @@ -329,45 +338,51 @@ public class Ricin.MainWindow : Gtk.ApplicationWindow {
}

private void init_keyboard_shortcuts () {
Gtk.AccelGroup accel_group = new Gtk.AccelGroup ();
this.add_accel_group (accel_group);
this.accel_group = new Gtk.AccelGroup ();
this.add_accel_group (this.accel_group);

/**
* Keyboard shortcut for switching to previous/next contact's chatview.
* FIXME: Ctrl+Up | Ctrl+Down doesn't call these signals.
* Shortcut for Ctrl+Up: Change the chat view to the previous one.
**/
this.change_chat_up.connect (() => {
var index = this.selected_row.get_index ();
if (index == 0) {
return;
}
var prev_row = this.friendlist.get_row_at_index (index - 1);
this.selected_row = prev_row;
this.selected_row.activate ();
this.friendlist.select_row (prev_row);
});
this.add_accelerator (
"change-chat-up", accel_group, Gdk.keyval_from_name("Up"),
Gdk.ModifierType.CONTROL_MASK, Gtk.AccelFlags.VISIBLE
);

this.change_chat_down.connect (() => {
var index = this.selected_row.get_index ();
var max = this.friendlist.get_children ().length ();
int index = this.selected_row.get_index ();
uint max = this.friendlist.get_children ().length ();

if (index == max) {
return;
}

var next_row = this.friendlist.get_row_at_index (index + 1);
this.selected_row = next_row;
this.selected_row.activate ();
this.friendlist.select_row (next_row);
});

/**
* Shortcut for Ctrl+Up: Change the chat view to the previous one.
**/
this.add_accelerator ("change-chat-up", accel_group, Gdk.keyval_from_name("Up"),
Gdk.ModifierType.CONTROL_MASK, Gtk.AccelFlags.VISIBLE);

/**
* Shortcut for Ctrl+Down: Change the chat view to the next one.
**/
this.add_accelerator ("change-chat-down", accel_group, Gdk.keyval_from_name("Down"),
Gdk.ModifierType.CONTROL_MASK, Gtk.AccelFlags.VISIBLE);
this.add_accelerator (
"change-chat-down", accel_group, Gdk.keyval_from_name("Down"),
Gdk.ModifierType.CONTROL_MASK, Gtk.AccelFlags.VISIBLE
);

this.change_chat_up.connect (() => {
int index = this.selected_row.get_index ();

if (index == 0) {
return;
}

var prev_row = this.friendlist.get_row_at_index (index - 1);
this.selected_row = prev_row;
this.selected_row.activate ();
this.friendlist.select_row (prev_row);
});
}

private async void append_friends () {
Expand Down
1 change: 1 addition & 0 deletions src/Notification.vala
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ class Ricin.Notification : Object {
notif = new Notify.Notification (sender, message, null);
notif.set_image_from_pixbuf (icon);
}

notif.set_category ("im.received");
notif.set_hint ("sound-name", new Variant.string ("message-new-instant"));
notif.set_timeout (timeout);
Expand Down
3 changes: 3 additions & 0 deletions src/ProfileChooser.vala
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,9 @@ class Ricin.ProfileChooser : Gtk.ApplicationWindow {
this.set_resizable (false);

this.populate_profiles ();

this.entry_login_password.activate.connect (this.login);

this.show_all ();
}

Expand Down
7 changes: 6 additions & 1 deletion src/QuoteMessageListRow.vala
Original file line number Diff line number Diff line change
Expand Up @@ -70,11 +70,16 @@ class Ricin.QuoteMessageListRow : Gtk.ListBoxRow {
var txt = "";

if (item is QuoteLabel) {
txt = ">" + ((QuoteLabel) item).label_quote.get_text ();
txt = ((QuoteLabel) item).label_quote.get_text ();
} else if (item is PlainLabel) {
txt = ((PlainLabel) item).label_text.get_text ();
}

string[] lines = txt.split ("\n");
foreach (string line in lines) {
txt = ">" + line;
}

sb.append (txt);
sb.append_c ('\n');
}
Expand Down
Loading

0 comments on commit 68aee5f

Please sign in to comment.