members_list.rs 5.76 KB
Newer Older
1 2 3 4 5 6 7 8
extern crate gtk;

use std::cell::RefCell;
use std::rc::Rc;

use self::gtk::prelude::*;
use glib::signal;

9
use i18n::i18n;
10
use types::Member;
11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
use widgets;
use widgets::avatar::AvatarExt;

#[derive(Debug, Clone)]
pub struct MembersList {
    container: gtk::ListBox,
    search_entry: gtk::SearchEntry,
    error: gtk::Label,
    members: Vec<Member>,
}

impl MembersList {
    pub fn new(m: Vec<Member>, entry: gtk::SearchEntry) -> MembersList {
        MembersList {
            container: gtk::ListBox::new(),
            error: gtk::Label::new(None),
            members: m,
            search_entry: entry,
        }
    }

32 33
    /* creates a empty list with members.len() rows, the content will be loaded when the row is
     * drawn */
34 35 36 37 38
    pub fn create(&self) -> Option<gtk::Box> {
        let b = gtk::Box::new(gtk::Orientation::Vertical, 0);
        b.set_hexpand(true);
        b.pack_start(&self.container, true, true, 0);
        add_rows(self.container.clone(), self.members.clone());
39 40 41
        self.error
            .get_style_context()?
            .add_class("no_member_search");
42
        self.error.set_text(&i18n("No matching members found"));
43 44 45 46 47 48 49
        b.pack_start(&self.error, true, true, 0);
        self.connect();
        b.show_all();
        self.error.hide();
        Some(b)
    }

50
    /* removes the content of the row with index i */
51
    #[allow(dead_code)]
52 53 54 55 56 57 58 59 60
    pub fn update(&self, uid: String) -> Option<()> {
        let mut index = None;
        for (i, member) in self.members.iter().enumerate() {
            if member.uid == uid {
                index = Some(i);
                break;
            }
        }
        let widget = self.container.get_row_at_index(index? as i32)?;
61 62 63 64 65 66 67 68
        let child = widget.get_child()?;
        widget.remove(&child);
        /* We don't need to create a new widget because the draw signal
         * will handle the creation */

        None
    }

69 70 71 72 73
    pub fn connect(&self) {
        let container = self.container.clone();
        let members = self.members.clone();
        let error = self.error.clone();
        let id = self.search_entry.connect_search_changed(move |w| {
74 75 76 77 78
            filter_rows(
                container.clone(),
                members.clone(),
                error.clone(),
                w.get_text(),
79
                );
80 81 82 83 84 85 86 87 88 89
        });
        /* we need to remove the handler when the member list is destroyed */
        let id: Rc<RefCell<Option<signal::SignalHandlerId>>> = Rc::new(RefCell::new(Some(id)));
        let search_entry = self.search_entry.clone();
        self.container.connect_destroy(move |_| {
            let id = id.borrow_mut().take();
            if let Some(id) = id {
                signal::signal_handler_disconnect(&search_entry, id);
            }
        });
90
        /* we could slowly load members when the main thread is idle */
91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124
        /*
           let container = self.container.clone();
           let members = self.members.clone();
           for (index, member) in members.iter().enumerate() {
           gtk::idle_add(clone!(index, member, container => move || {
           if let Some(w) = container.get_row_at_index(index as i32) {
           if w.get_child().is_none() {
           w.add(&load_row_content(member.clone()));
           }
           }
           gtk::Continue(false)
           }));
           }
           */
    }
}

fn create_row(member: Member) -> Option<gtk::ListBoxRow> {
    let row = gtk::ListBoxRow::new();
    row.connect_draw(clone!(member => move |w, _| {
        if w.get_child().is_none() {
            w.add(&load_row_content(member.clone()));
        }
        gtk::Inhibit(false)
    }));
    row.set_selectable(false);
    row.set_size_request(-1, 56);
    row.show();
    Some(row)
}

/* creating the row is quite slow, therefore we have a small delay when scrolling the members list */
fn load_row_content(member: Member) -> gtk::Box {
    let b = gtk::Box::new(gtk::Orientation::Horizontal, 12);
125 126
    let avatar = widgets::Avatar::avatar_new(Some(40));
    avatar.circle(member.uid.clone(), member.alias.clone(), 40);
127 128
    let user_box = gtk::Box::new(gtk::Orientation::Vertical, 0);
    let username = gtk::Label::new(Some(member.get_alias().as_str()));
129 130 131 132 133 134
    let uid = gtk::Label::new(Some(member.uid.as_str()));
    username.set_xalign(0.);
    uid.set_xalign(0.);
    if let Some(style) = uid.get_style_context() {
        style.add_class("uid-label");
    }
135 136 137 138 139
    b.set_margin_start(12);
    b.set_margin_end(12);
    b.set_margin_top(6);
    b.set_margin_bottom(6);
    user_box.pack_start(&username, true, true, 0);
140
    user_box.pack_start(&uid, false, false, 0);
141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157
    /* we don't have this state yet
     * let state = gtk::Label::new();
     * user_box.pack_end(&state, true, true, 0); */
    b.pack_start(&avatar, false, true, 0);
    b.pack_start(&user_box, false, true, 0);
    b.show_all();
    b
}

fn add_rows(container: gtk::ListBox, members: Vec<Member>) -> Option<usize> {
    /* Load just enough members to fill atleast the visible list */
    for member in members.iter() {
        container.insert(&create_row(member.clone())?, -1);
    }
    None
}

158 159 160 161 162 163
fn filter_rows(
    container: gtk::ListBox,
    members: Vec<Member>,
    label: gtk::Label,
    search: Option<String>,
) -> Option<usize> {
164
    /* Load just enough members to fill atleast the visible list */
165 166
    // Convert to Lowercase for case-insensitive searching
    let search = search?.to_lowercase();
167 168 169
    let search = search.as_str();
    let mut empty = true;
    for (index, member) in members.iter().enumerate() {
170 171
        let alias_lower = member.get_alias().to_lowercase();
        if !alias_lower.contains(search){
172 173 174 175 176 177 178 179 180
            container.get_row_at_index(index as i32)?.hide();
        } else {
            container.get_row_at_index(index as i32)?.show();
            empty = false;
        }
    }
    label.set_visible(empty);
    None
}