Merge branch 'small-MR-history' into 'master'

Message widget refactor and adds uitype for messages

See merge request World/fractal!220
This commit is contained in:
Daniel Garcia Moreno 2018-09-09 16:54:51 +00:00
commit 76d9a604c7
6 changed files with 368 additions and 236 deletions

View file

@ -83,6 +83,7 @@ fractal-gtk/src/passwd.rs
fractal-gtk/src/static_resources.rs
fractal-gtk/src/uibuilder.rs
fractal-gtk/src/util.rs
fractal-gtk/src/uitypes.rs
fractal-gtk/src/widgets/address.rs
fractal-gtk/src/widgets/autocomplete.rs
fractal-gtk/src/widgets/avatar.rs

View file

@ -14,12 +14,15 @@ use self::comrak::{markdown_to_html, ComrakOptions};
use app::InternalCommand;
use appop::AppOp;
use app::App;
use appop::RoomPanel;
use appop::room::Force;
use glib;
use globals;
use widgets;
use uitypes::MessageContent;
use uitypes::RowType;
use backend::BKCommand;
use types::Message;
@ -150,100 +153,137 @@ impl AppOp {
prev: Option<Message>,
force_full: bool,
first_new: bool) {
let msg_entry: sourceview::View = self.ui.builder
.get_object("msg_entry")
.expect("Couldn't find msg_entry in ui file.");
let messages = self.ui.builder
.get_object::<gtk::ListBox>("message_list")
.expect("Can't find message_list in ui file.");
if let Some(ui_msg) = self.create_new_room_message(&msg) {
let msg_entry: sourceview::View = self.ui.builder
.get_object("msg_entry")
.expect("Couldn't find msg_entry in ui file.");
let messages = self.ui.builder
.get_object::<gtk::ListBox>("message_list")
.expect("Can't find message_list in ui file.");
let mut calc_prev = prev;
if !force_full && calc_prev.is_none() {
if let Some(r) = self.rooms.get(&msg.room) {
calc_prev = match r.messages.iter().position(|ref m| m.id == msg.id) {
Some(pos) if pos > 0 => r.messages.get(pos - 1).cloned(),
_ => None
};
let mut calc_prev = prev;
if !force_full && calc_prev.is_none() {
if let Some(r) = self.rooms.get(&msg.room) {
calc_prev = match r.messages.iter().position(|ref m| m.id == msg.id) {
Some(pos) if pos > 0 => r.messages.get(pos - 1).cloned(),
_ => None
};
}
}
}
if msg.room == self.active_room.clone().unwrap_or_default() && !msg.redacted {
if let Some(r) = self.rooms.get(&self.active_room.clone().unwrap_or_default()) {
let m;
{
let mb = widgets::MessageBox::new(r, &msg, &self);
let entry = msg_entry.clone();
mb.username_event_box.set_focus_on_click(false);
mb.username_event_box.connect_button_press_event(move |eb, btn| {
if btn.get_button() != 3 {
if let Some(label) = eb.get_children().iter().next() {
if let Ok(l) = label.clone().downcast::<gtk::Label>() {
if let Some(t) = l.get_text() {
if let Some(buffer) = entry.get_buffer() {
buffer.insert_at_cursor(&t[..]);
if msg.room == self.active_room.clone().unwrap_or_default() && !msg.redacted {
if let Some(r) = self.rooms.get(&self.active_room.clone().unwrap_or_default()) {
let m;
{
let backend = self.backend.clone();
let ui = self.ui.clone();
let mut mb = widgets::MessageBox::new(&ui_msg, backend, &ui);
let entry = msg_entry.clone();
mb.username_event_box.set_focus_on_click(false);
mb.username_event_box.connect_button_press_event(move |eb, btn| {
if btn.get_button() != 3 {
if let Some(label) = eb.get_children().iter().next() {
if let Ok(l) = label.clone().downcast::<gtk::Label>() {
if let Some(t) = l.get_text() {
if let Some(buffer) = entry.get_buffer() {
buffer.insert_at_cursor(&t[..]);
}
}
entry.grab_focus();
}
}
}
}
glib::signal::Inhibit(false)
});
m = match calc_prev {
Some(ref p) if self.should_group(&msg, p) => mb.small_widget(),
Some(_) if self.has_small_mtype(&msg) => mb.small_widget(),
_ => mb.widget(),
};
if let Some(ref image) = mb.image {
let msg = msg.clone();
let room = r.clone();
image.connect_button_press_event(move |_, btn| {
if btn.get_button() != 3 {
let msg = msg.clone();
let room = room.clone();
APPOP!(create_media_viewer, (msg, room));
glib::signal::Inhibit(false)
});
m = match calc_prev {
Some(ref p) if self.should_group(&msg, p) => mb.small_widget(),
Some(_) if self.has_small_mtype(&msg) => mb.small_widget(),
_ => mb.widget(),
Inhibit(true)
} else {
Inhibit(false)
}
});
}
}
m.set_focus_on_click(false);
match msgpos {
MsgPos::Bottom => {
messages.insert(&m, -1);
if first_new {
let divider = widgets::divider::new(i18n("New Messages").as_str());
messages.insert(&divider, -1);
}
},
MsgPos::Top => {
messages.insert(&m, 1);
if first_new {
let divider = widgets::divider::new(i18n("New Messages").as_str());
messages.insert(&divider, 1);
}
},
};
self.shown_messages += 1;
}
m.set_focus_on_click(false);
match msgpos {
MsgPos::Bottom => {
messages.insert(&m, -1);
if first_new {
let divider = widgets::divider::new(i18n("New Messages").as_str());
messages.insert(&divider, -1);
}
},
MsgPos::Top => {
messages.insert(&m, 1);
if first_new {
let divider = widgets::divider::new(i18n("New Messages").as_str());
messages.insert(&divider, 1);
}
},
};
self.shown_messages += 1;
}
}
}
pub fn add_tmp_room_message(&mut self, msg: Message) {
let messages = self.ui.builder
.get_object::<gtk::ListBox>("message_list")
.expect("Can't find message_list in ui file.");
if let Some(ui_msg) = self.create_new_room_message(&msg) {
let messages = self.ui.builder
.get_object::<gtk::ListBox>("message_list")
.expect("Can't find message_list in ui file.");
if let Some(r) = self.rooms.get(&self.active_room.clone().unwrap_or_default()) {
let m;
{
let mb = widgets::MessageBox::new(r, &msg, &self);
m = mb.tmpwidget();
if let Some(r) = self.rooms.get(&self.active_room.clone().unwrap_or_default()) {
let m;
{
let backend = self.backend.clone();
let ui = self.ui.clone();
let mut mb = widgets::MessageBox::new(&ui_msg, backend, &ui);
m = mb.tmpwidget();
if let Some(ref image) = mb.image {
let msg = msg.clone();
let room = r.clone();
image.connect_button_press_event(move |_, btn| {
if btn.get_button() != 3 {
let msg = msg.clone();
let room = room.clone();
APPOP!(create_media_viewer, (msg, room));
Inhibit(true)
} else {
Inhibit(false)
}
});
}
}
messages.add(&m);
}
messages.add(&m);
}
if let Some(w) = messages.get_children().iter().last() {
self.msg_queue.insert(0, TmpMsg {
if let Some(w) = messages.get_children().iter().last() {
self.msg_queue.insert(0, TmpMsg {
msg: msg.clone(),
widget: Some(w.clone()),
});
};
});
};
}
}
pub fn clear_tmp_msgs(&mut self) {
@ -263,15 +303,35 @@ impl AppOp {
if let Some(r) = self.rooms.get(&self.active_room.clone().unwrap_or_default()) {
let mut widgets = vec![];
for t in self.msg_queue.iter().rev().filter(|m| m.msg.room == r.id) {
let m;
{
let mb = widgets::MessageBox::new(r, &t.msg, &self);
m = mb.tmpwidget();
}
if let Some(ui_msg) = self.create_new_room_message(&t.msg) {
let m;
{
let backend = self.backend.clone();
let ui = self.ui.clone();
let mut mb = widgets::MessageBox::new(&ui_msg, backend, &ui);
m = mb.tmpwidget();
if let Some(ref image) = mb.image {
println!("i have a image");
let msg = t.msg.clone();
let room = r.clone();
image.connect_button_press_event(move |_, btn| {
if btn.get_button() != 3 {
let msg = msg.clone();
let room = room.clone();
APPOP!(create_media_viewer, (msg, room));
messages.add(&m);
if let Some(w) = messages.get_children().iter().last() {
widgets.push(w.clone());
Inhibit(true)
} else {
Inhibit(false)
}
});
}
}
messages.add(&m);
if let Some(w) = messages.get_children().iter().last() {
widgets.push(w.clone());
}
}
}
@ -612,4 +672,66 @@ impl AppOp {
}
self.internal.send(InternalCommand::LoadMoreNormal).unwrap();
}
/* parese a backend Message into a Message for the UI */
pub fn create_new_room_message(&self, msg: &Message) -> Option<MessageContent> {
let mut highlights = vec![];
let t = match msg.mtype.as_ref() {
"m.emote" => RowType::Emote,
"m.image" => RowType::Image,
"m.sticker" => RowType::Sticker,
"m.audio" => RowType::Audio,
"m.video" => RowType::Video,
"m.file" => RowType::File,
_ => {
/* set message type to mention if the body contains the username, we should
* also match for MXID */
let is_mention = if let Some(user) = self.username.clone() {
msg.sender != self.uid.clone()? && msg.body.contains(&user)
} else {
false
};
if is_mention {
if let Some(user) = self.username.clone() {
highlights.push(user);
}
if let Some(mxid) = self.uid.clone() {
highlights.push(mxid);
}
highlights.push(String::from("message_menu"));
RowType::Mention
} else {
RowType::Message
}
}
};
let room = self.rooms.get(&msg.room)?;
let name = if let Some(member) = room.members.get(&msg.sender) {
member.alias.clone()
} else {
None
};
Some(create_ui_message(msg.clone(), name, t, highlights))
}
}
/* FIXME: don't convert msg to ui messages here, we should later get a ui message from storage */
fn create_ui_message (msg: Message, name: Option<String>, t: RowType, highlights: Vec<String>) -> MessageContent {
MessageContent {
msg: msg.clone(),
id: msg.id.unwrap_or(String::from("")),
sender: msg.sender,
sender_name: name,
mtype: t,
body: msg.body,
date: msg.date,
thumb: msg.thumb,
url: msg.url,
formatted_body: msg.formatted_body,
format: msg.format,
highlights: highlights,
}
}

View file

@ -4,6 +4,7 @@ use std::fs::File;
use std::fs::remove_dir_all;
use std::io::prelude::*;
use gtk;
use gtk::LabelExt;
use types::RoomList;
use error::Error;
@ -14,6 +15,7 @@ use globals;
/* includes for avatar download */
use backend::BKCommand;
use std::sync::mpsc::Sender;
use std::sync::mpsc::Receiver;
use std::sync::mpsc::channel;
use std::sync::mpsc::TryRecvError;
@ -93,3 +95,50 @@ pub fn download_to_cache(backend: Sender<BKCommand>,
}
});
}
/* Get username based on the MXID, we should cache the username */
pub fn download_to_cache_username(backend: Sender<BKCommand>,
uid: &str,
label: gtk::Label,
avatar: Option<Rc<RefCell<AvatarData>>>) {
let (tx, rx): (Sender<String>, Receiver<String>) = channel();
backend.send(BKCommand::GetUserNameAsync(uid.to_string(), tx)).unwrap();
gtk::timeout_add(50, move || match rx.try_recv() {
Err(TryRecvError::Empty) => gtk::Continue(true),
Err(TryRecvError::Disconnected) => gtk::Continue(false),
Ok(username) => {
label.set_text(&username);
if let Some(ref rc_data) = avatar {
let mut data = rc_data.borrow_mut();
data.redraw_fallback(Some(username));
}
gtk::Continue(false)
}
});
}
/* Download username for a given MXID and update a emote message
* FIXME: We should cache this request and do it before we need to display the username in an emote*/
pub fn download_to_cache_username_emote(backend: Sender<BKCommand>,
uid: &str,
text: &str,
label: gtk::Label,
avatar: Option<Rc<RefCell<AvatarData>>>) {
let (tx, rx): (Sender<String>, Receiver<String>) = channel();
backend.send(BKCommand::GetUserNameAsync(uid.to_string(), tx)).unwrap();
let text = text.to_string();
gtk::timeout_add(50, move || match rx.try_recv() {
Err(TryRecvError::Empty) => gtk::Continue(true),
Err(TryRecvError::Disconnected) => gtk::Continue(false),
Ok(username) => {
label.set_markup(&format!("<b>{}</b> {}", &username, text));
if let Some(ref rc_data) = avatar {
let mut data = rc_data.borrow_mut();
data.redraw_fallback(Some(username));
}
gtk::Continue(false)
}
});
}

View file

@ -41,6 +41,7 @@ mod globals;
#[macro_use]
mod util;
mod cache;
mod uitypes;
mod uibuilder;
mod static_resources;
mod passwd;

View file

@ -0,0 +1,36 @@
use chrono::prelude::DateTime;
use chrono::prelude::Local;
use types::Message;
/* MessageContent contains all data needed to display one row
* therefore it should contain only one Message body with one format
* To-Do: this should be moved to a file collecting all structs used in the UI */
#[derive(Debug, Clone, PartialEq)]
pub struct MessageContent {
pub id: String,
pub sender: String,
pub sender_name: Option<String>,
pub mtype: RowType,
pub body: String,
pub date: DateTime<Local>,
pub thumb: Option<String>,
pub url: Option<String>,
pub formatted_body: Option<String>,
pub format: Option<String>,
/* in some places we still need the backend message type (e.g. media viewer) */
pub msg: Message,
pub highlights: Vec<String>,
}
/* To-Do: this should be moved to a file collecting all structs used in the UI */
#[derive(Debug, Clone, PartialEq)]
pub enum RowType {
Divider,
Mention,
Emote,
Message,
Sticker,
Image,
Audio,
Video,
File,
}

View file

@ -10,11 +10,6 @@ use app::App;
use i18n::i18n;
use self::gtk::prelude::*;
use types::Message;
use types::Member;
use types::Room;
use self::chrono::prelude::*;
use backend::BKCommand;
@ -26,36 +21,40 @@ use std::sync::mpsc::{Sender, Receiver};
use std::sync::mpsc::TryRecvError;
use cache::download_to_cache;
use cache::download_to_cache_username;
use cache::download_to_cache_username_emote;
use appop::AppOp;
use globals;
use widgets;
use widgets::AvatarExt;
use widgets::message_menu::MessageMenu;
use uitypes::RowType;
use uitypes::MessageContent as Message;
use uibuilder::UI;
// Room Message item
pub struct MessageBox<'a> {
room: &'a Room,
msg: &'a Message,
op: &'a AppOp,
backend: Sender<BKCommand>,
ui: &'a UI,
username: gtk::Label,
pub username_event_box: gtk::EventBox,
pub row_event_box: gtk::EventBox,
pub image: Option<gtk::DrawingArea>,
}
impl<'a> MessageBox<'a> {
pub fn new(room: &'a Room, msg: &'a Message, op: &'a AppOp) -> MessageBox<'a> {
pub fn new(msg: &'a Message, backend: Sender<BKCommand>, ui: &'a UI) -> MessageBox<'a> {
let username = gtk::Label::new("");
let eb = gtk::EventBox::new();
let row_eb = gtk::EventBox::new();
let backend = op.backend.clone();
let ui = op.ui.clone();
let source_msg = msg.msg.clone();
row_eb.connect_button_press_event(clone!(msg => move |eb, btn| {
row_eb.connect_button_press_event(clone!(source_msg, backend, ui => move |eb, btn| {
if btn.get_button() == 3 {
let menu = MessageMenu::new_message_menu(ui.clone(), backend.clone(),
msg.clone(), None);
source_msg.clone(), None);
menu.show_menu_popover(eb.clone().upcast::<gtk::Widget>());
}
@ -64,15 +63,16 @@ impl<'a> MessageBox<'a> {
MessageBox {
msg: msg,
room: room,
op: op,
backend: backend,
ui: ui,
username: username,
username_event_box: eb,
row_event_box: row_eb,
image: None,
}
}
pub fn tmpwidget(&self) -> gtk::ListBoxRow {
pub fn tmpwidget(&mut self) -> gtk::ListBoxRow {
let w = self.widget();
if let Some(style) = w.get_style_context() {
style.add_class("msg-tmp");
@ -80,7 +80,7 @@ impl<'a> MessageBox<'a> {
w
}
pub fn widget(&self) -> gtk::ListBoxRow {
pub fn widget(&mut self) -> gtk::ListBoxRow {
// msg
// +--------+---------+
// | avatar | content |
@ -105,7 +105,7 @@ impl<'a> MessageBox<'a> {
row
}
pub fn small_widget(&self) -> gtk::ListBoxRow {
pub fn small_widget(&mut self) -> gtk::ListBoxRow {
// msg
// +--------+---------+
// | | content |
@ -127,7 +127,7 @@ impl<'a> MessageBox<'a> {
row
}
fn build_room_msg_content(&self, small: bool) -> gtk::Box {
fn build_room_msg_content(&mut self, small: bool) -> gtk::Box {
// content
// +------+
// | info |
@ -138,18 +138,18 @@ impl<'a> MessageBox<'a> {
let msg = self.msg;
if !small {
let info = self.build_room_msg_info(self.msg, small);
let info = self.build_room_msg_info(self.msg);
info.set_margin_top(2);
info.set_margin_bottom(3);
content.pack_start(&info, false, false, 0);
}
let body = match msg.mtype.as_ref() {
"m.sticker" => self.build_room_msg_sticker(),
"m.image" => self.build_room_msg_image(),
"m.emote" => self.build_room_msg_emote(&msg),
"m.audio" => self.build_room_audio_player(),
"m.video" | "m.file" => self.build_room_msg_file(),
let body = match msg.mtype {
RowType::Sticker => self.build_room_msg_sticker(),
RowType::Image => self.build_room_msg_image(),
RowType::Emote => self.build_room_msg_emote(&msg),
RowType::Audio => self.build_room_audio_player(),
RowType::Video | RowType::File => self.build_room_msg_file(),
_ => self.build_room_msg_body(&msg.body),
};
@ -160,52 +160,24 @@ impl<'a> MessageBox<'a> {
fn build_room_msg_avatar(&self) -> widgets::Avatar {
let uid = self.msg.sender.clone();
let alias = self.msg.sender_name.clone();
let avatar = widgets::Avatar::avatar_new(Some(globals::MSG_ICON_SIZE));
let m = self.room.members.get(&uid);
let data = avatar.circle(uid.clone(), alias.clone(), globals::MSG_ICON_SIZE);
if let Some(name) = alias {
self.username.set_text(&name);
} else {
self.username.set_text(&uid);
}
let data = match m {
Some(member) => {
self.username.set_text(&member.get_alias());
let username = Some(member.get_alias());
avatar.circle(uid.clone(), username, globals::MSG_ICON_SIZE)
}
None => {
let backend = self.op.backend.clone();
let data = avatar.circle(uid.clone(), None, globals::MSG_ICON_SIZE);
let l = self.username.clone();
let d = data.clone();
set_username_async(backend, &uid, move |uname| {
l.set_text(&uname);
let mut data = d.borrow_mut();
data.redraw_fallback(Some(uname));
});
data
}
};
download_to_cache(self.op.backend.clone(), uid.clone(),
data.clone());
download_to_cache(self.backend.clone(), uid.clone(), data.clone());
download_to_cache_username(self.backend.clone(), &uid, self.username.clone(), Some(data.clone()));
avatar
}
fn build_room_msg_username(&self, sender: &str, member: Option<&Member>, small: bool) -> gtk::Label {
let uname = match member {
Some(m) => m.get_alias(),
None => {
// in small widget, the avatar doesn't download the username
// so we need to download here
if small {
let backend = self.op.backend.clone();
let l = self.username.clone();
set_username_async(backend, sender, move |uname| {
l.set_text(&uname);
});
}
String::from(sender)
}
};
fn build_room_msg_username(&self, sender: &str) -> gtk::Label {
let uname = String::from(sender);
self.username.set_text(&uname);
self.username.set_justify(gtk::Justification::Left);
@ -219,23 +191,14 @@ impl<'a> MessageBox<'a> {
/// Add classes to the widget depending on the properties:
///
/// * msg-mention: if the message contains the username in the body and
/// sender is not app user
/// * msg-mention: if the message contains a keyword, e.g. the username
/// * msg-emote: if the message is an emote
fn set_msg_styles(&self, w: &gtk::ListBoxRow) {
let uname = &self.op.username.clone().unwrap_or_default();
let uid = self.op.uid.clone().unwrap_or_default();
let msg = self.msg;
let body: &str = &msg.body;
if let Some(style) = w.get_style_context() {
// mentions
if String::from(body).contains(uname) && msg.sender != uid {
style.add_class("msg-mention");
}
// emotes
if msg.mtype == "m.emote" {
style.add_class("msg-emote");
match self.msg.mtype {
RowType::Mention => style.add_class("msg-mention"),
RowType::Emote => style.add_class("msg-emote"),
_ => {},
}
}
}
@ -252,35 +215,39 @@ impl<'a> MessageBox<'a> {
fn build_room_msg_body(&self, body: &str) -> gtk::Box {
let bx = gtk::Box::new(gtk::Orientation::Vertical, 6);
let uname = self.op.username.clone().unwrap_or_default();
let msg_parts = self.calculate_msg_parts(body);
if self.msg.sender != self.op.uid.clone().unwrap_or_default()
&& String::from(body).contains(&uname) {
if self.msg.mtype == RowType::Mention {
for msg in msg_parts.iter() {
let name = uname.clone();
let highlights = self.msg.highlights.clone();
msg.connect_property_cursor_position_notify(move |w| {
if let Some(text) = w.get_text() {
if let Some(attr) = highlight_username(w.clone(), &name, text) {
w.set_attributes(&attr);
}
if let Some(text) = w.get_text() {
let attr = pango::AttrList::new();
for light in highlights.clone() {
highlight_username(w.clone(), &attr, &light, text.clone());
}
w.set_attributes(&attr);
}
});
let name = uname.clone();
let highlights = self.msg.highlights.clone();
msg.connect_property_selection_bound_notify(move |w| {
if let Some(text) = w.get_text() {
if let Some(attr) = highlight_username(w.clone(), &name, text) {
w.set_attributes(&attr);
}
if let Some(text) = w.get_text() {
let attr = pango::AttrList::new();
for light in highlights.clone() {
highlight_username(w.clone(), &attr, &light, text.clone());
}
w.set_attributes(&attr);
}
});
if let Some(text) = msg.get_text() {
if let Some(attr) = highlight_username(msg.clone(), &uname, text) {
msg.set_attributes(&attr);
let attr = pango::AttrList::new();
for light in self.msg.highlights.clone() {
highlight_username(msg.clone(), &attr, &light, text.clone());
}
msg.set_attributes(&attr);
}
}
}
@ -313,45 +280,31 @@ impl<'a> MessageBox<'a> {
parts_labels
}
fn build_room_msg_image(&self) -> gtk::Box {
fn build_room_msg_image(&mut self) -> gtk::Box {
let msg = self.msg;
let bx = gtk::Box::new(gtk::Orientation::Horizontal, 0);
let backend = self.op.backend.clone();
let img_path = match msg.thumb {
Some(ref m) => m.clone(),
None => msg.url.clone().unwrap_or_default(),
};
let image = widgets::image::Image::new(&backend, &img_path)
let image = widgets::image::Image::new(&self.backend, &img_path)
.size(Some(globals::MAX_IMAGE_SIZE)).build();
let msg = msg.clone();
let room = self.room.clone();
image.widget.connect_button_press_event(move |_, btn| {
if btn.get_button() != 3 {
let msg = msg.clone();
let room = room.clone();
APPOP!(create_media_viewer, (msg, room));
Inhibit(true)
} else {
Inhibit(false)
}
});
if let Some(style) = image.widget.get_style_context() {
style.add_class("image-widget");
}
bx.pack_start(&image.widget, true, true, 0);
bx.show_all();
self.image = Some(image.widget);
bx
}
fn build_room_msg_sticker(&self) -> gtk::Box {
let msg = self.msg;
let bx = gtk::Box::new(gtk::Orientation::Horizontal, 0);
let backend = self.op.backend.clone();
let backend = self.backend.clone();
let image = widgets::image::Image::new(&backend,
&msg.url.clone().unwrap_or_default())
.size(Some(globals::MAX_STICKER_SIZE)).build();
@ -370,7 +323,7 @@ impl<'a> MessageBox<'a> {
let name = msg.body.clone();
let url = msg.url.clone().unwrap_or_default();
let backend = self.op.backend.clone();
let backend = self.backend.clone();
let (tx, rx): (Sender<String>, Receiver<String>) = channel();
backend.send(BKCommand::GetMediaUrl(url.clone(), tx)).unwrap();
@ -431,7 +384,7 @@ impl<'a> MessageBox<'a> {
let name = msg.body.clone();
let url = msg.url.clone().unwrap_or_default();
let backend = self.op.backend.clone();
let backend = self.backend.clone();
let name_lbl = gtk::Label::new(name.as_str());
name_lbl.set_tooltip_text(name.as_str());
name_lbl.set_ellipsize(pango::EllipsizeMode::End);
@ -514,15 +467,14 @@ impl<'a> MessageBox<'a> {
date
}
fn build_room_msg_info(&self, msg: &Message, small: bool) -> gtk::Box {
fn build_room_msg_info(&self, msg: &Message) -> gtk::Box {
// info
// +----------+------+
// | username | date |
// +----------+------+
let info = gtk::Box::new(gtk::Orientation::Horizontal, 0);
let member = self.room.members.get(&msg.sender);
let username = self.build_room_msg_username(&msg.sender, member, small);
let username = self.build_room_msg_username(&msg.sender);
let date = self.build_room_msg_date(&msg.date);
self.username_event_box.add(&username);
@ -535,25 +487,13 @@ impl<'a> MessageBox<'a> {
fn build_room_msg_emote(&self, msg: &Message) -> gtk::Box {
let bx = gtk::Box::new(gtk::Orientation::Horizontal, 0);
let member = self.room.members.get(&msg.sender);
let sender: &str = &msg.sender;
/* Use MXID till we have a alias */
let sname = msg.sender_name.clone().unwrap_or(String::from(msg.sender.clone()));
let msg_label = gtk::Label::new("");
let body: &str = &msg.body;
let markup = markup_text(body);
let m = markup.clone();
let sname = match member {
Some(m) => m.get_alias(),
None => {
let backend = self.op.backend.clone();
let label = msg_label.clone();
set_username_async(backend, sender, move |n| {
label.set_markup(&format!("<b>{}</b> {}", n, m));
});
String::from(sender)
}
};
download_to_cache_username_emote(self.backend.clone(), &sname, &markup, msg_label.clone(), None);
self.connect_right_click_menu(msg_label.clone().upcast::<gtk::Widget>());
msg_label.set_markup(&format!("<b>{}</b> {}", sname, markup));
@ -565,9 +505,9 @@ impl<'a> MessageBox<'a> {
fn connect_right_click_menu(&self, w: gtk::Widget) {
let eb = self.row_event_box.clone();
let msg = self.msg.clone();
let backend = self.op.backend.clone();
let ui = self.op.ui.clone();
let backend = self.backend.clone();
let ui = self.ui.clone();
let msg = self.msg.msg.clone();
w.connect_button_press_event(move |w, btn| {
if btn.get_button() == 3 {
@ -582,7 +522,7 @@ impl<'a> MessageBox<'a> {
}
}
fn highlight_username(label: gtk::Label, alias: &String, input: String) -> Option<pango::AttrList> {
fn highlight_username(label: gtk::Label, attr: &pango::AttrList, alias: &String, input: String) -> Option<()> {
fn contains((start, end): (i32, i32), item: i32) -> bool {
match start <= end {
true => start <= item && end > item,
@ -599,7 +539,6 @@ fn highlight_username(label: gtk::Label, alias: &String, input: String) -> Optio
let blue = fg.blue * 65535. + 0.5;
let color = pango::Attribute::new_foreground(red as u16, green as u16, blue as u16)?;
let attr = pango::AttrList::new();
let mut input = input.clone();
let alias = &alias.to_lowercase();
let mut removed_char = 0;
@ -647,23 +586,7 @@ fn highlight_username(label: gtk::Label, alias: &String, input: String) -> Optio
removed_char = removed_char + pos.1 as u32;
}
Some(attr)
}
fn set_username_async<F>(backend: Sender<BKCommand>,
uid: &str,
cb: F)
where F: Fn(String) + 'static {
let (tx, rx): (Sender<String>, Receiver<String>) = channel();
backend.send(BKCommand::GetUserNameAsync(uid.to_string(), tx)).unwrap();
gtk::timeout_add(50, move || match rx.try_recv() {
Err(TryRecvError::Empty) => gtk::Continue(true),
Err(TryRecvError::Disconnected) => gtk::Continue(false),
Ok(username) => {
cb(username);
gtk::Continue(false)
}
});
None
}
#[derive(PartialEq)]