Skip to content

Commit 1011416

Browse files
committed
chat-info-members: Use GtkListView instead of GtkListBox
1 parent 266045e commit 1011416

File tree

10 files changed

+279
-87
lines changed

10 files changed

+279
-87
lines changed

.editorconfig

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,5 +7,5 @@ trim_trailing_whitespace = true
77
insert_final_newline = true
88
charset = utf-8
99

10-
[*.{build,yml,ui,yaml}]
10+
[*.{build,yml,ui,yaml,css}]
1111
indent_size = 2

data/resources/resources.gresource.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
<file compressed="true" preprocess="xml-stripblanks">ui/add-account-row.ui</file>
1414
<file compressed="true" preprocess="xml-stripblanks">ui/avatar-with-selection.ui</file>
1515
<file compressed="true" preprocess="xml-stripblanks">ui/components-avatar.ui</file>
16+
<file compressed="true" preprocess="xml-stripblanks">ui/components-chat-member-row.ui</file>
1617
<file compressed="true" preprocess="xml-stripblanks">ui/components-message-entry.ui</file>
1718
<file compressed="true" preprocess="xml-stripblanks">ui/content.ui</file>
1819
<file compressed="true" preprocess="xml-stripblanks">ui/content-chat-action-bar.ui</file>

data/resources/style.css

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -213,7 +213,7 @@ messagesticker messageindicators,
213213
.osd-indicator {
214214
background-color: alpha(black, 0.4);
215215
color: white;
216-
opacity: 1.0; /* Reset opacity from message bubble */
216+
opacity: 1; /* Reset opacity from message bubble */
217217
border-radius: 9999px;
218218
padding: 2px 6px;
219219
margin: 6px;
@@ -268,7 +268,7 @@ messageindicators,
268268
padding: 0;
269269
}
270270

