Browse Source

chore: Use new glib::clone! macro syntax

merge-requests/1716/head
Kévin Commaille 2 years ago
parent
commit
fd759155b3
No known key found for this signature in database
GPG Key ID: C971D9DBC9D678D
  1. 1
      Cargo.lock
  2. 1
      Cargo.toml
  3. 10
      src/account_switcher/account_switcher_button.rs
  4. 9
      src/account_switcher/account_switcher_popover.rs
  5. 111
      src/application.rs
  6. 10
      src/components/avatar/editable.rs
  7. 16
      src/components/avatar/image.rs
  8. 10
      src/components/avatar/overlapping.rs
  9. 33
      src/components/context_menu_bin.rs
  10. 81
      src/components/crypto/identity_setup_view.rs
  11. 19
      src/components/dialogs/auth.rs
  12. 17
      src/components/dialogs/join_room.rs
  13. 12
      src/components/dialogs/user_profile.rs
  14. 10
      src/components/drag_overlay.rs
  15. 10
      src/components/media/content_viewer.rs
  16. 12
      src/components/media/image_paintable.rs
  17. 19
      src/components/media/video_player.rs
  18. 18
      src/components/offline_banner.rs
  19. 20
      src/components/pill/mod.rs
  20. 25
      src/components/pill/search_entry.rs
  21. 30
      src/components/power_level_selection/popover.rs
  22. 9
      src/components/reaction_chooser.rs
  23. 12
      src/components/rows/button_row.rs
  24. 15
      src/components/rows/check_loading_row.rs
  25. 95
      src/components/rows/combo_loading_row.rs
  26. 9
      src/components/rows/loading_row.rs
  27. 10
      src/components/rows/substring_entry_row.rs
  28. 15
      src/components/rows/switch_loading_row.rs
  29. 38
      src/components/scale_revealer.rs
  30. 55
      src/components/user_page.rs
  31. 29
      src/contrib/qr_code_scanner/camera/camera_paintable/linux.rs
  32. 22
      src/contrib/qr_code_scanner/mod.rs
  33. 13
      src/identity_verification_view/accept_request_page.rs
  34. 22
      src/identity_verification_view/cancelled_page.rs
  35. 23
      src/identity_verification_view/choose_method_page.rs
  36. 13
      src/identity_verification_view/completed_page.rs
  37. 13
      src/identity_verification_view/confirm_qr_code_page.rs
  38. 25
      src/identity_verification_view/mod.rs
  39. 22
      src/identity_verification_view/no_supported_methods_page.rs
  40. 13
      src/identity_verification_view/qr_code_scanned_page.rs
  41. 22
      src/identity_verification_view/sas_page.rs
  42. 54
      src/identity_verification_view/scan_qr_code_page.rs
  43. 13
      src/identity_verification_view/wait_for_other_page.rs
  44. 12
      src/login/homeserver_page.rs
  45. 7
      src/login/idp_button.rs
  46. 19
      src/login/method_page.rs
  47. 39
      src/login/mod.rs
  48. 71
      src/login/session_setup_view.rs
  49. 10
      src/session/model/ignored_users.rs
  50. 40
      src/session/model/notifications/notifications_settings.rs
  51. 10
      src/session/model/remote_room.rs
  52. 21
      src/session/model/room/join_rule.rs
  53. 34
      src/session/model/room/member.rs
  54. 20
      src/session/model/room/member_list.rs
  55. 225
      src/session/model/room/mod.rs
  56. 10
      src/session/model/room/permissions.rs
  57. 20
      src/session/model/room/timeline/mod.rs
  58. 10
      src/session/model/room_list/mod.rs
  59. 50
      src/session/model/session.rs
  60. 12
      src/session/model/sidebar_data/category/mod.rs
  61. 10
      src/session/model/sidebar_data/item_list.rs
  62. 45
      src/session/model/sidebar_data/list_model.rs
  63. 9
      src/session/model/sidebar_data/selection.rs
  64. 30
      src/session/model/user.rs
  65. 22
      src/session/model/user_sessions_list/mod.rs
  66. 38
      src/session/model/verification/identity_verification.rs
  67. 10
      src/session/model/verification/verification_list.rs
  68. 21
      src/session/view/account_settings/general_page/change_password_subpage.rs
  69. 32
      src/session/view/account_settings/general_page/deactivate_account_subpage.rs
  70. 35
      src/session/view/account_settings/general_page/mod.rs
  71. 50
      src/session/view/account_settings/mod.rs
  72. 93
      src/session/view/account_settings/notifications_page.rs
  73. 44
      src/session/view/account_settings/security_page/ignored_users_subpage/mod.rs
  74. 41
      src/session/view/account_settings/security_page/mod.rs
  75. 42
      src/session/view/account_settings/user_sessions_page/mod.rs
  76. 9
      src/session/view/account_settings/user_sessions_page/user_session_row.rs
  77. 53
      src/session/view/content/explore/mod.rs
  78. 25
      src/session/view/content/explore/public_room.rs
  79. 24
      src/session/view/content/explore/public_room_list.rs
  80. 39
      src/session/view/content/explore/public_room_row.rs
  81. 10
      src/session/view/content/explore/server_list.rs
  82. 34
      src/session/view/content/explore/servers_popover.rs
  83. 18
      src/session/view/content/invite.rs
  84. 32
      src/session/view/content/mod.rs
  85. 107
      src/session/view/content/room_details/addresses_subpage/completion_popover.rs
  86. 133
      src/session/view/content/room_details/addresses_subpage/mod.rs
  87. 272
      src/session/view/content/room_details/general_page/mod.rs
  88. 36
      src/session/view/content/room_details/history_viewer/audio.rs
  89. 28
      src/session/view/content/room_details/history_viewer/audio_row.rs
  90. 36
      src/session/view/content/room_details/history_viewer/file.rs
  91. 36
      src/session/view/content/room_details/history_viewer/media.rs
  92. 40
      src/session/view/content/room_details/history_viewer/media_item.rs
  93. 30
      src/session/view/content/room_details/invite_subpage/list.rs
  94. 30
      src/session/view/content/room_details/invite_subpage/mod.rs
  95. 33
      src/session/view/content/room_details/members_page/members_list_view/extra_lists.rs
  96. 10
      src/session/view/content/room_details/members_page/members_list_view/membership_subpage_row.rs
  97. 18
      src/session/view/content/room_details/members_page/members_list_view/mod.rs
  98. 15
      src/session/view/content/room_details/members_page/mod.rs
  99. 108
      src/session/view/content/room_details/permissions/add_members_subpage.rs
  100. 9
      src/session/view/content/room_details/permissions/member_power_level.rs
  101. Some files were not shown because too many files have changed in this diff Show More

1
Cargo.lock generated

