Browse Source

room-details: Fix member selection not toggled when using PermissionsSelectMemberRow checkbox

It only worked when activating the full row.
fractal-12
Kévin Commaille 8 months ago
parent
commit
c5d7b4d6e7
No known key found for this signature in database
GPG Key ID: F26F4BE20A08255B
  1. 2
      src/session/view/content/room_details/invite_subpage/row.ui
  2. 288
      src/session/view/content/room_details/permissions/add_members_subpage.rs
  3. 2
      src/session/view/content/room_details/permissions/select_member_row.ui

2
src/session/view/content/room_details/invite_subpage/row.ui

@ -71,7 +71,7 @@
<lookup name="can-invite">
<lookup name="item">RoomDetailsInviteRow</lookup>
</lookup>
</binding>
</binding>
</object>
</child>
<child>

288
src/session/view/content/room_details/permissions/add_members_subpage.rs

@ -75,121 +75,19 @@ mod imp {
fn constructed(&self) {
self.parent_constructed();
let obj = self.obj();
self.search_entry.connect_pill_removed(clone!(
#[weak(rename_to = imp)]
self,
move |_, source| {
if let Ok(member) = source.downcast::<Member>() {
imp.remove_selected(member.user_id());
imp.remove_selected(&member);
}
}
));
self.list_view.connect_activate(clone!(
#[weak(rename_to = imp)]
self,
move |list_view, index| {
let Some(member) = list_view
.model()
.and_then(|m| m.item(index))
.and_downcast::<Member>()
else {
return;
};
imp.toggle_selected(member);
}
));
let user_expr = gtk::ClosureExpression::new::<String>(
&[] as &[gtk::Expression],
closure!(|item: Option<glib::Object>| {
item.and_downcast_ref()
.map(Member::search_string)
.unwrap_or_default()
}),
);
let search_filter = gtk::StringFilter::builder()
.match_mode(gtk::StringFilterMatchMode::Substring)
.expression(expression::normalize_string(user_expr))
.ignore_case(true)
.build();
expression::normalize_string(self.search_entry.property_expression("text")).bind(
&search_filter,
"search",
None::<&glib::Object>,
);
let filter = gtk::EveryFilter::new();
filter.append(self.power_level_filter.clone());
filter.append(search_filter);
self.filtered_model.set_filter(Some(&filter));
self.filtered_model.connect_items_changed(clone!(
#[weak(rename_to = imp)]
self,
move |_, _, _, _| {
imp.update_visible_page();
}
));
// Sort members by display name, then user ID.
let display_name_expr = Member::this_expression("display-name");
let display_name_sorter = gtk::StringSorter::new(Some(display_name_expr));
let user_id_expr = Member::this_expression("user-id-string");
let user_id_sorter = gtk::StringSorter::new(Some(user_id_expr));
let sorter = gtk::MultiSorter::new();
sorter.append(display_name_sorter);
sorter.append(user_id_sorter);
let sorted_model =
gtk::SortListModel::new(Some(self.filtered_model.clone()), Some(sorter));
self.list_view
.set_model(Some(&gtk::NoSelection::new(Some(sorted_model))));
let factory = gtk::SignalListItemFactory::new();
factory.connect_setup(clone!(
#[weak]
obj,
move |_, item| {
let Some(item) = item.downcast_ref::<gtk::ListItem>() else {
error!("List item factory did not receive a list item: {item:?}");
return;
};
let row = PermissionsSelectMemberRow::new();
item.set_child(Some(&row));
item.bind_property("item", &row, "member")
.sync_create()
.build();
item.set_selectable(false);
obj.connect_selection_changed(clone!(
#[weak]
row,
move |obj| {
let Some(member) = row.member() else {
return;
};
let selected = obj
.imp()
.selected_members
.borrow()
.contains_key(member.user_id());
row.set_selected(selected);
}
));
}
));
self.list_view.set_factory(Some(&factory));
self.initialize_filtered_model();
self.initialize_list_view();
}
}
@ -265,42 +163,58 @@ mod imp {
self.stack.set_visible_child_name(visible_page);
}
/// Whether the member with the given ID is selected.
fn is_selected(&self, user_id: &OwnedUserId) -> bool {
self.selected_members.borrow().contains_key(user_id)
}
/// Toggle whether the given member is selected.
fn toggle_selected(&self, member: Member) {
let is_empty = {
let mut selected_members = self.selected_members.borrow_mut();
let is_selected = self.is_selected(member.user_id());
if is_selected {
self.remove_selected(&member);
} else {
self.add_selected(member);
}
}
/// Add the given member to the selected list.
fn add_selected(&self, member: Member) {
{
let mut selected_members = self.selected_members.borrow_mut();
let user_id = member.user_id();
let was_selected = selected_members.remove(user_id).is_some();
if was_selected {
self.search_entry.remove_pill(&member.identifier());
} else {
self.search_entry.add_pill(&member);
selected_members.insert(user_id.clone(), member);
if selected_members.contains_key(user_id) {
// Nothing to do.
return;
}
selected_members.is_empty()
};
self.search_entry.add_pill(&member);
selected_members.insert(user_id.clone(), member);
}
self.add_button.set_sensitive(!is_empty);
self.add_button.set_sensitive(true);
self.obj().emit_by_name::<()>("selection-changed", &[]);
}
/// Remove the member with the given user ID from the selected list.
fn remove_selected(&self, user_id: &OwnedUserId) {
let (was_removed, is_empty) = {
/// Remove the given member from the selected list.
fn remove_selected(&self, member: &Member) {
let is_empty = {
let mut selected_members = self.selected_members.borrow_mut();
let was_removed = selected_members.remove(user_id).is_some();
let is_empty = selected_members.is_empty();
(was_removed, is_empty)
if selected_members.remove(member.user_id()).is_none() {
// Nothing happened.
return;
}
self.search_entry.remove_pill(&member.identifier());
selected_members.is_empty()
};
if was_removed {
self.add_button.set_sensitive(!is_empty);
self.obj().emit_by_name::<()>("selection-changed", &[]);
}
self.add_button.set_sensitive(!is_empty);
self.obj().emit_by_name::<()>("selection-changed", &[]);
}
/// Add the selected members to the list of members with custom power
@ -334,6 +248,128 @@ mod imp {
self.add_button.set_sensitive(false);
obj.emit_by_name::<()>("selection-changed", &[]);
}
fn initialize_filtered_model(&self) {
let user_expr = gtk::ClosureExpression::new::<String>(
&[] as &[gtk::Expression],
closure!(|item: Option<glib::Object>| {
item.and_downcast_ref()
.map(Member::search_string)
.unwrap_or_default()
}),
);
let search_filter = gtk::StringFilter::builder()
.match_mode(gtk::StringFilterMatchMode::Substring)
.expression(expression::normalize_string(user_expr))
.ignore_case(true)
.build();
expression::normalize_string(self.search_entry.property_expression("text")).bind(
&search_filter,
"search",
None::<&glib::Object>,
);
let filter = gtk::EveryFilter::new();
filter.append(self.power_level_filter.clone());
filter.append(search_filter);
self.filtered_model.set_filter(Some(&filter));
self.filtered_model.connect_items_changed(clone!(
#[weak(rename_to = imp)]
self,
move |_, _, _, _| {
imp.update_visible_page();
}
));
}
fn initialize_list_view(&self) {
self.list_view.connect_activate(clone!(
#[weak(rename_to = imp)]
self,
move |list_view, index| {
let Some(member) = list_view
.model()
.and_then(|m| m.item(index))
.and_downcast::<Member>()
else {
return;
};
imp.toggle_selected(member);
}
));
// Sort members by display name, then user ID.
let display_name_expr = Member::this_expression("display-name");
let display_name_sorter = gtk::StringSorter::new(Some(display_name_expr));
let user_id_expr = Member::this_expression("user-id-string");
let user_id_sorter = gtk::StringSorter::new(Some(user_id_expr));
let sorter = gtk::MultiSorter::new();
sorter.append(display_name_sorter);
sorter.append(user_id_sorter);
let sorted_model =
gtk::SortListModel::new(Some(self.filtered_model.clone()), Some(sorter));
self.list_view
.set_model(Some(&gtk::NoSelection::new(Some(sorted_model))));
let factory = gtk::SignalListItemFactory::new();
factory.connect_setup(clone!(
#[weak(rename_to = imp)]
self,
move |_, item| {
let Some(item) = item.downcast_ref::<gtk::ListItem>() else {
error!("List item factory did not receive a list item: {item:?}");
return;
};
let row = PermissionsSelectMemberRow::new();
item.set_child(Some(&row));
item.bind_property("item", &row, "member")
.sync_create()
.build();
item.set_selectable(false);
// Toggle the selection when the checkbox is toggled.
row.connect_selected_notify(clone!(
#[weak]
imp,
move |row| {
let Some(member) = row.member() else {
return;
};
if row.selected() {
imp.add_selected(member);
} else {
imp.remove_selected(&member);
}
}
));
// Toggle the checkbox when the selection changed.
imp.obj().connect_selection_changed(clone!(
#[weak]
row,
move |obj| {
let Some(member) = row.member() else {
return;
};
let selected = obj.imp().is_selected(member.user_id());
row.set_selected(selected);
}
));
}
));
self.list_view.set_factory(Some(&factory));
}
}
}

2
src/session/view/content/room_details/permissions/select_member_row.ui

@ -79,7 +79,7 @@
<child>
<object class="GtkCheckButton" id="check_button">
<property name="focusable">False</property>
<property name="active" bind-source="RoomDetailsPermissionsSelectMemberRow" bind-property="selected" bind-flags="sync-create" />
<property name="active" bind-source="RoomDetailsPermissionsSelectMemberRow" bind-property="selected" bind-flags="sync-create | bidirectional" />
</object>
</child>
</object>

Loading…
Cancel
Save