271-
.message-entry > overlay > entry > scrolledwindow > scrollbar {
271+
.message-entry > overlay > entry > scrolledwindow > scrollbar {
272272
box-shadow: none;
273273
background: none;
274274
}
@@ -298,3 +298,7 @@ window.chat-info .main-page > avatar {
298298
window.chat-info .main-page > list {
299299
margin-top: 12px;
300300
}
301+
302+
chatmember {
303+
padding: 6px 0px;
304+
}
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<interface>
3+
<template class="ComponentsChatMemberRow" parent="GtkBox">
4+
<property name="spacing">12</property>
5+
<child>
6+
<object class="ComponentsAvatar" id="avatar">
7+
<property name="size">40</property>
8+
</object>
9+
</child>
10+
<child>
11+
<object class="GtkBox">
12+
<property name="orientation">vertical</property>
13+
<property name="valign">center</property>
14+
<child>
15+
<object class="GtkBox">
16+
<property name="hexpand">True</property>
17+
<child>
18+
<object class="GtkLabel" id="user_name_label">
19+
<style>
20+
<class name="heading"/>
21+
</style>
22+
<property name="ellipsize">end</property>
23+
</object>
24+
</child>
25+
<child>
26+
<object class="GtkLabel" id="member_status_label">
27+
<style>
28+
<class name="accent"/>
29+
<class name="caption"/>
30+
</style>
31+
<property name="hexpand">True</property>
32+
<property name="xalign">1</property>
33+
<property name="ellipsize">end</property>
34+
</object>
35+
</child>
36+
</object>
37+
</child>
38+
<child>
39+
<object class="GtkInscription" id="user_status_label">
40+
<style>
41+
<class name="dim-label"/>
42+
</style>
43+
<property name="xalign">0</property>
44+
<property name="text-overflow">ellipsize-end</property>
45+
</object>
46+
</child>
47+
</object>
48+
</child>
49+
</template>
50+
</interface>

data/resources/ui/content-chat-info-window.ui

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -17,17 +17,15 @@
1717
<property name="centering-policy">strict</property>
1818
<property name="title-widget">
1919
<object class="AdwViewSwitcherTitle" id="title">
20+
<property name="title" translatable="yes">Info</property>
2021
<property name="stack">stack</property>
2122
</object>
2223
</property>
23-
<style>
24-
<class name="flat"/>
25-
</style>
2624
</object>
2725
</child>
2826
<child>
2927
<object class="AdwViewStack" id="stack">
30-
<property name="vexpand">true</property>
28+
<property name="vexpand">True</property>
3129
<child>
3230
<object class="AdwViewStackPage">
3331
<property name="name">info</property>
@@ -91,15 +89,16 @@
9189
<property name="child">
9290
<object class="GtkScrolledWindow">
9391
<property name="child">
94-
<object class="AdwClamp">
92+
<object class="AdwClampScrollable">
9593
<property name="maximum-size">440</property>
9694
<property name="tightening-threshold">200</property>
9795
<property name="child">
98-
<object class="GtkListBox" id="members_list">
99-
<property name="valign">center</property>
100-
<property name="selection-mode">none</property>
96+
<object class="GtkListView" id="members_list">
97+
<property name="model">
98+
<object class="GtkNoSelection" id="selection"/>
99+
</property>
101100
<style>
102-
<class name="boxed-list"/>
101+
<class name="navigation-sidebar"/>
103102
</style>
104103
</object>
105104
</property>
Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
use crate::tdlib::ChatMember;
2+
use gettextrs::gettext;
3+
use gtk::prelude::*;
4+
use gtk::subclass::prelude::*;
5+
use gtk::{glib, CompositeTemplate};
6+
7+
use super::Avatar;
8+
use crate::{expressions, strings};
9+
use tdlib::enums::{UserStatus, UserType};
10+
11+
mod imp {
12+
use super::*;
13+
#[derive(Debug, Default, CompositeTemplate)]
14+
#[template(resource = "/com/github/melix99/telegrand/ui/components-chat-member-row.ui")]
15+
pub(crate) struct ChatMemberRow {
16+
#[template_child]
17+
pub(super) avatar: TemplateChild<Avatar>,
18+
#[template_child]
19+
pub(super) user_name_label: TemplateChild<gtk::Label>,
20+
#[template_child]
21+
pub(super) member_status_label: TemplateChild<gtk::Label>,
22+
#[template_child]
23+
pub(super) user_status_label: TemplateChild<gtk::Inscription>,
24+
}
25+
26+
#[glib::object_subclass]
27+
impl ObjectSubclass for ChatMemberRow {
28+
const NAME: &'static str = "ComponentsChatMemberRow";
29+
type Type = super::ChatMemberRow;
30+
type ParentType = gtk::Box;
31+
32+
fn class_init(klass: &mut Self::Class) {
33+
Self::bind_template(klass);
34+
klass.set_css_name("chatmember");
35+
}
36+
37+
fn instance_init(obj: &glib::subclass::InitializingObject<Self>) {
38+
obj.init_template();
39+
}
40+
}
41+
42+
impl ObjectImpl for ChatMemberRow {}
43+
44+
impl WidgetImpl for ChatMemberRow {}
45+
46+
impl BoxImpl for ChatMemberRow {}
47+
}
48+
49+
glib::wrapper! {
50+
pub(crate) struct ChatMemberRow(ObjectSubclass<imp::ChatMemberRow>)
51+
@extends gtk::Widget;
52+
}
53+
54+
impl ChatMemberRow {
55+
pub fn new() -> Self {
56+
glib::Object::new(&[])
57+
}
58+
pub fn bind_member(&self, member: ChatMember) {
59+
let imp = self.imp();
60+
61+
let user = member.user();
62+
63+
let user_expression = gtk::ObjectExpression::new(&user);
64+
let name_expression = expressions::user_display_name(&user_expression);
65+
name_expression.bind(&*imp.user_name_label, "label", Some(&user));
66+
67+
if let UserType::Bot(_) = user.type_().0 {
68+
imp.user_status_label.set_text(Some(&gettext("bot")));
69+
} else {
70+
let status = user.status().0;
71+
let status_label = &*imp.user_status_label;
72+
73+
match status {
74+
UserStatus::Online(_) => status_label.set_css_classes(&["accent"]),
75+
_ => status_label.set_css_classes(&["dim-label"]),
76+
}
77+
78+
let status = strings::user_status(&status);
79+
imp.user_status_label.set_text(Some(&status));
80+
};
81+
82+
imp.member_status_label.set_label(&member.status());
83+
84+
imp.avatar.set_item(Some(user.upcast()));
85+
}
86+
}

src/session/components/mod.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
mod avatar;
2+
mod chat_member_row;
23
mod message_entry;
34
mod snow;
45

56
pub(crate) use self::avatar::Avatar;
7+
pub(crate) use self::chat_member_row::ChatMemberRow;
68
pub(crate) use self::message_entry::MessageEntry;
79
pub(crate) use self::snow::Snow;

src/session/content/chat_info_window.rs

Lines changed: 60 additions & 75 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,15 @@ use gtk::subclass::prelude::*;
55
use gtk::{glib, CompositeTemplate};
66
use tdlib::enums::ChatMembers::ChatMembers as TdChatMembers;
77
use tdlib::enums::User::User as TdUser;
8-
use tdlib::enums::{ChatMemberStatus, MessageSender, UserType};
8+
use tdlib::enums::{MessageSender, UserType};
99
use tdlib::functions;
10-
use tdlib::types::{BasicGroupFullInfo, ChatMember, ChatMembers, SupergroupFullInfo};
10+
use tdlib::types::{
11+
BasicGroupFullInfo, ChatMember as TdChatMember, ChatMembers, SupergroupFullInfo,
12+
};
1113

1214
use crate::i18n::ngettext_f;
13-
use crate::tdlib::{BasicGroup, BoxedUserStatus, Chat, ChatType, Supergroup, User};
15+
use crate::session::components::ChatMemberRow;
16+
use crate::tdlib::{BasicGroup, BoxedUserStatus, Chat, ChatMember, ChatType, Supergroup, User};
1417
use crate::utils::spawn;
1518
use crate::{expressions, strings};
1619

@@ -34,7 +37,7 @@ mod imp {
3437
#[template_child]
3538
pub(super) members_page: TemplateChild<adw::ViewStackPage>,
3639
#[template_child]
37-
pub(super) members_list: TemplateChild<gtk::ListBox>,
40+
pub(super) members_list: TemplateChild<gtk::ListView>,
3841
}
3942

4043
#[glib::object_subclass]
@@ -310,81 +313,63 @@ impl ChatInfoWindow {
310313
self.update_info_list_visibility();
311314
}
312315

313-
async fn append_members(&self, members: Vec<ChatMember>) {
314-
let session = self.chat().unwrap().session();
315-
let client_id = session.client_id();
316+
async fn append_members(&self, members: Vec<TdChatMember>) {
317+
let members: Vec<_> = {
318+
let mut users: Vec<User> = vec![];
316319

317-
let members_list = &self.imp().members_list;
320+
let session = self.chat().unwrap().session();
321+
let client_id = session.client_id();
318322

319-
for member in members {
320-
if let MessageSender::User(user) = member.member_id {
321-
if let Ok(TdUser(user)) = functions::get_user(user.user_id, client_id).await {
322-
let user_row = adw::ActionRow::new();
323-
user_row.set_title_lines(1);
324-
user_row.set_subtitle_lines(1);
325-
326-
let user = User::from_td_object(user, &session);
327-
328-
let user_expression = gtk::ObjectExpression::new(&user);
329-
let name_expression = expressions::user_display_name(&user_expression);
330-
name_expression.bind(&user_row, "title", Some(&user));
331-
332-
User::this_expression("status")
333-
.chain_closure::<String>(closure!(
334-
|_: Option<glib::Object>, status: BoxedUserStatus| {
335-
strings::user_status(&status.0)
336-
}
337-
))
338-
.bind(&user_row, "subtitle", Some(&user));
339-
340-
if let UserType::Bot(_) = user.type_().0 {
341-
user_row.set_subtitle(&gettext("bot"));
342-
} else {
343-
User::this_expression("status")
344-
.chain_closure::<String>(closure!(
345-
|_: Option<glib::Object>, status: BoxedUserStatus| {
346-
strings::user_status(&status.0)
347-
}
348-
))
349-
.bind(&user_row, "subtitle", Some(&user));
350-
};
351-
352-
let avatar = crate::session::components::Avatar::new();
353-
354-
avatar.set_item(Some(user.upcast()));
355-
avatar.set_size(32);
356-
user_row.add_prefix(&avatar);
357-
358-
let status = match member.status {
359-
ChatMemberStatus::Creator(owner) => {
360-
let title = if owner.custom_title.is_empty() {
361-
gettext("Owner")
362-
} else {
363-
owner.custom_title
364-
};
365-
Some(title)
366-
}
367-
ChatMemberStatus::Administrator(admin) => {
368-
let title = if admin.custom_title.is_empty() {
369-
gettext("Admin")
370-
} else {
371-
admin.custom_title
372-
};
373-
Some(title)
374-
}
375-
_ => None,
376-
};
377-
378-
if let Some(text) = status {
379-
let owner_label = gtk::Label::new(Some(&text));
380-
owner_label.set_yalign(0.2);
381-
owner_label.set_css_classes(&["caption", "accent"]);
382-
user_row.add_suffix(&owner_label);
323+
for member in &members {
324+
let user = match member.member_id {
325+
MessageSender::User(ref user) => {
326+
let TdUser(user) =
327+
functions::get_user(user.user_id, client_id).await.unwrap();
328+
User::from_td_object(user, &session)
383329
}
384-
385-
members_list.append(&user_row);
386-
}
330+
MessageSender::Chat(_) => unreachable!(),
331+
};
332+
users.push(user);
387333
}
334+
335+
members
336+
.into_iter()
337+
.zip(users.into_iter())
338+
.map(|(member, user)| ChatMember::new(member, user))
339+
.collect()
340+
};
341+
342+
let members_list = &self.imp().members_list;
343+
344+
let selection_model: gtk::NoSelection = members_list.model().unwrap().downcast().unwrap();
345+
346+
let model: gtk::gio::ListStore = if let Some(model) = selection_model.model() {
347+
model.downcast().unwrap()
348+
} else {
349+
let model = gtk::gio::ListStore::new(ChatMember::static_type());
350+
selection_model.set_model(Some(&model));
351+
model
352+
};
353+
354+
model.extend_from_slice(&members);
355+
356+
if members_list.factory().is_none() {
357+
let factory = gtk::SignalListItemFactory::new();
358+
359+
factory.connect_setup(move |_, list_item| {
360+
list_item.set_property("child", ChatMemberRow::new());
361+
});
362+
363+
factory.connect_bind(move |_, list_item| {
364+
let list_item: &gtk::ListItem = list_item.downcast_ref().unwrap();
365+
366+
let user_row: ChatMemberRow = list_item.child().unwrap().downcast().unwrap();
367+
let member: ChatMember = list_item.item().unwrap().downcast().unwrap();
368+
369+
user_row.bind_member(member);
370+
});
371+
372+
members_list.set_factory(Some(&factory));
388373
}
389374
}
390375

0 commit comments

Comments
 (0)