@ -1410,6 +1410,7 @@ dependencies = [
"futures-util",
"geo-uri",
"gettext-rs",
"glib",
"gst-plugin-gtk4",
"gstreamer",
"gstreamer-base",

1
Cargo.toml

@ -51,6 +51,7 @@ url = "2"
# gtk-rs project and dependents. These usually need to be updated together.
adw = { package = "libadwaita", version = "0.6", features = ["v1_5"] }
glib = { version = "0.19", features = ["unstable-clone-syntax"] } # Remove when GTK depends on version with new clone syntax
gst = { version = "0.22", package = "gstreamer" }
gst_base = { version = "0.22", package = "gstreamer-base" }
gst_gtk = { version = "0.12", package = "gst-plugin-gtk4" }

10
src/account_switcher/account_switcher_button.rs

@ -116,9 +116,13 @@ impl AccountSwitcherButton {
parent.set_popover(None);
}
let closed_handler = popover.connect_closed(clone!(@weak self as obj => move |_| {
obj.set_active(false);
}));
let closed_handler = popover.connect_closed(clone!(
#[weak(rename_to = obj)]
self,
move |_| {
obj.set_active(false);
}
));
popover.set_parent(self);
imp.popover.set(popover, vec![closed_handler]);

9
src/account_switcher/account_switcher_popover.rs

@ -68,10 +68,13 @@ mod imp {
});
if let Some(selection) = &selection {
let selected_handler =
selection.connect_selected_item_notify(clone!(@weak obj => move |selection| {
let selected_handler = selection.connect_selected_item_notify(clone!(
#[weak]
obj,
move |selection| {
obj.update_selected_item(selection.selected());
}));
}
));
obj.update_selected_item(selection.selected());
self.session_selection

111
src/application.rs

@ -56,18 +56,23 @@ mod imp {
app.set_up_gactions();
app.set_up_accels();
self.session_list
.connect_error_notify(clone!(@weak app => move |session_list| {
self.session_list.connect_error_notify(clone!(
#[weak]
app,
move |session_list| {
if let Some(message) = session_list.error() {
let window = app.present_main_window();
window.show_secret_error(&message);
}
}));
spawn!(
clone!(@weak self.session_list as session_list => async move {
}
));
spawn!(clone!(
#[weak(rename_to = session_list)]
self.session_list,
async move {
session_list.restore_sessions().await;
})
);
}
));
}
}
@ -228,15 +233,25 @@ impl Application {
dialog.add_credit_section(Some(&gettext("Name by")), &["Regina Bíró"]);
// If the user wants our support room, try to open it ourselves.
dialog.connect_activate_link(clone!(@weak self as obj, @weak dialog => @default-return false, move |_, uri| {
if uri == "https://matrix.to/#/#fractal:gnome.org" && obj.session_list().has_session_ready() {
obj.process_uri(uri);
dialog.close();
return true;
}
dialog.connect_activate_link(clone!(
#[weak(rename_to = obj)]
self,
#[weak]
dialog,
#[upgrade_or]
false,
move |_, uri| {
if uri == "https://matrix.to/#/#fractal:gnome.org"
&& obj.session_list().has_session_ready()
{
obj.process_uri(uri);
dialog.close();
return true;
}
false
}));
false
}
));
dialog.present(&self.present_main_window());
}
@ -277,26 +292,34 @@ impl Application {
self.process_session_intent(session_intent);
}
_ => {
spawn!(clone!(@weak self as obj => async move {
obj.choose_session_for_uri(matrix_uri).await;
}));
spawn!(clone!(
#[weak(rename_to = obj)]
self,
async move {
obj.choose_session_for_uri(matrix_uri).await;
}
));
}
},
}
} else {
// Wait for the list to be ready.
let cell = Rc::new(RefCell::new(Some(intent)));
let handler = session_list.connect_state_notify(
clone!(@weak self as app, @strong cell => move |session_list| {
let handler = session_list.connect_state_notify(clone!(
#[weak(rename_to = obj)]
self,
#[strong]
cell,
move |session_list| {
if session_list.state() == LoadingState::Ready {
app.imp().intent_handler.disconnect_signals();
obj.imp().intent_handler.disconnect_signals();
if let Some(intent) = cell.take() {
app.process_intent(intent);
obj.process_intent(intent);
}
}
}),
);
}
));
self.imp()
.intent_handler
.set(session_list.upcast_ref(), vec![handler]);
@ -335,17 +358,22 @@ impl Application {
} else {
// Wait for the session to be ready.
let cell = Rc::new(RefCell::new(Some(intent)));
let handler = session.connect_state_notify(
clone!(@weak self as app, @strong cell => move |session| {
let handler = session.connect_state_notify(clone!(
#[weak(rename_to = obj)]
self,
#[strong]
cell,
move |session| {
if session.state() == SessionState::Ready {
app.imp().intent_handler.disconnect_signals();
obj.imp().intent_handler.disconnect_signals();
if let Some(intent) = cell.take() {
app.present_main_window().process_session_intent_ready(intent);
obj.present_main_window()
.process_session_intent_ready(intent);
}
}
}),
);
}
));
self.imp()
.intent_handler
.set(session.upcast_ref(), vec![handler]);
@ -354,31 +382,38 @@ impl Application {
// Wait for the session to be a `Session`.
let session_list = self.session_list();
let cell = Rc::new(RefCell::new(Some(intent)));
let handler = session_list.connect_items_changed(
clone!(@weak self as app, @strong cell => move |session_list, pos, _, added| {
let handler = session_list.connect_items_changed(clone!(
#[weak(rename_to = obj)]
self,
#[strong]
cell,
move |session_list, pos, _, added| {
if added == 0 {
return;
}
let Some(session_id) = cell.borrow().as_ref().map(|i| i.session_id().to_owned()) else {
let Some(session_id) =
cell.borrow().as_ref().map(|i| i.session_id().to_owned())
else {
return;
};
for i in pos..pos + added {
let Some(session_info) = session_list.item(i).and_downcast::<SessionInfo>() else {
let Some(session_info) = session_list.item(i).and_downcast::<SessionInfo>()
else {
break;
};
if session_info.session_id() == session_id {
app.imp().intent_handler.disconnect_signals();
obj.imp().intent_handler.disconnect_signals();
if let Some(intent) = cell.take() {
app.process_session_intent(intent);
obj.process_session_intent(intent);
}
break;
}
}
}),
);
}
));
self.imp()
.intent_handler
.set(session_list.upcast_ref(), vec![handler]);

10
src/components/avatar/editable.rs

@ -213,9 +213,13 @@ mod imp {
obj.set_edit_state(ActionState::Success);
glib::timeout_add_local_once(
Duration::from_secs(2),
clone!(@weak obj => move || {
obj.set_state(EditableAvatarState::Default);
}),
clone!(
#[weak]
obj,
move || {
obj.set_state(EditableAvatarState::Default);
}
),
);
}
EditableAvatarState::RemovalInProgress => {

16
src/components/avatar/image.rs

@ -150,12 +150,16 @@ impl AvatarImage {
spawn!(
glib::Priority::LOW,
clone!(@weak self as obj => async move {
match handle.await.unwrap() {
Ok(data) => obj.set_image_data(Some(data)),
Err(error) => error!("Could not fetch avatar: {error}"),
};
})
clone!(
#[weak(rename_to = obj)]
self,
async move {
match handle.await.unwrap() {
Ok(data) => obj.set_image_data(Some(data)),
Err(error) => error!("Could not fetch avatar: {error}"),
};
}
)
);
}
}

10
src/components/avatar/overlapping.rs

@ -214,11 +214,13 @@ impl OverlappingAvatars {
return;
};
let signal_handler_id = model.connect_items_changed(
clone!(@weak self as obj => move |model, position, removed, added| {
let signal_handler_id = model.connect_items_changed(clone!(
#[weak(rename_to = obj)]
self,
move |model, position, removed, added| {
obj.handle_items_changed(model, position, removed, added)
}),
);
}
));
imp.bound_model
.set(model.clone().upcast(), vec![signal_handler_id]);

33
src/components/context_menu_bin.rs

@ -90,17 +90,22 @@ mod imp {
fn constructed(&self) {
let obj = self.obj();
self.long_press_gesture
.connect_pressed(clone!(@weak obj => move |gesture, x, y| {
self.long_press_gesture.connect_pressed(clone!(
#[weak]
obj,
move |gesture, x, y| {
if obj.has_context_menu() {
gesture.set_state(gtk::EventSequenceState::Claimed);
gesture.reset();
obj.open_menu_at(x as i32, y as i32);
}
}));
}
));
self.click_gesture.connect_released(
clone!(@weak obj => move |gesture, n_press, x, y| {
self.click_gesture.connect_released(clone!(
#[weak]
obj,
move |gesture, n_press, x, y| {
if n_press > 1 {
return;
}
@ -109,8 +114,8 @@ mod imp {
gesture.set_state(gtk::EventSequenceState::Claimed);
obj.open_menu_at(x as i32, y as i32);
}
}),
);
}
));
self.parent_constructed();
}
@ -167,12 +172,18 @@ mod imp {
popover.unparent();
popover.set_parent(&*obj);
let parent_handler =
popover.connect_parent_notify(clone!(@weak obj => move |popover| {
if !popover.parent().is_some_and(|w| &w == obj.upcast_ref::<gtk::Widget>()) {
let parent_handler = popover.connect_parent_notify(clone!(
#[weak]
obj,
move |popover| {
if !popover
.parent()
.is_some_and(|w| &w == obj.upcast_ref::<gtk::Widget>())
{
obj.imp().popover.disconnect_signals();
}
}));
}
));
self.popover.set(popover, vec![parent_handler]);
}

81
src/components/crypto/identity_setup_view.rs

@ -115,9 +115,13 @@ mod imp {
fn dispose(&self) {
if let Some(verification) = self.verification.obj() {
spawn!(clone!(@strong verification => async move {
let _ = verification.cancel().await;
}));
spawn!(clone!(
#[strong]
verification,
async move {
let _ = verification.cancel().await;
}
));
}
if let Some(session) = self.session.upgrade() {
@ -162,9 +166,13 @@ mod imp {
let recovery_view = CryptoRecoverySetupView::new(&session);
let obj = self.obj();
recovery_view.connect_completed(clone!(@weak obj => move |_| {
obj.emit_completed(CryptoIdentitySetupNextStep::None);
}));
recovery_view.connect_completed(clone!(
#[weak]
obj,
move |_| {
obj.emit_completed(CryptoIdentitySetupNextStep::None);
}
));
recovery_view
})
@ -176,8 +184,10 @@ mod imp {
// Use received verification requests too.
let verification_list = session.verification_list();
let verification_list_handler = verification_list.connect_items_changed(
clone!(@weak self as imp => move |verification_list, _, _, _| {
let verification_list_handler = verification_list.connect_items_changed(clone!(
#[weak(rename_to = imp)]
self,
move |verification_list, _, _, _| {
if imp.verification.obj().is_some() {
// We don't want to override the current verification.
return;
@ -186,8 +196,8 @@ mod imp {
if let Some(verification) = verification_list.ongoing_session_verification() {
imp.set_verification(Some(verification));
}
}),
);
}
));
self.verification_list_handler
.replace(Some(verification_list_handler));
@ -264,34 +274,47 @@ mod imp {
if let Some(verification) = prev_verification {
if !verification.is_finished() {
spawn!(clone!(@strong verification => async move {
let _ = verification.cancel().await;
}));
spawn!(clone!(
#[strong]
verification,
async move {
let _ = verification.cancel().await;
}
));
}
self.verification.disconnect_signals();
}
if let Some(verification) = &verification {
let replaced_handler = verification.connect_replaced(
clone!(@weak self as imp => move |_, new_verification| {
let replaced_handler = verification.connect_replaced(clone!(
#[weak(rename_to = imp)]
self,
move |_, new_verification| {
imp.set_verification(Some(new_verification.clone()));
}),
);
let done_handler = verification.connect_done(
clone!(@weak obj => @default-return glib::Propagation::Stop, move |verification| {
}
));
let done_handler = verification.connect_done(clone!(
#[weak]
obj,
#[upgrade_or]
glib::Propagation::Stop,
move |verification| {
obj.emit_completed(CryptoIdentitySetupNextStep::EnableRecovery);
obj.imp().set_verification(None);
verification.remove_from_list();
glib::Propagation::Stop
}),
);
let remove_handler =
verification.connect_dismiss(clone!(@weak self as imp => move |_| {
}
));
let remove_handler = verification.connect_dismiss(clone!(
#[weak(rename_to = imp)]
self,
move |_| {
imp.navigation.pop();
imp.set_verification(None);
}));
}
));
self.verification.set(
verification,
@ -328,9 +351,13 @@ mod imp {
.tag(CryptoIdentitySetupPage::Recovery.as_ref())
.child(recovery_view)
.build();
page.connect_shown(clone!(@weak recovery_view => move |_| {
recovery_view.grab_focus();
}));
page.connect_shown(clone!(
#[weak]
recovery_view,
move |_| {
recovery_view.grab_focus();
}
));
page
}

19
src/components/dialogs/auth.rs

@ -94,10 +94,13 @@ mod imp {
self.parent_constructed();
let obj = self.obj();
self.password
.connect_changed(clone!(@weak obj => move |password| {
self.password.connect_changed(clone!(
#[weak]
obj,
move |password| {
obj.set_response_enabled("confirm", !password.text().is_empty());
}));
}
));
}
}
@ -328,9 +331,10 @@ impl AuthDialog {
"{homeserver}_matrix/client/r0/auth/{auth_type}/fallback/web?session={uiaa_session}"
);
let handler = imp
.open_browser_btn
.connect_clicked(clone!(@weak self as obj => move |_| {
let handler = imp.open_browser_btn.connect_clicked(clone!(
#[weak(rename_to = obj)]
self,
move |_| {
let uri = uri.clone();
spawn!(async move {
let Some(parent) = obj.parent() else {
@ -346,7 +350,8 @@ impl AuthDialog {
obj.set_response_enabled("confirm", true);
});
}));
}
));
imp.open_browser_btn_handler.replace(Some(handler));
}

17
src/components/dialogs/join_room.rs

@ -110,11 +110,18 @@ mod imp {
) {
obj.fill_details();
} else {
room.connect_loading_state_notify(clone!(@weak obj => move |room| {
if matches!(room.loading_state(), LoadingState::Ready | LoadingState::Error) {
obj.fill_details();
}
}));
room.connect_loading_state_notify(clone!(
#[weak]
obj,
move |room| {
if matches!(
room.loading_state(),
LoadingState::Ready | LoadingState::Error
) {
obj.fill_details();
}
}
));
}
}

12
src/components/dialogs/user_profile.rs

@ -68,10 +68,14 @@ impl UserProfileDialog {
let user = RemoteUser::new(session, user_id);
imp.user_page.set_user(Some(user.clone()));
spawn!(clone!(@weak imp => async move {
user.load_profile().await;
imp.stack.set_visible_child_name("details");
}));
spawn!(clone!(
#[weak]
imp,
async move {
user.load_profile().await;
imp.stack.set_visible_child_name("details");
}
));
}
/// Set the member to present.

10
src/components/drag_overlay.rs

@ -102,8 +102,10 @@ mod imp {
}
self.drop_target.disconnect_signals();
let handler_id = drop_target.connect_current_drop_notify(
clone!(@weak self.revealer as revealer => move |target| {
let handler_id = drop_target.connect_current_drop_notify(clone!(
#[weak(rename_to = revealer)]
self.revealer,
move |target| {
let reveal = target.current_drop().is_some();
if reveal {
@ -111,8 +113,8 @@ mod imp {
}
revealer.set_reveal_child(reveal);
}),
);
}
));
obj.add_controller(drop_target.clone());
self.drop_target.set(drop_target, vec![handler_id]);

10
src/components/media/content_viewer.rs

@ -168,9 +168,13 @@ impl MediaContentViewer {
pub fn view_file(&self, file: gio::File) {
self.show_loading();
spawn!(clone!(@weak self as obj => async move {
obj.view_file_inner(file).await;
}));
spawn!(clone!(
#[weak(rename_to = obj)]
self,
async move {
obj.view_file_inner(file).await;
}
));
}
async fn view_file_inner(&self, file: gio::File) {

12
src/components/media/image_paintable.rs

@ -359,10 +359,14 @@ impl ImagePaintable {
self.invalidate_contents();
// Update the frame when the duration is elapsed.
let update_frame_callback = glib::clone!(@weak self as obj => move || {
obj.imp().timeout_source_id.take();
obj.update_frame();
});
let update_frame_callback = glib::clone!(
#[weak(rename_to = obj)]
self,
move || {
obj.imp().timeout_source_id.take();
obj.update_frame();
}
);
let source_id = glib::timeout_add_local_once(next_frame.duration, update_frame_callback);
imp.timeout_source_id.replace(Some(source_id));

19
src/components/media/video_player.rs

@ -57,12 +57,19 @@ mod imp {
self.parent_constructed();
let obj = self.obj();
let bus_guard = self.player
let bus_guard = self
.player
.message_bus()
.add_watch_local(
clone!(@weak obj => @default-return glib::ControlFlow::Break, move |_, message| {
.add_watch_local(clone!(
#[weak]
obj,
#[upgrade_or]
glib::ControlFlow::Break,
move |_, message| {
match PlayMessage::parse(message) {
Ok(PlayMessage::DurationChanged { duration }) => obj.duration_changed(duration),
Ok(PlayMessage::DurationChanged { duration }) => {
obj.duration_changed(duration)
}
Ok(PlayMessage::Warning { error, .. }) => {
warn!("Warning playing video: {error}");
}
@ -73,8 +80,8 @@ mod imp {
}
glib::ControlFlow::Continue
}),
)
}
))
.unwrap();
self.bus_guard.set(bus_guard).unwrap();
}

18
src/components/offline_banner.rs

@ -59,10 +59,13 @@ mod imp {
self.session.disconnect_signals();
if let Some(session) = session {
let offline_handler =
session.connect_offline_notify(clone!(@weak self as imp => move |_| {
let offline_handler = session.connect_offline_notify(clone!(
#[weak(rename_to = imp)]
self,
move |_| {
imp.update();
}));
}
));
self.session.set(session, vec![offline_handler]);
}
@ -83,10 +86,13 @@ mod imp {
}
let monitor = gio::NetworkMonitor::default();
let monitor_handler =
monitor.connect_network_changed(clone!(@weak self as imp => move |_, _| {
let monitor_handler = monitor.connect_network_changed(clone!(
#[weak(rename_to = imp)]
self,
move |_, _| {
imp.update();
}));
}
));
self.monitor_handler.replace(Some(monitor_handler));
}

20
src/components/pill/mod.rs

@ -97,11 +97,13 @@ mod imp {
self.source.disconnect_signals();
if let Some(source) = source {
let display_name_handler = source.connect_disambiguated_name_notify(
clone!(@weak self as imp => move |source| {
let display_name_handler = source.connect_disambiguated_name_notify(clone!(
#[weak(rename_to = imp)]
self,
move |source| {
imp.set_display_name(&source.disambiguated_name());
}),
);
}
));
self.set_display_name(&source.disambiguated_name());
self.source.set(source, vec![display_name_handler]);
@ -126,9 +128,13 @@ mod imp {
if activatable {
let gesture_click = gtk::GestureClick::new();
gesture_click.connect_released(clone!(@weak obj => move |_, _, _, _| {
obj.activate();
}));
gesture_click.connect_released(clone!(
#[weak]
obj,
move |_, _, _, _| {
obj.activate();
}
));
obj.add_controller(gesture_click.clone());
self.gesture_click.replace(Some(gesture_click));

25
src/components/pill/search_entry.rs

@ -68,8 +68,10 @@ mod imp {
self.parent_constructed();
let obj = self.obj();
self.text_buffer
.connect_delete_range(clone!(@weak obj => move |_, start, end| {
self.text_buffer.connect_delete_range(clone!(
#[weak]
obj,
move |_, start, end| {
if start == end {
// Nothing to do.
return;
@ -83,7 +85,12 @@ mod imp {
.and_downcast_ref::<Pill>()
.and_then(|p| p.source())
{
let removed = obj.imp().pills.borrow_mut().remove(&source.identifier()).is_some();
let removed = obj
.imp()
.pills
.borrow_mut()
.remove(&source.identifier())
.is_some();
if removed {
obj.emit_by_name::<()>("pill-removed", &[&source]);
@ -96,7 +103,8 @@ mod imp {
break;
}
}
}));
}
));
self.text_buffer
.connect_insert_text(|text_buffer, location, text| {
@ -121,10 +129,13 @@ mod imp {
}
});
self.text_buffer
.connect_text_notify(clone!(@weak obj => move |_| {
self.text_buffer.connect_text_notify(clone!(
#[weak]
obj,
move |_| {
obj.notify_text();
}));
}
));
}
}

30
src/components/power_level_selection/popover.rs

@ -84,24 +84,30 @@ mod imp {
self.permissions.disconnect_signals();
if let Some(permissions) = permissions {
let own_pl_handler = permissions.connect_own_power_level_notify(
clone!(@weak self as imp => move |_| {
let own_pl_handler = permissions.connect_own_power_level_notify(clone!(
#[weak(rename_to = imp)]
self,
move |_| {
imp.update();
}),
);
let default_pl_handler = permissions.connect_default_power_level_notify(
clone!(@weak self as imp => move |_| {
}
));
let default_pl_handler = permissions.connect_default_power_level_notify(clone!(
#[weak(rename_to = imp)]
self,
move |_| {
imp.update_default();
imp.update_muted();
imp.update_selection();
}),
);
let muted_pl_handler = permissions.connect_mute_power_level_notify(
clone!(@weak self as imp => move |_| {
}
));
let muted_pl_handler = permissions.connect_mute_power_level_notify(clone!(
#[weak(rename_to = imp)]
self,
move |_| {
imp.update_muted();
imp.update_selection();
}),
);
}
));
self.permissions.set(
permissions,

9
src/components/reaction_chooser.rs

@ -151,10 +151,13 @@ impl ReactionChooser {
}
if let Some(reactions) = reactions.as_ref() {
let signal_handler =
reactions.connect_items_changed(clone!(@weak self as obj => move |_, _, _, _| {
let signal_handler = reactions.connect_items_changed(clone!(
#[weak(rename_to = obj)]
self,
move |_, _, _, _| {
obj.update_reactions();
}));
}
));
imp.reactions_handler.replace(Some(signal_handler));
}
imp.reactions.replace(reactions);

12
src/components/rows/button_row.rs

@ -58,11 +58,15 @@ mod imp {
self.obj().connect_parent_notify(|obj| {
if let Some(listbox) = obj.parent().and_downcast_ref::<gtk::ListBox>() {
listbox.connect_row_activated(clone!(@weak obj => move |_, row| {
if row == obj.upcast_ref::<gtk::ListBoxRow>() {
obj.emit_by_name::<()>("activated", &[]);
listbox.connect_row_activated(clone!(
#[weak]
obj,
move |_, row| {
if row == obj.upcast_ref::<gtk::ListBoxRow>() {
obj.emit_by_name::<()>("activated", &[]);
}
}
}));
));
}
});
}

15
src/components/rows/check_loading_row.rs

@ -51,12 +51,15 @@ mod imp {
self.parent_constructed();
let obj = self.obj();
self.check
.connect_active_notify(clone!(@weak obj => move |check| {
obj.update_state(&[gtk::accessible::State::Checked(bool_to_accessible_tristate(
check.is_active(),
))]);
}));
self.check.connect_active_notify(clone!(
#[weak]
obj,
move |check| {
obj.update_state(&[gtk::accessible::State::Checked(
bool_to_accessible_tristate(check.is_active()),
)]);
}
));
obj.update_state(&[gtk::accessible::State::Checked(
bool_to_accessible_tristate(self.check.is_active()),
)]);

95
src/components/rows/combo_loading_row.rs

@ -89,57 +89,68 @@ mod imp {
self.list.bind_model(
model.as_ref(),
clone!(@weak obj => @default-return { gtk::ListBoxRow::new().upcast() }, move |item| {
let Some(item) = item.downcast_ref::<gtk::StringObject>() else {
return gtk::ListBoxRow::new().upcast();
};
let string = item.string();
let child = gtk::Box::new(gtk::Orientation::Horizontal, 6);
let label = gtk::Label::builder()
.xalign(0.0)
.ellipsize(pango::EllipsizeMode::End)
.max_width_chars(40)
.valign(gtk::Align::Center)
.label(string)
.build();
child.append(&label);
clone!(
#[weak]
obj,
#[upgrade_or_else]
|| { gtk::ListBoxRow::new().upcast() },
move |item| {
let Some(item) = item.downcast_ref::<gtk::StringObject>() else {
return gtk::ListBoxRow::new().upcast();
};
let icon = gtk::Image::builder()
.accessible_role(gtk::AccessibleRole::Presentation)
.icon_name("object-select-symbolic")
.build();
let string = item.string();
let child = gtk::Box::new(gtk::Orientation::Horizontal, 6);
let label = gtk::Label::builder()
.xalign(0.0)
.ellipsize(pango::EllipsizeMode::End)
.max_width_chars(40)
.valign(gtk::Align::Center)
.label(string)
.build();
child.append(&label);
let icon = gtk::Image::builder()
.accessible_role(gtk::AccessibleRole::Presentation)
.icon_name("object-select-symbolic")
.build();
let selected_handler = obj.connect_selected_string_notify(clone!(
#[weak]
label,
#[weak]
icon,
move |obj| {
let is_selected =
obj.selected_string().is_some_and(|s| s == label.label());
let opacity = if is_selected { 1.0 } else { 0.0 };
icon.set_opacity(opacity);
}
));
obj.imp()
.selected_handlers
.borrow_mut()
.push(selected_handler);
let selected_handler = obj.connect_selected_string_notify(clone!(@weak label, @weak icon => move |obj| {
let is_selected = obj.selected_string().is_some_and(|s| s == label.label());
let opacity = if is_selected {
1.0
} else {
0.0
};
let opacity = if is_selected { 1.0 } else { 0.0 };
icon.set_opacity(opacity);
}));
obj.imp().selected_handlers.borrow_mut().push(selected_handler);
let is_selected = obj.selected_string().is_some_and(|s| s == label.label());
let opacity = if is_selected {
1.0
} else {
0.0
};
icon.set_opacity(opacity);
child.append(&icon);
child.append(&icon);
gtk::ListBoxRow::builder().child(&child).build().upcast()
}),
gtk::ListBoxRow::builder().child(&child).build().upcast()
}
),
);
if let Some(model) = model {
let items_changed_handler =
model.connect_items_changed(clone!(@weak self as imp => move |_, _, _, _| {
let items_changed_handler = model.connect_items_changed(clone!(
#[weak(rename_to = imp)]
self,
move |_, _, _, _| {
imp.update_selected();
}));
}
));
self.string_model.set(model, vec![items_changed_handler]);
}

9
src/components/rows/loading_row.rs

@ -59,10 +59,13 @@ mod imp {
self.parent_constructed();
let obj = self.obj();
self.retry_button
.connect_clicked(clone!(@weak obj => move |_| {
self.retry_button.connect_clicked(clone!(
#[weak]
obj,
move |_| {
obj.emit_by_name::<()>("retry", &[]);
}));
}
));
}
}

10
src/components/rows/substring_entry_row.rs

@ -134,11 +134,13 @@ mod imp {
obj.init_delegate();
self.text
.buffer()
.connect_length_notify(clone!(@weak obj => move |_| {
self.text.buffer().connect_length_notify(clone!(
#[weak]
obj,
move |_| {
obj.notify_text_length();
}));
}
));
}
fn dispose(&self) {

15
src/components/rows/switch_loading_row.rs

@ -52,13 +52,16 @@ mod imp {
self.parent_constructed();
let obj = self.obj();
self.switch
.connect_active_notify(clone!(@weak obj => move |switch| {
obj.update_state(&[gtk::accessible::State::Checked(bool_to_accessible_tristate(
switch.is_active(),
))]);
self.switch.connect_active_notify(clone!(
#[weak]
obj,
move |switch| {
obj.update_state(&[gtk::accessible::State::Checked(
bool_to_accessible_tristate(switch.is_active()),
)]);
obj.notify_is_active();
}));
}
));
obj.update_state(&[gtk::accessible::State::Checked(
bool_to_accessible_tristate(self.switch.is_active()),
)]);

38
src/components/scale_revealer.rs

@ -44,26 +44,34 @@ mod imp {
self.parent_constructed();
let obj = self.obj();
let target = adw::CallbackAnimationTarget::new(clone!(@weak obj => move |_| {
obj.queue_draw();
}));
let target = adw::CallbackAnimationTarget::new(clone!(
#[weak]
obj,
move |_| {
obj.queue_draw();
}
));
let animation = adw::TimedAnimation::new(&*obj, 0.0, 1.0, ANIMATION_DURATION, target);
animation.set_easing(adw::Easing::EaseOutQuart);
animation.connect_done(clone!(@weak obj => move |_| {
let imp = obj.imp();
if !imp.reveal_child.get() {
if let Some(source_widget) = imp.source_widget.upgrade() {
// Show the original source widget now that the
// transition is over.
source_widget.set_opacity(1.0);
animation.connect_done(clone!(
#[weak]
obj,
move |_| {
let imp = obj.imp();
if !imp.reveal_child.get() {
if let Some(source_widget) = imp.source_widget.upgrade() {
// Show the original source widget now that the
// transition is over.
source_widget.set_opacity(1.0);
}
obj.set_visible(false);
}
obj.set_visible(false);
}
obj.emit_by_name::<()>("transition-done", &[]);
}));
obj.emit_by_name::<()>("transition-done", &[]);
}
));
self.animation.set(animation).unwrap();
obj.set_visible(false);

55
src/components/user_page.rs

@ -176,46 +176,65 @@ mod imp {
.build();
let bindings = vec![title_binding, avatar_binding];
let verified_handler = user.connect_verified_notify(clone!(@weak obj => move |_| {
obj.update_verified();
}));
let ignored_handler =
user.connect_is_ignored_notify(clone!(@weak obj => move |_| {
let verified_handler = user.connect_verified_notify(clone!(
#[weak]
obj,
move |_| {
obj.update_verified();
}
));
let ignored_handler = user.connect_is_ignored_notify(clone!(
#[weak]
obj,
move |_| {
obj.update_direct_chat();
obj.update_ignored();
}));
}
));
let mut handlers = vec![verified_handler, ignored_handler];
if let Some(member) = user.downcast_ref::<Member>() {
let room = member.room();
let permissions = room.permissions();
let permissions_handler =
permissions.connect_changed(clone!(@weak obj => move |_| {
let permissions_handler = permissions.connect_changed(clone!(
#[weak]
obj,
move |_| {
obj.update_room();
}));
}
));
self.permissions_handler.replace(Some(permissions_handler));
self.power_level_row.set_permissions(Some(permissions));
let room_display_name_handler =
room.connect_display_name_notify(clone!(@weak obj => move |_| {
let room_display_name_handler = room.connect_display_name_notify(clone!(
#[weak]
obj,
move |_| {
obj.update_room();
}));
}
));
self.room_display_name_handler
.replace(Some(room_display_name_handler));
let membership_handler =
member.connect_membership_notify(clone!(@weak obj => move |member| {
let membership_handler = member.connect_membership_notify(clone!(
#[weak]
obj,
move |member| {
if member.membership() == Membership::Leave {
obj.emit_by_name::<()>("close", &[]);
} else {
obj.update_room();
}
}));
let power_level_handler =
member.connect_power_level_notify(clone!(@weak obj => move |_| {
}
));
let power_level_handler = member.connect_power_level_notify(clone!(
#[weak]
obj,
move |_| {
obj.update_room();
}));
}
));
handlers.extend([membership_handler, power_level_handler]);
}

29
src/contrib/qr_code_scanner/camera/camera_paintable/linux.rs

@ -183,8 +183,8 @@ impl LinuxCameraPaintable {
gst::Element::link_many([&queue2, &videoconvert2, &sink]).unwrap();
let bus = pipeline.bus().unwrap();
let bus_guard = bus.add_watch_local(
clone!(@weak self as paintable => @default-return glib::ControlFlow::Break, move |_, msg| {
let bus_guard = bus
.add_watch_local(move |_, msg| {
if let gst::MessageView::Error(err) = msg.view() {
error!(
"Error from {:?}: {} ({:?})",
@ -194,9 +194,8 @@ impl LinuxCameraPaintable {
);
}
glib::ControlFlow::Continue
}),
)
.expect("Could not add bus watch");
})
.expect("Could not add bus watch");
let paintable = sink.property::<gdk::Paintable>("paintable");
@ -224,13 +223,21 @@ impl LinuxCameraPaintable {
fn set_sink_paintable(&self, paintable: gdk::Paintable) {
let imp = self.imp();
paintable.connect_invalidate_contents(clone!(@weak self as obj => move |_| {
obj.invalidate_contents();
}));
paintable.connect_invalidate_contents(clone!(
#[weak(rename_to = obj)]
self,
move |_| {
obj.invalidate_contents();
}
));
paintable.connect_invalidate_size(clone!(@weak self as obj => move |_| {
obj.invalidate_size();
}));
paintable.connect_invalidate_size(clone!(
#[weak(rename_to = obj)]
self,
move |_| {
obj.invalidate_size();
}
));
imp.sink_paintable.replace(Some(paintable));

22
src/contrib/qr_code_scanner/mod.rs

@ -90,12 +90,22 @@ impl QrCodeScanner {
imp.picture.set_paintable(Some(&paintable));
let callback = glib::clone!(@weak self as obj => @default-return None, move |args: &[glib::Value]| {
let code = args.get(1).unwrap().get::<QrVerificationDataBoxed>().unwrap();
obj.emit_by_name::<()>("code-detected", &[&code]);
None
});
let callback = glib::clone!(
#[weak(rename_to = obj)]
self,
#[upgrade_or]
None,
move |args: &[glib::Value]| {
let code = args
.get(1)
.unwrap()
.get::<QrVerificationDataBoxed>()
.unwrap();
obj.emit_by_name::<()>("code-detected", &[&code]);
None
}
);
let handler = paintable.connect_local("code-detected", false, callback);
imp.handler.replace(Some(handler));

13
src/identity_verification_view/accept_request_page.rs

@ -87,12 +87,13 @@ mod imp {
}
if let Some(verification) = &verification {
let display_name_handler =
verification
.user()
.connect_display_name_notify(clone!(@weak obj => move |_| {
obj.update_labels();
}));
let display_name_handler = verification.user().connect_display_name_notify(clone!(
#[weak]
obj,
move |_| {
obj.update_labels();
}
));
self.display_name_handler
.replace(Some(display_name_handler));
}

22
src/identity_verification_view/cancelled_page.rs

@ -84,19 +84,23 @@ mod imp {
self.verification.disconnect_signals();
if let Some(verification) = verification {
let display_name_handler =
verification
.user()
.connect_display_name_notify(clone!(@weak obj => move |_| {
obj.update_message();
}));
let display_name_handler = verification.user().connect_display_name_notify(clone!(
#[weak]
obj,
move |_| {
obj.update_message();
}
));
self.display_name_handler
.replace(Some(display_name_handler));
let cancel_info_changed_handler =
verification.connect_cancel_info_changed(clone!(@weak obj => move |_| {
let cancel_info_changed_handler = verification.connect_cancel_info_changed(clone!(
#[weak]
obj,
move |_| {
obj.update_message();
}));
}
));
self.verification
.set(&verification, vec![cancel_info_changed_handler]);

23
src/identity_verification_view/choose_method_page.rs

@ -104,19 +104,24 @@ mod imp {
self.verification.disconnect_signals();
if let Some(verification) = &verification {
let display_name_handler =
verification
.user()
.connect_display_name_notify(clone!(@weak obj => move |_| {
obj.update_page();
}));
let display_name_handler = verification.user().connect_display_name_notify(clone!(
#[weak]
obj,
move |_| {
obj.update_page();
}
));
self.display_name_handler
.replace(Some(display_name_handler));
let supported_methods_handler =
verification.connect_supported_methods_notify(clone!(@weak obj => move |_| {
obj.update_page();
}));
verification.connect_supported_methods_notify(clone!(
#[weak]
obj,
move |_| {
obj.update_page();
}
));
self.verification
.set(verification, vec![supported_methods_handler]);

13
src/identity_verification_view/completed_page.rs

@ -79,12 +79,13 @@ mod imp {
}
if let Some(verification) = &verification {
let display_name_handler =
verification
.user()
.connect_display_name_notify(clone!(@weak obj => move |_| {
obj.update_labels();
}));
let display_name_handler = verification.user().connect_display_name_notify(clone!(
#[weak]
obj,
move |_| {
obj.update_labels();
}
));
self.display_name_handler
.replace(Some(display_name_handler));
}

13
src/identity_verification_view/confirm_qr_code_page.rs

@ -85,12 +85,13 @@ mod imp {
}
if let Some(verification) = &verification {
let display_name_handler =
verification
.user()
.connect_display_name_notify(clone!(@weak obj => move |_| {
obj.update_labels();
}));
let display_name_handler = verification.user().connect_display_name_notify(clone!(
#[weak]
obj,
move |_| {
obj.update_labels();
}
));
self.display_name_handler
.replace(Some(display_name_handler));
}

25
src/identity_verification_view/mod.rs

@ -84,14 +84,14 @@ mod imp {
fn constructed(&self) {
self.parent_constructed();
self.main_stack.connect_transition_running_notify(
clone!(@weak self as imp => move |stack|
if !stack.is_transition_running() {
// Focus the default widget when the transition has ended.
imp.grab_focus();
}
),
);
self.main_stack.connect_transition_running_notify(clone!(
#[weak(rename_to = imp)]
self,
move |stack| if !stack.is_transition_running() {
// Focus the default widget when the transition has ended.
imp.grab_focus();
}
));
}
}
@ -130,10 +130,13 @@ mod imp {
self.verification.disconnect_signals();
if let Some(verification) = verification {
let state_handler =
verification.connect_state_notify(clone!(@weak obj => move |_| {
let state_handler = verification.connect_state_notify(clone!(
#[weak]
obj,
move |_| {
obj.update_view();
}));
}
));
verification.set_was_viewed(true);
self.verification.set(verification, vec![state_handler]);

22
src/identity_verification_view/no_supported_methods_page.rs

@ -85,19 +85,23 @@ mod imp {
self.verification.disconnect_signals();
if let Some(verification) = &verification {
let display_name_handler =
verification
.user()
.connect_display_name_notify(clone!(@weak obj => move |_| {
obj.update_page();
}));
let display_name_handler = verification.user().connect_display_name_notify(clone!(
#[weak]
obj,
move |_| {
obj.update_page();
}
));
self.display_name_handler
.replace(Some(display_name_handler));
let was_accepted_handler =
verification.connect_was_accepted_notify(clone!(@weak obj => move |_| {
let was_accepted_handler = verification.connect_was_accepted_notify(clone!(
#[weak]
obj,
move |_| {
obj.update_page();
}));
}
));
self.verification
.set(verification, vec![was_accepted_handler]);

13
src/identity_verification_view/qr_code_scanned_page.rs

@ -76,12 +76,13 @@ mod imp {
}
if let Some(verification) = &verification {
let display_name_handler =
verification
.user()
.connect_display_name_notify(clone!(@weak obj => move |_| {
obj.update_labels();
}));
let display_name_handler = verification.user().connect_display_name_notify(clone!(
#[weak]
obj,
move |_| {
obj.update_labels();
}
));
self.display_name_handler
.replace(Some(display_name_handler));
}

22
src/identity_verification_view/sas_page.rs

@ -94,20 +94,24 @@ mod imp {
self.verification.disconnect_signals();
if let Some(verification) = &verification {
let display_name_handler =
verification
.user()
.connect_display_name_notify(clone!(@weak obj => move |_| {
obj.update_labels();
}));
let display_name_handler = verification.user().connect_display_name_notify(clone!(
#[weak]
obj,
move |_| {
obj.update_labels();
}
));
self.display_name_handler
.replace(Some(display_name_handler));
let sas_data_changed_handler =
verification.connect_sas_data_changed(clone!(@weak obj => move |_| {
let sas_data_changed_handler = verification.connect_sas_data_changed(clone!(
#[weak]
obj,
move |_| {
obj.update_labels();
obj.fill_rows();
}));
}
));
self.verification
.set(verification, vec![sas_data_changed_handler]);

54
src/identity_verification_view/scan_qr_code_page.rs

@ -64,10 +64,13 @@ mod imp {
self.parent_constructed();
let obj = self.obj();
self.qr_code_scanner
.connect_code_detected(clone!(@weak obj => move |_, data| {
self.qr_code_scanner.connect_code_detected(clone!(
#[weak]
obj,
move |_, data| {
obj.code_detected(data);
}));
}
));
}
fn dispose(&self) {
@ -83,9 +86,13 @@ mod imp {
fn map(&self) {
self.parent_map();
spawn!(clone!(@weak self as imp => async move {
imp.qr_code_scanner.start().await;
}));
spawn!(clone!(
#[weak(rename_to = imp)]
self,
async move {
imp.qr_code_scanner.start().await;
}
));
}
fn unmap(&self) {
@ -114,19 +121,24 @@ mod imp {
self.verification.disconnect_signals();
if let Some(verification) = &verification {
let display_name_handler =
verification
.user()
.connect_display_name_notify(clone!(@weak obj => move |_| {
obj.update_labels();
}));
let display_name_handler = verification.user().connect_display_name_notify(clone!(
#[weak]
obj,
move |_| {
obj.update_labels();
}
));
self.display_name_handler
.replace(Some(display_name_handler));
let supported_methods_handler =
verification.connect_supported_methods_notify(clone!(@weak obj => move |_| {
obj.update_page();
}));
verification.connect_supported_methods_notify(clone!(
#[weak]
obj,
move |_| {
obj.update_page();
}
));
self.verification
.set(verification, vec![supported_methods_handler]);
@ -207,11 +219,15 @@ impl ScanQrCodePage {
return;
};
spawn!(clone!(@weak self as obj => async move {
if verification.qr_code_scanned(data).await.is_err() {
toast!(obj, gettext("Could not validate scanned QR Code"));
spawn!(clone!(
#[weak(rename_to = obj)]
self,
async move {
if verification.qr_code_scanned(data).await.is_err() {
toast!(obj, gettext("Could not validate scanned QR Code"));
}
}
}));
));
}
/// Switch to the screen to scan a QR Code.

13
src/identity_verification_view/wait_for_other_page.rs

@ -80,12 +80,13 @@ mod imp {
}
if let Some(verification) = &verification {
let display_name_handler =
verification
.user()
.connect_display_name_notify(clone!(@weak obj => move |_| {
obj.update_labels();
}));
let display_name_handler = verification.user().connect_display_name_notify(clone!(
#[weak]
obj,
move |_| {
obj.update_labels();
}
));
self.display_name_handler
.replace(Some(display_name_handler));
}

12
src/login/homeserver_page.rs

@ -78,10 +78,14 @@ mod imp {
self.login.disconnect_signals();
if let Some(login) = login {
let handler = login.connect_autodiscovery_notify(clone!(@weak obj => move |_| {
obj.update_next_state();
obj.update_text();
}));
let handler = login.connect_autodiscovery_notify(clone!(
#[weak]
obj,
move |_| {
obj.update_next_state();
obj.update_text();
}
));
self.login.set(login, vec![handler]);
}

7
src/login/idp_button.rs

@ -109,8 +109,11 @@ mod imp {
self.parent_constructed();
let obj = self.obj();
adw::StyleManager::default()
.connect_dark_notify(clone!(@weak obj => move |_| obj.update_icon()));
adw::StyleManager::default().connect_dark_notify(clone!(
#[weak]
obj,
move |_| obj.update_icon()
));
obj.update_icon();
obj.set_tooltip_text(Some(&gettext_f(

19
src/login/method_page.rs

@ -74,13 +74,20 @@ mod imp {
self.login.disconnect_signals();
if let Some(login) = login {
let domain_handler = login.connect_domain_notify(clone!(@weak obj => move |_| {
obj.update_domain_name();
}));
let login_types_handler =
login.connect_login_types_notify(clone!(@weak obj => move |_| {
let domain_handler = login.connect_domain_notify(clone!(
#[weak]
obj,
move |_| {
obj.update_domain_name();
}
));
let login_types_handler = login.connect_login_types_notify(clone!(
#[weak]
obj,
move |_| {
obj.update_sso();
}));
}
));
self.login
.set(login, vec![domain_handler, login_types_handler]);

39
src/login/mod.rs

@ -137,15 +137,22 @@ mod imp {
self.parent_constructed();
let monitor = gio::NetworkMonitor::default();
monitor.connect_network_changed(clone!(@weak obj => move |_, available| {
obj.action_set_enabled("login.sso", available);
}));
monitor.connect_network_changed(clone!(
#[weak]
obj,
move |_, available| {
obj.action_set_enabled("login.sso", available);
}
));
obj.action_set_enabled("login.sso", monitor.is_network_available());
self.navigation
.connect_visible_page_notify(clone!(@weak obj => move |_| {
self.navigation.connect_visible_page_notify(clone!(
#[weak]
obj,
move |_| {
obj.visible_page_changed();
}));
}
));
}
fn dispose(&self) {
@ -368,9 +375,13 @@ impl Login {
.navigation
.push_by_tag(LoginPage::Method.as_ref());
} else {
spawn!(clone!(@weak self as obj => async move {
obj.login_with_sso(None).await;
}));
spawn!(clone!(
#[weak(rename_to = obj)]
self,
async move {
obj.login_with_sso(None).await;
}
));
}
}
@ -443,9 +454,13 @@ impl Login {
let imp = self.imp();
let setup_view = SessionSetupView::new(&session);
setup_view.connect_completed(clone!(@weak imp => move |_| {
imp.navigation.push_by_tag(LoginPage::Completed.as_ref());
}));
setup_view.connect_completed(clone!(
#[weak]
imp,
move |_| {
imp.navigation.push_by_tag(LoginPage::Completed.as_ref());
}
));
imp.navigation.push(&setup_view);
self.drop_client();

71
src/login/session_setup_view.rs

@ -122,13 +122,19 @@ mod imp {
.expect("Session should still have a strong reference");
let crypto_identity_view = CryptoIdentitySetupView::new(&session);
crypto_identity_view.connect_completed(clone!(@weak self as imp => move |_, next| {
match next {
CryptoIdentitySetupNextStep::None => imp.obj().emit_completed(),
CryptoIdentitySetupNextStep::EnableRecovery => imp.check_recovery(true),
CryptoIdentitySetupNextStep::CompleteRecovery => imp.check_recovery(false),
crypto_identity_view.connect_completed(clone!(
#[weak(rename_to = imp)]
self,
move |_, next| {
match next {
CryptoIdentitySetupNextStep::None => imp.obj().emit_completed(),
CryptoIdentitySetupNextStep::EnableRecovery => imp.check_recovery(true),
CryptoIdentitySetupNextStep::CompleteRecovery => {
imp.check_recovery(false)
}
}
}
}));
));
crypto_identity_view
})
@ -144,9 +150,13 @@ mod imp {
let recovery_view = CryptoRecoverySetupView::new(&session);
let obj = self.obj();
recovery_view.connect_completed(clone!(@weak obj => move |_| {
obj.emit_completed();
}));
recovery_view.connect_completed(clone!(
#[weak]
obj,
move |_| {
obj.emit_completed();
}
));
recovery_view
})
@ -156,11 +166,15 @@ mod imp {
fn set_session(&self, session: &Session) {
self.session.set(Some(session));
let ready_handler = session.connect_ready(clone!(@weak self as imp => move |_| {
spawn!(async move {
imp.load().await;
});
}));
let ready_handler = session.connect_ready(clone!(
#[weak(rename_to = imp)]
self,
move |_| {
spawn!(async move {
imp.load().await;
});
}
));
self.session_handler.replace(Some(ready_handler));
}
@ -195,11 +209,13 @@ mod imp {
// Wait if we don't know the crypto identity state.
let crypto_identity_state = session.crypto_identity_state();
if crypto_identity_state == CryptoIdentityState::Unknown {
let handler = session.connect_crypto_identity_state_notify(
clone!(@weak self as imp => move |_| {
let handler = session.connect_crypto_identity_state_notify(clone!(
#[weak(rename_to = imp)]
self,
move |_| {
imp.check_session_setup();
}),
);
}
));
self.session_handler.replace(Some(handler));
return;
}
@ -207,11 +223,13 @@ mod imp {
// Wait if we don't know the verification state.
let verification_state = session.verification_state();
if verification_state == SessionVerificationState::Unknown {
let handler = session.connect_verification_state_notify(
clone!(@weak self as imp => move |_| {
let handler = session.connect_verification_state_notify(clone!(
#[weak(rename_to = imp)]
self,
move |_| {
imp.check_session_setup();
}),
);
}
));
self.session_handler.replace(Some(handler));
return;
}
@ -219,10 +237,13 @@ mod imp {
// Wait if we don't know the recovery state.
let recovery_state = session.recovery_state();
if recovery_state == RecoveryState::Unknown {
let handler =
session.connect_recovery_state_notify(clone!(@weak self as imp => move |_| {
let handler = session.connect_recovery_state_notify(clone!(
#[weak(rename_to = imp)]
self,
move |_| {
imp.check_session_setup();
}));
}
));
self.session_handler.replace(Some(handler));
return;
}

10
src/session/model/ignored_users.rs

@ -104,9 +104,13 @@ mod imp {
let abort_handle = spawn_tokio!(fut).abort_handle();
self.abort_handle.replace(Some(abort_handle));
spawn!(clone!(@weak self as imp => async move {
imp.load_list().await;
}));
spawn!(clone!(
#[weak(rename_to = imp)]
self,
async move {
imp.load_list().await;
}
));
}
/// Load the list from the store and update it.

40
src/session/model/notifications/notifications_settings.rs

@ -139,9 +139,13 @@ mod imp {
self.session.set(session);
obj.notify_session();
spawn!(clone!(@weak obj => async move {
obj.init_api().await;
}));
spawn!(clone!(
#[weak]
obj,
async move {
obj.init_api().await;
}
));
}
/// Set whether notifications are enabled for this session.
@ -190,11 +194,15 @@ impl NotificationsSettings {
if session.state() != SessionState::Ready {
self.imp().api.take();
session.connect_ready(clone!(@weak self as obj => move |_| {
spawn!(async move {
obj.init_api().await;
});
}));
session.connect_ready(clone!(
#[weak(rename_to = obj)]
self,
move |_| {
spawn!(async move {
obj.init_api().await;
});
}
));
return;
}
@ -227,10 +235,22 @@ impl NotificationsSettings {
}
});
spawn!(clone!(@weak self as obj => async move { obj.update().await; }));
spawn!(clone!(
#[weak(rename_to = obj)]
self,
async move {
obj.update().await;
}
));
while let Some(()) = receiver.next().await {
spawn!(clone!(@weak self as obj => async move { obj.update().await; }));
spawn!(clone!(
#[weak(rename_to = obj)]
self,
async move {
obj.update().await;
}
));
}
}

10
src/session/model/remote_room.rs

@ -188,9 +188,13 @@ impl RemoteRoom {
imp.uri.set(uri).unwrap();
imp.update_display_name();
spawn!(clone!(@weak obj => async move {
obj.load().await;
}));
spawn!(clone!(
#[weak]
obj,
async move {
obj.load().await;
}
));
obj
}

21
src/session/model/room/join_rule.rs

@ -129,11 +129,13 @@ mod imp {
return;
};
let own_membership_handler =
room.own_member()
.connect_membership_notify(clone!(@weak self as imp => move |_| {
imp.update_we_can_join();
}));
let own_membership_handler = room.own_member().connect_membership_notify(clone!(
#[weak(rename_to = imp)]
self,
move |_| {
imp.update_we_can_join();
}
));
self.own_membership_handler
.replace(Some(own_membership_handler));
@ -254,10 +256,13 @@ mod imp {
RemoteRoom::new(&session, room_id.into()).upcast()
};
let display_name_handler =
room.connect_display_name_notify(clone!(@weak self as imp => move |_| {
let display_name_handler = room.connect_display_name_notify(clone!(
#[weak(rename_to = imp)]
self,
move |_| {
imp.update_display_name();
}));
}
));
self.membership_room.set(room, vec![display_name_handler])
}

34
src/session/model/room/member.rs

@ -103,16 +103,22 @@ mod imp {
fn set_room(&self, room: Room) {
let obj = self.obj();
let default_pl_handler = room.permissions().connect_default_power_level_notify(
clone!(@weak obj => move |_| {
obj.update_role();
}),
);
let mute_pl_handler =
room.permissions()
.connect_mute_power_level_notify(clone!(@weak obj => move |_| {
let default_pl_handler = room
.permissions()
.connect_default_power_level_notify(clone!(
#[weak]
obj,
move |_| {
obj.update_role();
}));
}
));
let mute_pl_handler = room.permissions().connect_mute_power_level_notify(clone!(
#[weak]
obj,
move |_| {
obj.update_role();
}
));
self.power_level_handlers
.replace(vec![default_pl_handler, mute_pl_handler]);
@ -201,9 +207,13 @@ impl Member {
/// Update this member with the SDK's data.
pub fn update(&self) {
spawn!(clone!(@weak self as obj => async move {
obj.update_inner().await;
}));
spawn!(clone!(
#[weak(rename_to = obj)]
self,
async move {
obj.update_inner().await;
}
));
}
async fn update_inner(&self) {

20
src/session/model/room/member_list.rs

@ -79,9 +79,13 @@ mod imp {
spawn!(
glib::Priority::LOW,
clone!(@weak obj => async move {
obj.load().await;
})
clone!(
#[weak]
obj,
async move {
obj.load().await;
}
)
);
}
}
@ -115,9 +119,13 @@ impl MemberList {
pub fn reload(&self) {
self.set_state(LoadingState::Initial);
spawn!(clone!(@weak self as obj => async move {
obj.load().await;
}));
spawn!(clone!(
#[weak(rename_to = obj)]
self,
async move {
obj.load().await;
}
));
}
/// Load this list.

225
src/session/model/room/mod.rs

@ -344,15 +344,21 @@ mod imp {
});
if let Some(verification) = &verification {
let state_handler =
verification.connect_is_finished_notify(clone!(@weak self as imp => move |_| {
let state_handler = verification.connect_is_finished_notify(clone!(
#[weak(rename_to = imp)]
self,
move |_| {
imp.set_verification(None);
}));
}
));
let dismiss_handler =
verification.connect_dismiss(clone!(@weak self as imp => move |_| {
let dismiss_handler = verification.connect_dismiss(clone!(
#[weak(rename_to = imp)]
self,
move |_| {
imp.set_verification(None);
}));
}
));
self.verification
.set(verification, vec![state_handler, dismiss_handler]);
@ -429,51 +435,79 @@ impl Room {
spawn!(
glib::Priority::DEFAULT_IDLE,
clone!(@weak self as obj => async move {
obj.load_display_name().await;
})
clone!(
#[weak(rename_to = obj)]
self,
async move {
obj.load_display_name().await;
}
)
);
spawn!(
glib::Priority::DEFAULT_IDLE,
clone!(@weak self as obj => async move {
obj.load_own_member().await;
})
clone!(
#[weak(rename_to = obj)]
self,
async move {
obj.load_own_member().await;
}
)
);
spawn!(
glib::Priority::DEFAULT_IDLE,
clone!(@weak self as obj => async move {
obj.load_is_direct().await;
})
clone!(
#[weak(rename_to = obj)]
self,
async move {
obj.load_is_direct().await;
}
)
);
spawn!(
glib::Priority::DEFAULT_IDLE,
clone!(@weak self as obj => async move {
obj.watch_room_info().await;
})
clone!(
#[weak(rename_to = obj)]
self,
async move {
obj.watch_room_info().await;
}
)
);
spawn!(
glib::Priority::DEFAULT_IDLE,
clone!(@weak self as obj => async move {
obj.load_inviter().await;
})
clone!(
#[weak(rename_to = obj)]
self,
async move {
obj.load_inviter().await;
}
)
);
spawn!(
glib::Priority::DEFAULT_IDLE,
clone!(@weak self as obj => async move {
obj.imp().permissions.init(&obj).await;
})
clone!(
#[weak(rename_to = obj)]
self,
async move {
obj.imp().permissions.init(&obj).await;
}
)
);
spawn!(
glib::Priority::DEFAULT_IDLE,
clone!(@weak self as obj => async move {
obj.imp().join_rule.init(&obj).await;
})
clone!(
#[weak(rename_to = obj)]
self,
async move {
obj.imp().join_rule.init(&obj).await;
}
)
);
}
@ -481,21 +515,27 @@ impl Room {
let timeline = Timeline::new(self);
self.imp().timeline.set(timeline.clone()).unwrap();
timeline
.sdk_items()
.connect_items_changed(clone!(@weak self as obj => move |_, _, _, _| {
timeline.sdk_items().connect_items_changed(clone!(
#[weak(rename_to = obj)]
self,
move |_, _, _, _| {
spawn!(async move {
obj.update_is_read().await;
});
}));
}
));
if !matches!(self.category(), RoomType::Left | RoomType::Outdated) {
// Load the room history when idle.
spawn!(
glib::source::Priority::LOW,
clone!(@weak self as obj => async move {
obj.timeline().load().await;
})
clone!(
#[weak(rename_to = obj)]
self,
async move {
obj.timeline().load().await;
}
)
);
}
}
@ -524,9 +564,13 @@ impl Room {
self.imp().is_direct.set(is_direct);
self.notify_is_direct();
spawn!(clone!(@weak self as obj => async move {
obj.load_direct_member().await;
}));
spawn!(clone!(
#[weak(rename_to = obj)]
self,
async move {
obj.load_direct_member().await;
}
));
}
/// Load whether the room is direct or not.
@ -913,19 +957,23 @@ impl Room {
spawn!(
glib::Priority::DEFAULT_IDLE,
clone!(@weak self as obj => async move {
let mut category = RoomType::Normal;
if let Ok(Some(tags)) = tags.await.unwrap() {
if tags.contains_key(&TagName::Favorite) {
category = RoomType::Favorite;
} else if tags.contains_key(&TagName::LowPriority) {
category = RoomType::LowPriority;
clone!(
#[weak(rename_to = obj)]
self,
async move {
let mut category = RoomType::Normal;
if let Ok(Some(tags)) = tags.await.unwrap() {
if tags.contains_key(&TagName::Favorite) {
category = RoomType::Favorite;
} else if tags.contains_key(&TagName::LowPriority) {
category = RoomType::LowPriority;
}
}
}
obj.set_category_internal(category);
})
obj.set_category_internal(category);
}
)
);
}
}
@ -1034,9 +1082,13 @@ impl Room {
for (_event_id, receipts) in content.iter() {
if let Some(users) = receipts.get(&ReceiptType::Read) {
if users.contains_key(own_user_id) {
spawn!(clone!(@weak self as obj => async move {
obj.update_is_read().await;
}));
spawn!(clone!(
#[weak(rename_to = obj)]
self,
async move {
obj.update_is_read().await;
}
));
}
}
}
@ -1239,11 +1291,15 @@ impl Room {
let inviter = Member::new(self, inviter_id);
inviter.update_from_room_member(&inviter_member);
inviter.upcast_ref::<User>().connect_is_ignored_notify(
clone!(@weak self as obj => move |_| {
obj.load_category();
}),
);
inviter
.upcast_ref::<User>()
.connect_is_ignored_notify(clone!(
#[weak(rename_to = obj)]
self,
move |_| {
obj.load_category();
}
));
self.imp().inviter.replace(Some(inviter));
@ -1283,19 +1339,27 @@ impl Room {
}
// It might change the direct member.
spawn!(clone!(@weak self as obj => async move {
obj.load_direct_member().await;
obj.load_display_name().await;
}));
spawn!(clone!(
#[weak(rename_to = obj)]
self,
async move {
obj.load_direct_member().await;
obj.load_display_name().await;
}
));
}
AnySyncStateEvent::RoomAvatar(SyncStateEvent::Original(_)) => {
self.update_avatar();
}
AnySyncStateEvent::RoomName(_) => {
self.notify_name();
spawn!(clone!(@weak self as obj => async move {
obj.load_display_name().await;
}));
spawn!(clone!(
#[weak(rename_to = obj)]
self,
async move {
obj.load_display_name().await;
}
));
}
AnySyncStateEvent::RoomTopic(_) => {
self.notify_topic();
@ -1394,15 +1458,12 @@ impl Room {
let matrix_room = matrix_room.clone();
let handle = spawn_tokio!(async move { matrix_room.typing_notice(is_typing).await });
spawn!(
glib::Priority::DEFAULT_IDLE,
clone!(@weak self as obj => async move {
match handle.await.unwrap() {
Ok(_) => {},
Err(error) => error!("Could not send typing notification: {error}"),
};
})
);
spawn!(glib::Priority::DEFAULT_IDLE, async move {
match handle.await.unwrap() {
Ok(_) => {}
Err(error) => error!("Could not send typing notification: {error}"),
};
});
}
pub async fn accept_invite(&self) -> MatrixResult<()> {
@ -1466,9 +1527,13 @@ impl Room {
}
self.load_category();
spawn!(clone!(@weak self as obj => async move {
obj.load_inviter().await;
}));
spawn!(clone!(
#[weak(rename_to = obj)]
self,
async move {
obj.load_inviter().await;
}
));
}
pub fn handle_left_update(&self, update: LeftRoomUpdate) {
@ -1816,9 +1881,13 @@ impl Room {
spawn!(
glib::Priority::DEFAULT_IDLE,
clone!(@weak self as obj => async move {
obj.load_is_encrypted().await;
})
clone!(
#[weak(rename_to = obj)]
self,
async move {
obj.load_is_encrypted().await;
}
)
);
}

10
src/session/model/room/permissions.rs

@ -178,9 +178,13 @@ mod imp {
impl Permissions {
/// Initialize the room.
pub(super) fn init_own_member(&self, own_member: Member) {
own_member.connect_membership_notify(clone!(@weak self as imp => move |_| {
imp.update_is_joined();
}));
own_member.connect_membership_notify(clone!(
#[weak(rename_to = imp)]
self,
move |_| {
imp.update_is_joined();
}
));
self.update_is_joined();
}

20
src/session/model/room/timeline/mod.rs

@ -156,18 +156,24 @@ mod imp {
self.room.set(room.as_ref());
if let Some(room) = room {
room.typing_list().connect_items_changed(
clone!(@weak obj => move |list, _, _, _| {
room.typing_list().connect_items_changed(clone!(
#[weak]
obj,
move |list, _, _, _| {
if !list.is_empty() {
obj.add_typing_row();
}
}),
);
}
));
}
spawn!(clone!(@weak obj => async move {
obj.setup_timeline().await;
}));
spawn!(clone!(
#[weak]
obj,
async move {
obj.setup_timeline().await;
}
));
}
/// Whether the timeline is empty.

10
src/session/model/room_list/mod.rs

@ -251,9 +251,13 @@ impl RoomList {
let position = list.len().saturating_sub(added);
for (_room_id, room) in list.iter().skip(position) {
room.connect_room_forgotten(clone!(@weak self as obj => move |room| {
obj.remove(room.room_id());
}));
room.connect_room_forgotten(clone!(
#[weak(rename_to = obj)]
self,
move |room| {
obj.remove(room.room_id());
}
));
}
let mut to_remove = Vec::new();

50
src/session/model/session.rs

@ -206,11 +206,15 @@ mod imp {
self.user_sessions.init(&obj, obj.user_id().clone());
let monitor = gio::NetworkMonitor::default();
let handler_id = monitor.connect_network_changed(clone!(@weak obj => move |_, _| {
spawn!(async move {
obj.update_offline().await;
});
}));
let handler_id = monitor.connect_network_changed(clone!(
#[weak]
obj,
move |_, _| {
spawn!(async move {
obj.update_offline().await;
});
}
));
self.offline_handler_id.replace(Some(handler_id));
}
@ -365,12 +369,16 @@ impl Session {
pub async fn prepare(&self) {
spawn!(
glib::Priority::LOW,
clone!(@weak self as obj => async move {
// First, load the profile from the cache, it will be quicker.
obj.init_user_profile().await;
// Then, check if the profile changed.
obj.update_user_profile().await;
})
clone!(
#[weak(rename_to = obj)]
self,
async move {
// First, load the profile from the cache, it will be quicker.
obj.init_user_profile().await;
// Then, check if the profile changed.
obj.update_user_profile().await;
}
)
);
self.update_offline().await;
@ -380,9 +388,13 @@ impl Session {
self.init_verification_state();
self.init_recovery_state();
spawn!(clone!(@weak self as obj => async move {
obj.init_crypto_identity_state().await;
}));
spawn!(clone!(
#[weak(rename_to = obj)]
self,
async move {
obj.init_crypto_identity_state().await;
}
));
self.set_state(SessionState::InitialSync);
self.sync();
@ -891,9 +903,13 @@ impl Session {
spawn!(
glib::Priority::LOW,
clone!(@strong self as obj => async move {
obj.clean_up().await;
})
clone!(
#[strong(rename_to = obj)]
self,
async move {
obj.clean_up().await;
}
)
);
}

12
src/session/model/sidebar_data/category/mod.rs

@ -108,10 +108,14 @@ mod imp {
model
};
model.connect_items_changed(clone!(@weak obj => move |model, pos, removed, added| {
obj.items_changed(pos, removed, added);
obj.imp().set_empty(model.n_items() == 0);
}));
model.connect_items_changed(clone!(
#[weak]
obj,
move |model, pos, removed, added| {
obj.items_changed(pos, removed, added);
obj.imp().set_empty(model.n_items() == 0);
}
));
self.set_empty(model.n_items() == 0);
self.model.set(model).unwrap();

10
src/session/model/sidebar_data/item_list.rs

@ -102,9 +102,13 @@ mod imp {
for (pos, item) in list.iter().enumerate() {
if let Some(category) = item.item.downcast_ref::<Category>() {
category.connect_empty_notify(clone!(@weak self as imp => move |_| {
imp.update_item_at(pos);
}));
category.connect_empty_notify(clone!(
#[weak(rename_to = imp)]
self,
move |_| {
imp.update_item_at(pos);
}
));
}
self.update_item_at(pos);
}

45
src/session/model/sidebar_data/list_model.rs

@ -60,29 +60,38 @@ mod imp {
.build();
// When a verification is replaced, select the replacement automatically.
self.unfiltered_selection_model.connect_selected_item_notify(
clone!(@weak self as imp => move |selection_model| {
imp.selected_item.disconnect_signals();
if let Some(item) = &selection_model.selected_item() {
if let Some(verification) = item.downcast_ref::<IdentityVerification>() {
let verification_handler = verification.connect_replaced(
clone!(@weak selection_model => move |_, new_verification| {
selection_model.set_selected_item(Some(new_verification.clone()));
}),
);
imp.selected_item.set(item, vec![verification_handler]);
self.unfiltered_selection_model
.connect_selected_item_notify(clone!(
#[weak(rename_to = imp)]
self,
move |selection_model| {
imp.selected_item.disconnect_signals();
if let Some(item) = &selection_model.selected_item() {
if let Some(verification) = item.downcast_ref::<IdentityVerification>()
{
let verification_handler = verification.connect_replaced(clone!(
#[weak]
selection_model,
move |_, new_verification| {
selection_model
.set_selected_item(Some(new_verification.clone()));
}
));
imp.selected_item.set(item, vec![verification_handler]);
}
}
}
}),
);
));
// Switch between the filtered and unfiltered list models.
self.string_filter.connect_search_notify(
clone!(@weak self as imp => move |string_filter| {
self.string_filter.connect_search_notify(clone!(
#[weak(rename_to = imp)]
self,
move |string_filter| {
imp.set_is_filtered(string_filter.search().filter(|s| !s.is_empty()).is_some());
}),
);
}
));
}
}

9
src/session/model/sidebar_data/selection.rs

@ -93,10 +93,13 @@ mod imp {
self.model.disconnect_signals();
if let Some(model) = model {
let items_changed_handler =
model.connect_items_changed(clone!(@weak obj => move |m, p, r, a| {
let items_changed_handler = model.connect_items_changed(clone!(
#[weak]
obj,
move |m, p, r, a| {
obj.items_changed_cb(m, p, r, a);
}));
}
));
obj.items_changed_cb(&model, 0, n_items_before, model.n_items());

30
src/session/model/user.rs

@ -109,8 +109,10 @@ mod imp {
self.is_own_user.set(*session.user_id() == user_id);
let ignored_users = session.ignored_users();
let ignored_handler = ignored_users.connect_items_changed(
clone!(@weak self as imp => move |ignored_users, _, _, _| {
let ignored_handler = ignored_users.connect_items_changed(clone!(
#[weak(rename_to = imp)]
self,
move |ignored_users, _, _, _| {
let user_id = imp.user_id.get().unwrap();
let is_ignored = ignored_users.contains(user_id);
@ -118,8 +120,8 @@ mod imp {
imp.is_ignored.set(is_ignored);
imp.obj().notify_is_ignored();
}
}),
);
}
));
self.is_ignored.set(ignored_users.contains(&user_id));
self.ignored_handler.replace(Some(ignored_handler));
@ -170,16 +172,20 @@ impl User {
/// Load whether this user is verified.
fn init_is_verified(&self) {
spawn!(clone!(@weak self as obj => async move {
let verified = obj.crypto_identity().await.is_some_and(|i| i.is_verified());
spawn!(clone!(
#[weak(rename_to = obj)]
self,
async move {
let verified = obj.crypto_identity().await.is_some_and(|i| i.is_verified());
if verified == obj.verified() {
return;
}
if verified == obj.verified() {
return;
obj.imp().verified.set(verified);
obj.notify_verified();
}
obj.imp().verified.set(verified);
obj.notify_verified();
}));
));
}
/// The existing direct chat with this user, if any.

22
src/session/model/user_sessions_list/mod.rs

@ -127,12 +127,22 @@ impl UserSessionsList {
imp.session.set(Some(session));
imp.user_id.set(user_id).unwrap();
spawn!(clone!(@weak self as obj => async move {
obj.load().await;
}));
spawn!(clone!(@weak self as obj, @weak session => async move {
obj.init_sessions_watch(session.client()).await;
}));
spawn!(clone!(
#[weak(rename_to = obj)]
self,
async move {
obj.load().await;
}
));
spawn!(clone!(
#[weak(rename_to = obj)]
self,
#[weak]
session,
async move {
obj.init_sessions_watch(session.client()).await;
}
));
}
/// Start listening to changes in the user sessions.

38
src/session/model/verification/identity_verification.rs

@ -259,12 +259,16 @@ mod imp {
if matches!(request.state(), VerificationRequestState::Requested { .. }) {
let source_id = glib::timeout_add_local_once(
REQUEST_RECEIVED_TIMEOUT,
clone!(@weak self as imp => move || {
imp.received_timeout_source.take();
imp.set_state(VerificationState::Dismissed);
imp.obj().dismiss();
}),
clone!(
#[weak(rename_to = imp)]
self,
move || {
imp.received_timeout_source.take();
imp.set_state(VerificationState::Dismissed);
imp.obj().dismiss();
}
),
);
self.received_timeout_source.replace(Some(source_id));
}
@ -280,10 +284,13 @@ mod imp {
// to keep track of their name since it's used as the display name.
if user.is::<Member>() {
let obj = self.obj();
let display_name_handler =
user.connect_display_name_notify(clone!(@weak obj => move |_| {
let display_name_handler = user.connect_display_name_notify(clone!(
#[weak]
obj,
move |_| {
obj.notify_display_name();
}));
}
));
handlers.push(display_name_handler);
}
@ -297,18 +304,21 @@ mod imp {
return;
};
let handler = room.own_member().connect_membership_notify(
clone!(@weak self as imp => move |own_member| {
let handler = room.own_member().connect_membership_notify(clone!(
#[weak(rename_to = imp)]
self,
move |own_member| {
if matches!(own_member.membership(), Membership::Leave | Membership::Ban) {
// If the user is not in the room anymore, nothing can be done with this verification.
// If the user is not in the room anymore, nothing can be done with this
// verification.
imp.set_state(VerificationState::RoomLeft);
if let Some(handler) = imp.membership_handler.take() {
own_member.disconnect(handler);
}
}
}),
);
}
));
self.membership_handler.replace(Some(handler));
self.room.set(Some(room));

10
src/session/model/verification/verification_list.rs

@ -268,9 +268,13 @@ impl VerificationList {
return;
}
verification.connect_remove_from_list(clone!(@weak self as obj => move |verification| {
obj.remove(&verification.key());
}));
verification.connect_remove_from_list(clone!(
#[weak(rename_to = obj)]
self,
move |verification| {
obj.remove(&verification.key());
}
));
let (pos, _) = imp.list.borrow_mut().insert_full(key, verification);

21
src/session/view/account_settings/general_page/change_password_subpage.rs

@ -82,15 +82,22 @@ mod imp {
self.password_progress
.add_offset_value(gtk::LEVEL_BAR_OFFSET_FULL, 5.0);
self.password.connect_changed(clone!(@weak obj => move|_| {
obj.validate_password();
obj.validate_password_confirmation();
}));
self.password.connect_changed(clone!(
#[weak]
obj,
move |_| {
obj.validate_password();
obj.validate_password_confirmation();
}
));
self.confirm_password
.connect_changed(clone!(@weak obj => move|_| {
self.confirm_password.connect_changed(clone!(
#[weak]
obj,
move |_| {
obj.validate_password_confirmation();
}));
}
));
}
}

32
src/session/view/account_settings/general_page/deactivate_account_subpage.rs

@ -55,22 +55,32 @@ mod imp {
self.parent_constructed();
let obj = self.obj();
self.confirmation
.connect_entry_activated(clone!(@weak obj => move |_| {
self.confirmation.connect_entry_activated(clone!(
#[weak]
obj,
move |_| {
spawn!(async move {
obj.deactivate_account().await;
});
}));
self.confirmation
.connect_changed(clone!(@weak obj => move|_| {
}
));
self.confirmation.connect_changed(clone!(
#[weak]
obj,
move |_| {
obj.update_button();
}));
}
));
self.button.connect_clicked(clone!(@weak obj => move |_| {
spawn!(async move {
obj.deactivate_account().await;
});
}));
self.button.connect_clicked(clone!(
#[weak]
obj,
move |_| {
spawn!(async move {
obj.deactivate_account().await;
});
}
));
}
}

35
src/session/view/account_settings/general_page/mod.rs

@ -116,25 +116,38 @@ mod imp {
self.session_id.set_subtitle(session.device_id().as_str());
let user = session.user();
let avatar_uri_handler = user.avatar_data().image().unwrap().connect_uri_notify(
clone!(@weak obj => move |avatar_image| {
obj.user_avatar_changed(avatar_image.uri());
}),
);
let avatar_uri_handler =
user.avatar_data()
.image()
.unwrap()
.connect_uri_notify(clone!(
#[weak]
obj,
move |avatar_image| {
obj.user_avatar_changed(avatar_image.uri());
}
));
self.avatar_uri_handler.replace(Some(avatar_uri_handler));
let display_name_handler =
user.connect_display_name_notify(clone!(@weak obj => move |user| {
let display_name_handler = user.connect_display_name_notify(clone!(
#[weak]
obj,
move |user| {
obj.user_display_name_changed(user.display_name());
}));
}
));
self.display_name_handler
.replace(Some(display_name_handler));
spawn!(
glib::Priority::LOW,
clone!(@weak self as imp => async move {
imp.load_can_change_password().await;
})
clone!(
#[weak(rename_to = imp)]
self,
async move {
imp.load_can_change_password().await;
}
)
);
}

50
src/session/view/account_settings/mod.rs

@ -120,9 +120,13 @@ mod imp {
self.session.disconnect_signals();
if let Some(session) = session {
let logged_out_handler = session.connect_logged_out(clone!(@weak obj => move |_| {
obj.close();
}));
let logged_out_handler = session.connect_logged_out(clone!(
#[weak]
obj,
move |_| {
obj.close();
}
));
self.session.set(&session, vec![logged_out_handler]);
}
@ -163,33 +167,49 @@ impl AccountSettings {
}
AccountSettingsSubpage::CryptoIdentitySetup => {
let view = CryptoIdentitySetupView::new(&session);
view.connect_completed(clone!(@weak self as obj => move |_, _| {
obj.pop_subpage();
}));
view.connect_completed(clone!(
#[weak(rename_to = obj)]
self,
move |_, _| {
obj.pop_subpage();
}
));
let page = adw::NavigationPage::builder()
.tag(AccountSettingsSubpage::CryptoIdentitySetup.as_ref())
.child(&view)
.build();
page.connect_shown(clone!(@weak view => move |_| {
view.grab_focus();
}));
page.connect_shown(clone!(
#[weak]
view,
move |_| {
view.grab_focus();
}
));
page
}
AccountSettingsSubpage::RecoverySetup => {
let view = CryptoRecoverySetupView::new(&session);
view.connect_completed(clone!(@weak self as obj => move |_| {
obj.pop_subpage();
}));
view.connect_completed(clone!(
#[weak(rename_to = obj)]
self,
move |_| {
obj.pop_subpage();
}
));
let page = adw::NavigationPage::builder()
.tag(AccountSettingsSubpage::RecoverySetup.as_ref())
.child(&view)
.build();
page.connect_shown(clone!(@weak view => move |_| {
view.grab_focus();
}));
page.connect_shown(clone!(
#[weak]
view,
move |_| {
view.grab_focus();
}
));
page
}

93
src/session/view/account_settings/notifications_page.rs

@ -87,10 +87,13 @@ mod imp {
self.parent_constructed();
let obj = self.obj();
self.keywords_add_row
.connect_changed(clone!(@weak obj => move |_| {
self.keywords_add_row.connect_changed(clone!(
#[weak]
obj,
move |_| {
obj.update_keywords();
}));
}
));
}
}
@ -111,18 +114,27 @@ mod imp {
self.notifications_settings.disconnect_signals();
if let Some(settings) = notifications_settings {
let account_enabled_handler =
settings.connect_account_enabled_notify(clone!(@weak obj => move |_| {
let account_enabled_handler = settings.connect_account_enabled_notify(clone!(
#[weak]
obj,
move |_| {
obj.update_account();
}));
let session_enabled_handler =
settings.connect_session_enabled_notify(clone!(@weak obj => move |_| {
}
));
let session_enabled_handler = settings.connect_session_enabled_notify(clone!(
#[weak]
obj,
move |_| {
obj.update_session();
}));
let global_setting_handler =
settings.connect_global_setting_notify(clone!(@weak obj => move |_| {
}
));
let global_setting_handler = settings.connect_global_setting_notify(clone!(
#[weak]
obj,
move |_| {
obj.update_global();
}));
}
));
self.notifications_settings.set(
settings,
@ -143,12 +155,24 @@ mod imp {
let flattened_list = gtk::FlattenListModel::new(Some(all_items));
self.keywords.bind_model(
Some(&flattened_list),
clone!(@weak obj => @default-return { adw::ActionRow::new().upcast() }, move |item| obj.create_keyword_row(item)),
clone!(
#[weak]
obj,
#[upgrade_or_else]
|| { adw::ActionRow::new().upcast() },
move |item| obj.create_keyword_row(item)
),
);
} else {
self.keywords.bind_model(
None::<&gio::ListModel>,
clone!(@weak obj => @default-return { adw::ActionRow::new().upcast() }, move |item| obj.create_keyword_row(item)),
clone!(
#[weak]
obj,
#[upgrade_or_else]
|| { adw::ActionRow::new().upcast() },
move |item| obj.create_keyword_row(item)
),
);
}
@ -176,9 +200,13 @@ mod imp {
};
let obj = self.obj();
spawn!(clone!(@weak obj => async move {
obj.global_setting_changed(default).await;
}));
spawn!(clone!(
#[weak]
obj,
async move {
obj.global_setting_changed(default).await;
}
));
}
}
}
@ -359,9 +387,13 @@ impl NotificationsPage {
&[("keyword", &keyword)],
)));
row.connect_remove(clone!(@weak self as obj => move |row| {
obj.remove_keyword(row);
}));
row.connect_remove(clone!(
#[weak(rename_to = obj)]
self,
move |row| {
obj.remove_keyword(row);
}
));
row.upcast()
} else {
@ -378,16 +410,19 @@ impl NotificationsPage {
row.set_is_loading(true);
spawn!(clone!(@weak self as obj, @weak row => async move {
if settings.remove_keyword(row.title().into()).await.is_err() {
toast!(
obj,
gettext("Could not remove notification keyword")
);
}
spawn!(clone!(
#[weak(rename_to = obj)]
self,
#[weak]
row,
async move {
if settings.remove_keyword(row.title().into()).await.is_err() {
toast!(obj, gettext("Could not remove notification keyword"));
}
row.set_is_loading(false);
}));
row.set_is_loading(false);
}
));
}
/// Whether we can add the keyword that is currently in the entry.

44
src/session/view/account_settings/security_page/ignored_users_subpage/mod.rs

@ -73,21 +73,25 @@ mod imp {
self.filtered_model.set_filter(Some(&search_filter));
let factory = gtk::SignalListItemFactory::new();
factory.connect_setup(clone!(@weak self as imp => move |_, item| {
let Some(session) = imp.session.upgrade() else {
return;
};
let Some(item) = item.downcast_ref::<gtk::ListItem>() else {
error!("List item factory did not receive a list item: {item:?}");
return;
};
let row = IgnoredUserRow::new(&session.ignored_users());
item.set_child(Some(&row));
item.bind_property("item", &row, "item").build();
item.set_activatable(false);
item.set_selectable(false);
}));
factory.connect_setup(clone!(
#[weak(rename_to = imp)]
self,
move |_, item| {
let Some(session) = imp.session.upgrade() else {
return;
};
let Some(item) = item.downcast_ref::<gtk::ListItem>() else {
error!("List item factory did not receive a list item: {item:?}");
return;
};
let row = IgnoredUserRow::new(&session.ignored_users());
item.set_child(Some(&row));
item.bind_property("item", &row, "item").build();
item.set_activatable(false);
item.set_selectable(false);
}
));
self.list_view.set_factory(Some(&factory));
self.list_view.set_model(Some(&gtk::NoSelection::new(Some(
@ -124,11 +128,13 @@ mod imp {
let ignored_users = session.as_ref().map(|s| s.ignored_users());
if let Some(ignored_users) = &ignored_users {
let items_changed_handler = ignored_users.connect_items_changed(
clone!(@weak self as imp => move |_, _, _, _| {
let items_changed_handler = ignored_users.connect_items_changed(clone!(
#[weak(rename_to = imp)]
self,
move |_, _, _, _| {
imp.update_visible_page();
}),
);
}
));
self.items_changed_handler
.replace(Some(items_changed_handler));
}

41
src/session/view/account_settings/security_page/mod.rs

@ -114,11 +114,14 @@ mod imp {
if let Some(session) = &session {
let ignored_users = session.ignored_users();
let ignored_users_count_handler = ignored_users.connect_items_changed(
clone!(@weak self as imp => move |ignored_users, _, _, _| {
imp.ignored_users_row.set_count(ignored_users.n_items().to_string());
}),
);
let ignored_users_count_handler = ignored_users.connect_items_changed(clone!(
#[weak(rename_to = imp)]
self,
move |ignored_users, _, _, _| {
imp.ignored_users_row
.set_count(ignored_users.n_items().to_string());
}
));
self.ignored_users_row
.set_count(ignored_users.n_items().to_string());
@ -146,17 +149,27 @@ mod imp {
.replace(vec![public_read_receipts_binding, typing_binding]);
let crypto_identity_state_handler =
session.connect_crypto_identity_state_notify(clone!(@weak obj => move |_| {
obj.update_crypto_identity();
}));
let verification_state_handler =
session.connect_verification_state_notify(clone!(@weak obj => move |_| {
session.connect_crypto_identity_state_notify(clone!(
#[weak]
obj,
move |_| {
obj.update_crypto_identity();
}
));
let verification_state_handler = session.connect_verification_state_notify(clone!(
#[weak]
obj,
move |_| {
obj.update_crypto_identity();
}));
let recovery_state_handler =
session.connect_recovery_state_notify(clone!(@weak obj => move |_| {
}
));
let recovery_state_handler = session.connect_recovery_state_notify(clone!(
#[weak]
obj,
move |_| {
obj.update_recovery();
}));
}
));
self.session.set(
session,

42
src/session/view/account_settings/user_sessions_page/mod.rs

@ -101,30 +101,40 @@ mod imp {
UserSessionRow::new(user_session).upcast()
});
let other_sessions_handler = other_sessions.connect_items_changed(
clone!(@weak self as imp => move |other_sessions, _, _, _| {
imp.other_sessions_group.set_visible(other_sessions.n_items() > 0);
}),
);
let other_sessions_handler = other_sessions.connect_items_changed(clone!(
#[weak(rename_to = imp)]
self,
move |other_sessions, _, _, _| {
imp.other_sessions_group
.set_visible(other_sessions.n_items() > 0);
}
));
self.other_sessions_handler
.replace(Some(other_sessions_handler));
self.other_sessions_group
.set_visible(other_sessions.n_items() > 0);
let loading_state_handler = user_sessions.connect_loading_state_notify(
clone!(@weak self as imp => move |_| {
let loading_state_handler = user_sessions.connect_loading_state_notify(clone!(
#[weak(rename_to = imp)]
self,
move |_| {
imp.update_state();
}),
);
let is_empty_handler =
user_sessions.connect_is_empty_notify(clone!(@weak self as imp => move |_| {
}
));
let is_empty_handler = user_sessions.connect_is_empty_notify(clone!(
#[weak(rename_to = imp)]
self,
move |_| {
imp.update_state();
}));
let current_session_handler = user_sessions.connect_current_session_notify(
clone!(@weak self as imp => move |_| {
}
));
let current_session_handler = user_sessions.connect_current_session_notify(clone!(
#[weak(rename_to = imp)]
self,
move |_| {
imp.update_current_session();
}),
);
}
));
self.user_sessions.set(
user_sessions,

9
src/session/view/account_settings/user_sessions_page/user_session_row.rs

@ -64,10 +64,13 @@ mod imp {
let obj = self.obj();
let system_settings = Application::default().system_settings();
let system_settings_handler =
system_settings.connect_clock_format_notify(clone!(@weak obj => move |_| {
let system_settings_handler = system_settings.connect_clock_format_notify(clone!(
#[weak]
obj,
move |_| {
obj.update_last_seen_ts();
}));
}
));
self.system_settings_handler
.replace(Some(system_settings_handler));
}

53
src/session/view/content/explore/mod.rs

@ -74,27 +74,36 @@ mod imp {
let obj = self.obj();
let adj = self.scrolled_window.vadjustment();
adj.connect_value_changed(clone!(@weak self as imp => move |adj| {
if adj.upper() - adj.value() < adj.page_size() * 2.0 {
if let Some(public_room_list) = &*imp.public_room_list.borrow() {
public_room_list.load_public_rooms(false);
adj.connect_value_changed(clone!(
#[weak(rename_to = imp)]
self,
move |adj| {
if adj.upper() - adj.value() < adj.page_size() * 2.0 {
if let Some(public_room_list) = &*imp.public_room_list.borrow() {
public_room_list.load_public_rooms(false);
}
}
}
}));
));
self.search_entry
.connect_search_changed(clone!(@weak obj => move |_| {
self.search_entry.connect_search_changed(clone!(
#[weak]
obj,
move |_| {
obj.trigger_search();
}));
}
));
self.servers_popover.connect_selected_server_changed(
clone!(@weak obj => move |_, server| {
self.servers_popover.connect_selected_server_changed(clone!(
#[weak]
obj,
move |_, server| {
if let Some(server) = server {
obj.imp().servers_button.set_label(&server.name());
obj.trigger_search();
}
}),
);
}
));
}
}
@ -114,13 +123,21 @@ mod imp {
self.listview
.set_model(Some(&gtk::NoSelection::new(Some(public_room_list.clone()))));
public_room_list.connect_loading_notify(clone!(@weak obj => move |_| {
obj.update_visible_child();
}));
public_room_list.connect_loading_notify(clone!(
#[weak]
obj,
move |_| {
obj.update_visible_child();
}
));
public_room_list.connect_empty_notify(clone!(@weak obj => move |_| {
obj.update_visible_child();
}));
public_room_list.connect_empty_notify(clone!(
#[weak]
obj,
move |_| {
obj.update_visible_child();
}
));
self.public_room_list.replace(Some(public_room_list));
obj.update_visible_child();

25
src/session/view/content/explore/public_room.rs

@ -58,15 +58,20 @@ mod imp {
self.avatar_data.set(avatar_data).unwrap();
obj.room_list()
.connect_pending_rooms_changed(clone!(@weak obj => move |_| {
obj.room_list().connect_pending_rooms_changed(clone!(
#[weak]
obj,
move |_| {
let Some(matrix_public_room) = obj.matrix_public_room() else {
return;
};
obj.set_pending(obj.room_list()
.is_pending_room((*matrix_public_room.room_id).into()));
}));
obj.set_pending(
obj.room_list()
.is_pending_room((*matrix_public_room.room_id).into()),
);
}
));
}
fn dispose(&self) {
@ -121,16 +126,18 @@ impl PublicRoom {
self.set_room(room);
} else {
let room_id = room.room_id.clone();
let handler_id = self.room_list().connect_items_changed(
clone!(@weak self as obj => move |room_list, _, _, _| {
let handler_id = self.room_list().connect_items_changed(clone!(
#[weak(rename_to = obj)]
self,
move |room_list, _, _, _| {
if let Some(room) = room_list.get(&room_id) {
if let Some(handler_id) = obj.imp().room_handler.take() {
obj.set_room(room);
room_list.disconnect(handler_id);
}
}
}),
);
}
));
imp.room_handler.replace(Some(handler_id));
}

24
src/session/view/content/explore/public_room_list.rs

@ -259,18 +259,22 @@ impl PublicRoomList {
spawn!(
glib::Priority::DEFAULT_IDLE,
clone!(@weak self as obj => async move {
// If the search term changed we ignore the response
if obj.is_valid_response(current_search_term, current_server, current_network) {
match handle.await.unwrap() {
Ok(response) => obj.handle_public_rooms_response(response),
Err(error) => {
obj.set_request_sent(false);
error!("Error loading public rooms: {error}")
},
clone!(
#[weak(rename_to = obj)]
self,
async move {
// If the search term changed we ignore the response
if obj.is_valid_response(current_search_term, current_server, current_network) {
match handle.await.unwrap() {
Ok(response) => obj.handle_public_rooms_response(response),
Err(error) => {
obj.set_request_sent(false);
error!("Error loading public rooms: {error}")
}
}
}
}
})
)
);
}
}

39
src/session/view/content/explore/public_room_row.rs

@ -63,10 +63,13 @@ mod imp {
impl ObjectImpl for PublicRoomRow {
fn constructed(&self) {
self.parent_constructed();
self.button
.connect_clicked(clone!(@weak self as imp => move |_| {
self.button.connect_clicked(clone!(
#[weak(rename_to = imp)]
self,
move |_| {
imp.obj().join_or_view();
}));
}
));
}
}
@ -89,14 +92,20 @@ mod imp {
}
if public_room.matrix_public_room().is_some() {
let pending_handler =
public_room.connect_pending_notify(clone!(@weak self as imp => move |_| {
let pending_handler = public_room.connect_pending_notify(clone!(
#[weak(rename_to = imp)]
self,
move |_| {
imp.update_button();
}));
let room_handler =
public_room.connect_room_notify(clone!(@weak self as imp => move |_| {
}
));
let room_handler = public_room.connect_room_notify(clone!(
#[weak(rename_to = imp)]
self,
move |_| {
imp.update_button();
}));
}
));
self.public_room
.set(public_room, vec![pending_handler, room_handler]);
@ -225,11 +234,15 @@ impl PublicRoomRow {
(id, via)
});
spawn!(clone!(@weak self as obj => async move {
if let Err(error) = room_list.join_by_id_or_alias(room_id, via).await {
toast!(obj, error);
spawn!(clone!(
#[weak(rename_to = obj)]
self,
async move {
if let Err(error) = room_list.join_by_id_or_alias(room_id, via).await {
toast!(obj, error);
}
}
}));
));
}
}
}

10
src/session/view/content/explore/server_list.rs

@ -60,9 +60,13 @@ mod imp {
)]);
obj.items_changed(0, 0, 1);
spawn!(clone!(@weak obj => async move {
obj.load_servers().await;
}));
spawn!(clone!(
#[weak]
obj,
async move {
obj.load_servers().await;
}
));
}
}
}

34
src/session/view/content/explore/servers_popover.rs

@ -62,14 +62,16 @@ mod imp {
self.parent_constructed();
let obj = self.obj();
self.server_entry
.connect_changed(clone!(@weak obj => move |_| {
obj.update_add_server_state()
}));
self.server_entry
.connect_activate(clone!(@weak obj => move |_| {
obj.add_server()
}));
self.server_entry.connect_changed(clone!(
#[weak]
obj,
move |_| obj.update_add_server_state()
));
self.server_entry.connect_activate(clone!(
#[weak]
obj,
move |_| obj.add_server()
));
obj.update_add_server_state();
}
@ -135,11 +137,17 @@ impl ExploreServersPopover {
&self,
f: F,
) -> glib::SignalHandlerId {
self.imp()
.listbox
.connect_row_selected(clone!(@weak self as obj => move |_, row| {
f(&obj, row.and_then(|row| row.downcast_ref::<ExploreServerRow>()).and_then(|row| row.server()));
}))
self.imp().listbox.connect_row_selected(clone!(
#[weak(rename_to = obj)]
self,
move |_, row| {
f(
&obj,
row.and_then(|row| row.downcast_ref::<ExploreServerRow>())
.and_then(|row| row.server()),
);
}
))
}
/// Whether the server currently in the text entry can be added.

18
src/session/view/content/invite.rs

@ -125,18 +125,22 @@ mod imp {
}
if let Some(room) = &room {
let category_handler = room.connect_category_notify(
clone!(@weak obj => move |room| {
let category_handler = room.connect_category_notify(clone!(
#[weak]
obj,
move |room| {
let category = room.category();
if category == RoomType::Left {
// We declined the invite or the invite was retracted, we should close the room
// if it is opened.
// We declined the invite or the invite was retracted, we should close
// the room if it is opened.
let Some(session) = room.session() else {
return;
};
let selection = session.sidebar_list_model().selection_model();
if let Some(selected_room) = selection.selected_item().and_downcast::<Room>() {
if let Some(selected_room) =
selection.selected_item().and_downcast::<Room>()
{
if selected_room == *room {
selection.set_selected_item(None::<glib::Object>);
}
@ -152,8 +156,8 @@ mod imp {
room.disconnect(category_handler);
}
}
}),
);
}
));
self.category_handler.replace(Some(category_handler));
}

32
src/session/view/content/mod.rs

@ -91,12 +91,16 @@ mod imp {
fn constructed(&self) {
self.parent_constructed();
self.stack
.connect_visible_child_notify(clone!(@weak self as imp => move |_| {
self.stack.connect_visible_child_notify(clone!(
#[weak(rename_to = imp)]
self,
move |_| {
if imp.visible_page() != ContentPage::Verification {
imp.identity_verification_widget.set_verification(None::<IdentityVerification>);
imp.identity_verification_widget
.set_verification(None::<IdentityVerification>);
}
}));
}
));
if let Some(binding) = self.item_binding.take() {
binding.unbind()
@ -163,15 +167,23 @@ mod imp {
if let Some(item) = &item {
if let Some(room) = item.downcast_ref::<Room>() {
let handler_id = room.connect_category_notify(clone!(@weak obj => move |_| {
obj.update_visible_child();
}));
let handler_id = room.connect_category_notify(clone!(
#[weak]
obj,
move |_| {
obj.update_visible_child();
}
));
self.signal_handler.replace(Some(handler_id));
} else if let Some(verification) = item.downcast_ref::<IdentityVerification>() {
let handler_id = verification.connect_dismiss(clone!(@weak obj => move |_| {
obj.set_item(None::<glib::Object>);
}));
let handler_id = verification.connect_dismiss(clone!(
#[weak]
obj,
move |_| {
obj.set_item(None::<glib::Object>);
}
));
self.signal_handler.replace(Some(handler_id));
}
}

107
src/session/view/content/room_details/addresses_subpage/completion_popover.rs

@ -64,10 +64,13 @@ mod imp {
.set_expression(Some(gtk::StringObject::this_expression("string")));
self.filtered_list.set_filter(Some(&self.filter));
self.filtered_list
.connect_items_changed(clone!(@weak obj => move |_,_,_,_| {
self.filtered_list.connect_items_changed(clone!(
#[weak]
obj,
move |_, _, _, _| {
obj.update_completion();
}));
}
));
self.list.bind_model(Some(&self.filtered_list), |item| {
let Some(item) = item.downcast_ref::<gtk::StringObject>() else {
@ -125,46 +128,55 @@ mod imp {
if let Some(entry) = entry {
let key_events = gtk::EventControllerKey::new();
key_events.connect_key_pressed(clone!(@weak obj => @default-return glib::Propagation::Proceed, move |_, key, _, modifier| {
if modifier.is_empty() {
if obj.is_visible() {
let imp = obj.imp();
if matches!(key, gdk::Key::Return | gdk::Key::KP_Enter | gdk::Key::ISO_Enter) {
// Activate completion.
obj.activate_selected_row();
return glib::Propagation::Stop;
} else if matches!(key, gdk::Key::Up | gdk::Key::KP_Up) {
// Move up, if possible.
let idx = obj.selected_row_index().unwrap_or_default();
if idx > 0 {
obj.select_row_at_index(Some(idx - 1));
key_events.connect_key_pressed(clone!(
#[weak]
obj,
#[upgrade_or]
glib::Propagation::Proceed,
move |_, key, _, modifier| {
if modifier.is_empty() {
if obj.is_visible() {
let imp = obj.imp();
if matches!(
key,
gdk::Key::Return | gdk::Key::KP_Enter | gdk::Key::ISO_Enter
) {
// Activate completion.
obj.activate_selected_row();
return glib::Propagation::Stop;
} else if matches!(key, gdk::Key::Up | gdk::Key::KP_Up) {
// Move up, if possible.
let idx = obj.selected_row_index().unwrap_or_default();
if idx > 0 {
obj.select_row_at_index(Some(idx - 1));
}
return glib::Propagation::Stop;
} else if matches!(key, gdk::Key::Down | gdk::Key::KP_Down) {
// Move down, if possible.
let new_idx = if let Some(idx) = obj.selected_row_index() {
idx + 1
} else {
0
};
let max = imp.filtered_list.n_items() as usize;
if new_idx < max {
obj.select_row_at_index(Some(new_idx));
}
return glib::Propagation::Stop;
} else if matches!(key, gdk::Key::Escape) {
// Close.
obj.popdown();
return glib::Propagation::Stop;
}
return glib::Propagation::Stop;
} else if matches!(key, gdk::Key::Down | gdk::Key::KP_Down) {
// Move down, if possible.
let new_idx = if let Some(idx) = obj.selected_row_index() {
idx + 1
} else {
0
};
let max = imp.filtered_list.n_items() as usize;
if new_idx < max {
obj.select_row_at_index(Some(new_idx));
}
return glib::Propagation::Stop;
} else if matches!(key, gdk::Key::Escape) {
// Close.
obj.popdown();
} else if matches!(key, gdk::Key::Tab) {
obj.update_completion();
return glib::Propagation::Stop;
}
} else if matches!(key, gdk::Key::Tab) {
obj.update_completion();
return glib::Propagation::Stop;
}
glib::Propagation::Proceed
}
glib::Propagation::Proceed
}));
));
entry.add_controller(key_events.clone());
self.entry_controller.replace(Some(key_events));
@ -175,14 +187,21 @@ mod imp {
.build();
self.entry_binding.replace(Some(search_binding));
let changed_handler = entry.connect_changed(clone!(@weak obj => move |_| {
obj.update_completion();
}));
let changed_handler = entry.connect_changed(clone!(
#[weak]
obj,
move |_| {
obj.update_completion();
}
));
let state_flags_handler =
entry.connect_state_flags_changed(clone!(@weak obj => move |_, _| {
let state_flags_handler = entry.connect_state_flags_changed(clone!(
#[weak]
obj,
move |_, _| {
obj.update_completion();
}));
}
));
obj.set_parent(entry);
self.entry

133
src/session/view/content/room_details/addresses_subpage/mod.rs

@ -96,19 +96,29 @@ mod imp {
self.public_addresses_list.bind_model(
Some(&flattened_public_list),
clone!(
@weak obj => @default-return { adw::ActionRow::new().upcast() },
#[weak]
obj,
#[upgrade_or_else]
|| { adw::ActionRow::new().upcast() },
move |item| obj.create_public_address_row(item)
),
);
self.public_addresses_add_row
.connect_changed(clone!(@weak obj => move |_| {
self.public_addresses_add_row.connect_changed(clone!(
#[weak]
obj,
move |_| {
obj.update_public_addresses_add_row();
}));
}
));
// Filter addresses already in the list.
let new_addresses_filter = gtk::CustomFilter::new(
clone!(@weak self as imp => @default-return false, move |item: &glib::Object| {
let new_addresses_filter = gtk::CustomFilter::new(clone!(
#[weak(rename_to = imp)]
self,
#[upgrade_or]
false,
move |item: &glib::Object| {
let Some(item) = item.downcast_ref::<gtk::StringObject>() else {
return false;
};
@ -127,15 +137,17 @@ mod imp {
}
true
}),
);
}
));
// Update the filtered list everytime an item changes.
self.public_addresses().connect_items_changed(
clone!(@weak new_addresses_filter => move |_,_,_,_| {
self.public_addresses().connect_items_changed(clone!(
#[weak]
new_addresses_filter,
move |_, _, _, _| {
new_addresses_filter.changed(gtk::FilterChange::Different);
}),
);
}
));
let new_local_addresses = gtk::FilterListModel::new(
Some(self.local_addresses.clone()),
@ -157,15 +169,21 @@ mod imp {
self.local_addresses_list.bind_model(
Some(&flattened_local_list),
clone!(
@weak obj => @default-return { adw::ActionRow::new().upcast() },
#[weak]
obj,
#[upgrade_or_else]
|| { adw::ActionRow::new().upcast() },
move |item| obj.create_local_address_row(item)
),
);
self.local_addresses_add_row
.connect_changed(clone!(@weak obj => move |_| {
self.local_addresses_add_row.connect_changed(clone!(
#[weak]
obj,
move |_| {
obj.update_local_addresses_add_row();
}));
}
));
}
fn dispose(&self) {
@ -192,10 +210,13 @@ mod imp {
fn set_room(&self, room: Room) {
let aliases = room.aliases();
let aliases_changed_handler =
aliases.connect_changed(clone!(@weak self as imp => move |_| {
let aliases_changed_handler = aliases.connect_changed(clone!(
#[weak(rename_to = imp)]
self,
move |_| {
imp.update_public_addresses();
}));
}
));
self.aliases_changed_handler
.replace(Some(aliases_changed_handler));
@ -205,9 +226,13 @@ mod imp {
self.update_public_addresses();
self.update_local_addresses_server();
spawn!(clone!(@weak self as imp => async move {
imp.update_local_addresses().await;
}));
spawn!(clone!(
#[weak(rename_to = imp)]
self,
async move {
imp.update_local_addresses().await;
}
));
}
/// Update the list of public addresses.
@ -382,16 +407,30 @@ impl AddressesSubpage {
&[("address", alias.as_str())],
)));
address.connect_is_main_notify(clone!(@weak self as obj, @weak row => move |address| {
obj.update_public_row_is_main(&row, address.is_main());
}));
address.connect_is_main_notify(clone!(
#[weak(rename_to = obj)]
self,
#[weak]
row,
move |address| {
obj.update_public_row_is_main(&row, address.is_main());
}
));
self.update_public_row_is_main(&row, address.is_main());
row.connect_remove(clone!(@weak self as obj => move |row| {
spawn!(clone!(@weak row => async move {
obj.remove_public_address(&row).await;
}));
}));
row.connect_remove(clone!(
#[weak(rename_to = obj)]
self,
move |row| {
spawn!(clone!(
#[weak]
row,
async move {
obj.remove_public_address(&row).await;
}
));
}
));
row.upcast()
} else {
@ -440,11 +479,17 @@ impl AddressesSubpage {
);
button.update_property(&[gtk::accessible::Property::Label(&accessible_label)]);
button.connect_clicked(clone!(@weak self as obj, @weak row => move |_| {
spawn!(async move {
obj.set_main_public_address(&row).await;
});
}));
button.connect_clicked(clone!(
#[weak(rename_to = obj)]
self,
#[weak]
row,
move |_| {
spawn!(async move {
obj.set_main_public_address(&row).await;
});
}
));
row.set_extra_suffix(Some(button));
}
@ -622,11 +667,19 @@ impl AddressesSubpage {
&[("address", &alias)],
)));
row.connect_remove(clone!(@weak self as obj => move |row| {
spawn!(clone!(@weak row => async move {
obj.unregister_local_address(&row).await;
}));
}));
row.connect_remove(clone!(
#[weak(rename_to = obj)]
self,
move |row| {
spawn!(clone!(
#[weak]
row,
async move {
obj.unregister_local_address(&row).await;
}
));
}
));
row.upcast()
} else {

272
src/session/view/content/room_details/general_page/mod.rs

@ -186,82 +186,131 @@ mod imp {
.chain_property::<AvatarImage>("uri")
.watch(
Some(&avatar_data),
clone!(@weak obj, @weak avatar_data => move || {
obj.avatar_changed(avatar_data.image().and_then(|i| i.uri()));
}),
clone!(
#[weak]
obj,
#[weak]
avatar_data,
move || {
obj.avatar_changed(avatar_data.image().and_then(|i| i.uri()));
}
),
);
self.expr_watches.borrow_mut().push(expr_watch);
let membership_handler =
room.own_member()
.connect_membership_notify(clone!(@weak obj => move |_| {
obj.update_sections();
}));
let membership_handler = room.own_member().connect_membership_notify(clone!(
#[weak]
obj,
move |_| {
obj.update_sections();
}
));
self.membership_handler.replace(Some(membership_handler));
let permissions_handler =
room.permissions()
.connect_changed(clone!(@weak obj => move |_| {
obj.update_upgrade_button();
obj.update_edit_addresses_button();
obj.update_join_rule();
obj.update_guest_access();
obj.update_history_visibility();
obj.update_encryption();
let permissions_handler = room.permissions().connect_changed(clone!(
#[weak]
obj,
move |_| {
obj.update_upgrade_button();
obj.update_edit_addresses_button();
obj.update_join_rule();
obj.update_guest_access();
obj.update_history_visibility();
obj.update_encryption();
spawn!(async move {
obj.update_publish().await;
});
}));
spawn!(async move {
obj.update_publish().await;
});
}
));
self.permissions_handler.replace(Some(permissions_handler));
let aliases = room.aliases();
let canonical_alias_handler =
aliases.connect_canonical_alias_string_notify(clone!(@weak obj => move |_| {
let canonical_alias_handler = aliases.connect_canonical_alias_string_notify(clone!(
#[weak]
obj,
move |_| {
obj.update_addresses();
}));
}
));
self.canonical_alias_handler
.replace(Some(canonical_alias_handler));
let alt_aliases_handler = aliases.alt_aliases_model().connect_items_changed(
clone!(@weak obj => move |_,_,_,_| {
let alt_aliases_handler = aliases.alt_aliases_model().connect_items_changed(clone!(
#[weak]
obj,
move |_, _, _, _| {
obj.update_addresses();
}),
);
}
));
self.alt_aliases_handler.replace(Some(alt_aliases_handler));
let join_rule_handler =
room.join_rule()
.connect_changed(clone!(@weak obj => move |_| {
obj.update_join_rule();
}));
let join_rule_handler = room.join_rule().connect_changed(clone!(
#[weak]
obj,
move |_| {
obj.update_join_rule();
}
));
self.join_rule_handler.replace(Some(join_rule_handler));
let room_handler_ids = vec![
room.connect_name_notify(clone!(@weak obj => move |room| {
obj.name_changed(room.name());
})),
room.connect_topic_notify(clone!(@weak obj => move |room| {
obj.topic_changed(room.topic());
})),
room.connect_joined_members_count_notify(clone!(@weak obj => move |room| {
obj.member_count_changed(room.joined_members_count());
})),
room.connect_notifications_setting_notify(clone!(@weak obj => move |_| {
obj.update_notifications();
})),
room.connect_is_tombstoned_notify(clone!(@weak obj => move |_| {
obj.update_upgrade_button();
})),
room.connect_guests_allowed_notify(clone!(@weak obj => move |_| {
obj.update_guest_access();
})),
room.connect_history_visibility_notify(clone!(@weak obj => move |_| {
obj.update_history_visibility();
})),
room.connect_is_encrypted_notify(clone!(@weak obj => move |_| {
obj.update_encryption();
})),
room.connect_name_notify(clone!(
#[weak]
obj,
move |room| {
obj.name_changed(room.name());
}
)),
room.connect_topic_notify(clone!(
#[weak]
obj,
move |room| {
obj.topic_changed(room.topic());
}
)),
room.connect_joined_members_count_notify(clone!(
#[weak]
obj,
move |room| {
obj.member_count_changed(room.joined_members_count());
}
)),
room.connect_notifications_setting_notify(clone!(
#[weak]
obj,
move |_| {
obj.update_notifications();
}
)),
room.connect_is_tombstoned_notify(clone!(
#[weak]
obj,
move |_| {
obj.update_upgrade_button();
}
)),
room.connect_guests_allowed_notify(clone!(
#[weak]
obj,
move |_| {
obj.update_guest_access();
}
)),
room.connect_history_visibility_notify(clone!(
#[weak]
obj,
move |_| {
obj.update_history_visibility();
}
)),
room.connect_is_encrypted_notify(clone!(
#[weak]
obj,
move |_| {
obj.update_encryption();
}
)),
];
obj.member_count_changed(room.joined_members_count());
@ -278,12 +327,20 @@ mod imp {
if let Some(session) = room.session() {
let settings = session.notifications().settings();
let notifications_settings_handlers = vec![
settings.connect_account_enabled_notify(clone!(@weak obj => move |_| {
obj.update_notifications();
})),
settings.connect_session_enabled_notify(clone!(@weak obj => move |_| {
obj.update_notifications();
})),
settings.connect_account_enabled_notify(clone!(
#[weak]
obj,
move |_| {
obj.update_notifications();
}
)),
settings.connect_session_enabled_notify(clone!(
#[weak]
obj,
move |_| {
obj.update_notifications();
}
)),
];
self.notifications_settings_handlers
@ -302,9 +359,13 @@ mod imp {
obj.update_encryption();
obj.update_upgrade_button();
spawn!(clone!(@weak obj => async move {
obj.update_publish().await;
}));
spawn!(clone!(
#[weak]
obj,
async move {
obj.update_publish().await;
}
));
self.load_capabilities();
}
@ -347,20 +408,22 @@ mod imp {
spawn!(
glib::Priority::LOW,
clone!(@weak self as imp => async move {
let handle = spawn_tokio!(async move {
client.get_capabilities().await
});
match handle.await.unwrap() {
Ok(capabilities) => {
imp.capabilities.replace(capabilities);
}
Err(error) => {
error!("Could not get server capabilities: {error}");
imp.capabilities.take();
clone!(
#[weak(rename_to = imp)]
self,
async move {
let handle = spawn_tokio!(async move { client.get_capabilities().await });
match handle.await.unwrap() {
Ok(capabilities) => {
imp.capabilities.replace(capabilities);
}
Err(error) => {
error!("Could not get server capabilities: {error}");
imp.capabilities.take();
}
}
}
})
)
);
}
}
@ -396,16 +459,24 @@ impl GeneralPage {
fn init_avatar(&self) {
let avatar = &*self.imp().avatar;
avatar.connect_edit_avatar(clone!(@weak self as obj => move |_, file| {
spawn!(async move {
obj.change_avatar(file).await;
});
}));
avatar.connect_remove_avatar(clone!(@weak self as obj => move |_| {
spawn!(async move {
obj.remove_avatar().await;
});
}));
avatar.connect_edit_avatar(clone!(
#[weak(rename_to = obj)]
self,
move |_, file| {
spawn!(async move {
obj.change_avatar(file).await;
});
}
));
avatar.connect_remove_avatar(clone!(
#[weak(rename_to = obj)]
self,
move |_| {
spawn!(async move {
obj.remove_avatar().await;
});
}
));
}
fn avatar_changed(&self, uri: Option<String>) {
@ -860,17 +931,22 @@ impl GeneralPage {
self.set_notifications_loading(true, setting);
let settings = session.notifications().settings();
spawn!(clone!(@weak self as obj => async move {
if settings.set_per_room_setting(room.room_id().to_owned(), setting).await.is_err() {
toast!(
obj,
gettext("Could not change notifications setting")
);
}
spawn!(clone!(
#[weak(rename_to = obj)]
self,
async move {
if settings
.set_per_room_setting(room.room_id().to_owned(), setting)
.await
.is_err()
{
toast!(obj, gettext("Could not change notifications setting"));
}
obj.set_notifications_loading(false, setting);
obj.update_notifications();
}));
obj.set_notifications_loading(false, setting);
obj.update_notifications();
}
));
}
/// Update the button to edit addresses.

36
src/session/view/content/room_details/history_viewer/audio.rs

@ -64,20 +64,32 @@ mod imp {
self.list_view.set_model(Some(&model));
// Load an initial number of items
spawn!(clone!(@weak self as imp, @weak timeline => async move {
while model.n_items() < MIN_N_ITEMS {
if !timeline.load().await {
break;
spawn!(clone!(
#[weak(rename_to = imp)]
self,
#[weak]
timeline,
async move {
while model.n_items() < MIN_N_ITEMS {
if !timeline.load().await {
break;
}
}
}
let adj = imp.list_view.vadjustment().unwrap();
adj.connect_value_notify(clone!(@weak timeline => move |adj| {
if adj.value() + adj.page_size() * 2.0 >= adj.upper() {
spawn!(async move { timeline.load().await; });
}
}));
}));
let adj = imp.list_view.vadjustment().unwrap();
adj.connect_value_notify(clone!(
#[weak]
timeline,
move |adj| {
if adj.value() + adj.page_size() * 2.0 >= adj.upper() {
spawn!(async move {
timeline.load().await;
});
}
}
));
}
));
self.timeline.set(timeline).unwrap();
}

28
src/session/view/content/room_details/history_viewer/audio_row.rs

@ -98,9 +98,13 @@ mod imp {
}
if let Some(session) = event.room().and_then(|r| r.session()) {
spawn!(clone!(@weak obj => async move {
obj.download_audio(audio, &session).await;
}));
spawn!(clone!(
#[weak]
obj,
async move {
obj.download_audio(audio, &session).await;
}
));
}
}
}
@ -150,16 +154,22 @@ impl AudioRow {
fn prepare_audio(&self, file: gio::File) {
let media_file = gtk::MediaFile::for_file(&file);
media_file.connect_error_notify(clone!(@weak self as obj => move |media_file| {
media_file.connect_error_notify(|media_file| {
if let Some(error) = media_file.error() {
warn!("Error reading audio file: {}", error);
}
}));
media_file.connect_ended_notify(clone!(@weak self as obj => move |media_file| {
if media_file.is_ended() {
obj.imp().play_button.set_icon_name("media-playback-start-symbolic");
});
media_file.connect_ended_notify(clone!(
#[weak(rename_to = obj)]
self,
move |media_file| {
if media_file.is_ended() {
obj.imp()
.play_button
.set_icon_name("media-playback-start-symbolic");
}
}
}));
));
self.imp().media_file.replace(Some(media_file));
}

36
src/session/view/content/room_details/history_viewer/file.rs

@ -64,20 +64,32 @@ mod imp {
self.list_view.set_model(Some(&model));
// Load an initial number of items
spawn!(clone!(@weak self as imp, @weak timeline => async move {
while model.n_items() < MIN_N_ITEMS {
if !timeline.load().await {
break;
spawn!(clone!(
#[weak(rename_to = imp)]
self,
#[weak]
timeline,
async move {
while model.n_items() < MIN_N_ITEMS {
if !timeline.load().await {
break;
}
}
}
let adj = imp.list_view.vadjustment().unwrap();
adj.connect_value_notify(clone!(@weak timeline => move |adj| {
if adj.value() + adj.page_size() * 2.0 >= adj.upper() {
spawn!(async move { timeline.load().await; });
}
}));
}));
let adj = imp.list_view.vadjustment().unwrap();
adj.connect_value_notify(clone!(
#[weak]
timeline,
move |adj| {
if adj.value() + adj.page_size() * 2.0 >= adj.upper() {
spawn!(async move {
timeline.load().await;
});
}
}
));
}
));
self.timeline.set(timeline).unwrap();
}

36
src/session/view/content/room_details/history_viewer/media.rs

@ -66,20 +66,32 @@ mod imp {
self.grid_view.set_model(Some(&model));
// Load an initial number of items.
spawn!(clone!(@weak self as imp, @weak timeline => async move {
while model.n_items() < MIN_N_ITEMS {
if !timeline.load().await {
break;
spawn!(clone!(
#[weak(rename_to = imp)]
self,
#[weak]
timeline,
async move {
while model.n_items() < MIN_N_ITEMS {
if !timeline.load().await {
break;
}
}
}
let adj = imp.grid_view.vadjustment().unwrap();
adj.connect_value_notify(clone!(@weak timeline => move |adj| {
if adj.value() + adj.page_size() * 2.0 >= adj.upper() {
spawn!(async move { timeline.load().await; });
}
}));
}));
let adj = imp.grid_view.vadjustment().unwrap();
adj.connect_value_notify(clone!(
#[weak]
timeline,
move |adj| {
if adj.value() + adj.page_size() * 2.0 >= adj.upper() {
spawn!(async move {
timeline.load().await;
});
}
}
));
}
));
self.timeline.set(timeline).unwrap();
}

40
src/session/view/content/room_details/history_viewer/media_item.rs

@ -193,28 +193,32 @@ impl MediaItem {
spawn!(
glib::Priority::LOW,
clone!(@weak self as obj => async move {
let imp = obj.imp();
match handle.await.unwrap() {
Ok(Some(data)) => {
match gdk::Texture::from_bytes(&glib::Bytes::from(&data)) {
Ok(texture) => {
imp.picture.set_paintable(Some(&texture));
}
Err(error) => {
warn!("Image file not supported: {}", error);
clone!(
#[weak(rename_to = obj)]
self,
async move {
let imp = obj.imp();
match handle.await.unwrap() {
Ok(Some(data)) => {
match gdk::Texture::from_bytes(&glib::Bytes::from(&data)) {
Ok(texture) => {
imp.picture.set_paintable(Some(&texture));
}
Err(error) => {
warn!("Image file not supported: {}", error);
}
}
}
}
Ok(None) => {
warn!("Could not retrieve invalid media file");
}
Err(error) => {
warn!("Could not retrieve media file: {}", error);
Ok(None) => {
warn!("Could not retrieve invalid media file");
}
Err(error) => {
warn!("Could not retrieve media file: {}", error);
}
}
}
})
)
);
}

30
src/session/view/content/room_details/invite_subpage/list.rs

@ -115,9 +115,13 @@ mod imp {
self.search_term.replace(search_term);
spawn!(clone!(@weak obj => async move {
obj.search_users().await;
}));
spawn!(clone!(
#[weak]
obj,
async move {
obj.search_users().await;
}
));
obj.notify_search_term();
}
@ -330,12 +334,20 @@ impl InviteList {
let item = InviteItem::new(user);
item.set_invite_exception(invite_exception);
item.connect_is_invitee_notify(clone!(@weak self as obj => move |item| {
obj.update_invitees_for_item(item);
}));
item.connect_can_invite_notify(clone!(@weak self as obj => move |item| {
obj.update_invitees_for_item(item);
}));
item.connect_is_invitee_notify(clone!(
#[weak(rename_to = obj)]
self,
move |item| {
obj.update_invitees_for_item(item);
}
));
item.connect_can_invite_notify(clone!(
#[weak(rename_to = obj)]
self,
move |item| {
obj.update_invitees_for_item(item);
}
));
item
}

30
src/session/view/content/room_details/invite_subpage/mod.rs

@ -95,17 +95,29 @@ mod imp {
let obj = self.obj();
let invite_list = self.invite_list.get_or_init(|| InviteList::new(&room));
invite_list.connect_invitee_added(clone!(@weak self as imp => move |_, invitee| {
imp.search_entry.add_pill(&invitee.user());
}));
invite_list.connect_invitee_added(clone!(
#[weak(rename_to = imp)]
self,
move |_, invitee| {
imp.search_entry.add_pill(&invitee.user());
}
));
invite_list.connect_invitee_removed(clone!(@weak self as imp => move |_, invitee| {
imp.search_entry.remove_pill(&invitee.user().identifier());
}));
invite_list.connect_invitee_removed(clone!(
#[weak(rename_to = imp)]
self,
move |_, invitee| {
imp.search_entry.remove_pill(&invitee.user().identifier());
}
));
invite_list.connect_state_notify(clone!(@weak self as imp => move |_| {
imp.update_view();
}));
invite_list.connect_state_notify(clone!(
#[weak(rename_to = imp)]
self,
move |_| {
imp.update_view();
}
));
self.search_entry
.bind_property("text", invite_list, "search-term")

33
src/session/view/content/room_details/members_page/members_list_view/extra_lists.rs

@ -46,13 +46,21 @@ mod imp {
let invited_members = obj.invited().model();
let banned_members = obj.banned().model();
invited_members.connect_items_changed(clone!(@weak obj => move |_, _, _, _| {
obj.update_invited();
}));
invited_members.connect_items_changed(clone!(
#[weak]
obj,
move |_, _, _, _| {
obj.update_invited();
}
));
banned_members.connect_items_changed(clone!(@weak obj => move |_, _, _, _| {
obj.update_banned();
}));
banned_members.connect_items_changed(clone!(
#[weak]
obj,
move |_, _, _, _| {
obj.update_banned();
}
));
self.invited_is_empty.set(invited_members.n_items() == 0);
self.banned_is_empty.set(banned_members.n_items() == 0);
@ -104,12 +112,13 @@ mod imp {
self.members.disconnect_signals();
let signal_handler_ids =
vec![
members.connect_state_notify(clone!(@weak obj => move |members| {
obj.update_loading_state(members.state());
})),
];
let signal_handler_ids = vec![members.connect_state_notify(clone!(
#[weak]
obj,
move |members| {
obj.update_loading_state(members.state());
}
))];
obj.update_loading_state(members.state());
self.members.set(&members, signal_handler_ids);

10
src/session/view/content/room_details/members_page/members_list_view/membership_subpage_row.rs

@ -79,12 +79,14 @@ mod imp {
if let Some(item) = &item {
let model = item.model();
let items_changed_handler = model.connect_items_changed(
clone!(@weak self as imp => move |model, _, _, _| {
let items_changed_handler = model.connect_items_changed(clone!(
#[weak(rename_to = imp)]
self,
move |model, _, _, _| {
imp.member_count_changed(model.n_items());
imp.obj().notify_label();
}),
);
}
));
self.items_changed_handler
.replace(Some(items_changed_handler));

18
src/session/view/content/room_details/members_page/members_list_view/mod.rs

@ -116,8 +116,10 @@ mod imp {
self.list_view.set_model(Some(&gtk::NoSelection::new(Some(
self.filtered_model.clone(),
))));
self.list_view
.connect_activate(clone!(@weak obj => move |_, pos| {
self.list_view.connect_activate(clone!(
#[weak]
obj,
move |_, pos| {
let Some(item) = obj.imp().filtered_model.item(pos) else {
return;
};
@ -135,7 +137,8 @@ mod imp {
)
.unwrap();
}
}));
}
));
obj.connect_tag_notify(|obj| {
obj.imp().update_title();
@ -176,10 +179,13 @@ mod imp {
}
if let Some(model) = &model {
let items_changed_handler =
model.connect_items_changed(clone!(@weak self as imp => move |_, _, _, _| {
let items_changed_handler = model.connect_items_changed(clone!(
#[weak(rename_to = imp)]
self,
move |_, _, _, _| {
imp.update_title();
}));
}
));
self.items_changed_handler
.replace(Some(items_changed_handler));
}

15
src/session/view/content/room_details/members_page/mod.rs

@ -80,10 +80,17 @@ mod imp {
let member = room.get_or_create_members().get_or_create(user_id);
let user_page = UserPage::new(&member);
user_page.connect_close(clone!(@weak obj => move |_| {
obj.imp().navigation_view.pop();
toast!(obj, gettext("The user is not in the room members list anymore"));
}));
user_page.connect_close(clone!(
#[weak]
obj,
move |_| {
obj.imp().navigation_view.pop();
toast!(
obj,
gettext("The user is not in the room members list anymore")
);
}
));
obj.imp().navigation_view.push(&user_page);
},

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

@ -79,15 +79,20 @@ mod imp {
self.parent_constructed();
let obj = self.obj();
self.search_entry
.connect_pill_removed(clone!(@weak self as imp => move |_, source| {
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());
}
}));
}
));
self.list_view
.connect_activate(clone!(@weak self as imp => move |list_view, index| {
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))
@ -97,7 +102,8 @@ mod imp {
};
imp.toggle_selected(member);
}));
}
));
// Search filter.
fn search_string(member: Member) -> String {
@ -134,11 +140,13 @@ mod imp {
self.filtered_model.set_filter(Some(&filter));
self.filtered_model.connect_items_changed(
clone!(@weak self as imp => move |_, _, _, _| {
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");
@ -158,32 +166,40 @@ mod imp {
.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 {
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 selected = obj
.imp()
.selected_members
.borrow()
.contains_key(member.user_id());
row.set_selected(selected);
}));
}));
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));
}
}
@ -201,15 +217,23 @@ mod imp {
self.permissions.set(permissions.as_ref());
if let Some(permissions) = &permissions {
self.power_level_filter.set_filter_func(clone!(@weak permissions => @default-return true, move |obj| {
let Some(member) = obj.downcast_ref::<Member>() else {
return false;
};
// Filter out members whose power level cannot be changed.
permissions
.can_do_to_user(member.user_id(), PowerLevelUserAction::ChangePowerLevel)
}));
self.power_level_filter.set_filter_func(clone!(
#[weak]
permissions,
#[upgrade_or]
true,
move |obj| {
let Some(member) = obj.downcast_ref::<Member>() else {
return false;
};
// Filter out members whose power level cannot be changed.
permissions.can_do_to_user(
member.user_id(),
PowerLevelUserAction::ChangePowerLevel,
)
}
));
}
let members = permissions

9
src/session/view/content/room_details/permissions/member_power_level.rs

@ -56,12 +56,15 @@ mod imp {
impl MemberPowerLevel {
/// Set the room member.
fn set_permissions(&self, permissions: &Permissions) {
let changed_handler =
permissions.connect_changed(clone!(@weak self as imp => move |_| {
let changed_handler = permissions.connect_changed(clone!(
#[weak(rename_to = imp)]
self,
move |_| {
imp.update_power_level();
imp.update_role();
imp.update_editable();
}));
}
));
self.permissions.set(permissions, vec![changed_handler]);
}

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save