From: Peter Michael Green <plugwash@debian.org>
Date: Thu, 25 Sep 2025 11:58:17 +0000
Subject: update-gtk-crates
MIME-Version: 1.0
Content-Type: text/plain; charset="utf-8"
Content-Transfer-Encoding: 8bit

This patch is based on the upstream commits described below adapted
for use in the Debian package by Peter Michael Green.

commit 27f81842a1c1f8a3178e3a006570b48b5b7c938b
Author: Maximiliano Sandoval <msandova@gnome.org>
Date:   Sun Jul 14 11:20:03 2024 +0200

    Use adw::Spinner

commit b2cbf3dbc1b15bb507f68aeeef9cb6c76b2cbe80
Author: Felix Häcker <haeckerfelix@gnome.org>
Date:   Sun Aug 3 14:53:48 2025 +0200

    misc: Upgrade gtk-rs to 0.21

commit 89296ea1ae3c4633f697f120c4e77e32a693a5f8
Author: Maximiliano Sandoval <msandova@gnome.org>
Date:   Sun Jul 14 12:58:35 2024 +0200

    Port to new clone macro
---
 Cargo.toml                        |   6 +-
 data/gtk/add_connection_dialog.ui |   5 +-
 data/gtk/connection_box.ui        |   5 +-
 data/gtk/torrent_row.ui           |   4 +-
 src/actions.rs                    | 402 ++++++++++++++++++++++++++------------
 src/app.rs                        | 322 +++++++++++++++++++-----------
 src/backend/connection.rs         |   2 +-
 src/backend/connection_manager.rs |  61 ++++--
 src/backend/daemon.rs             |  16 ++
 src/ui/add_connection_dialog.rs   |  66 ++++---
 src/ui/connection_box.rs          | 139 +++++++------
 src/ui/connection_popover.rs      |  47 +++--
 src/ui/connection_row.rs          |   5 +-
 src/ui/drag_overlay.rs            |  15 +-
 src/ui/file_page.rs               |   7 +-
 src/ui/file_row.rs                |  31 +--
 src/ui/folder_page.rs             |   5 +-
 src/ui/folder_page_contents.rs    |  47 +++--
 src/ui/preferences_dialog.rs      | 114 +++++++----
 src/ui/stats_dialog.rs            | 121 ++++++++----
 src/ui/status_header.rs           |   5 +-
 src/ui/torrent_dialog.rs          | 121 +++++++-----
 src/ui/torrent_group.rs           |  17 +-
 src/ui/torrent_page.rs            |  23 ++-
 src/ui/torrent_row.rs             |  50 +++--
 src/ui/window.rs                  | 189 +++++++++++-------
 src/utils.rs                      |  30 ++-
 27 files changed, 1188 insertions(+), 667 deletions(-)

diff --git a/Cargo.toml b/Cargo.toml
index 5ad9641..4db901f 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -7,7 +7,7 @@ license = "GPL-3.0-or-later"
 
 [dependencies]
 anyhow = "1.0"
-ashpd = { version = "0.11", default-features=false, features = ["gtk4", "async-std"] }
+ashpd = { version = "0.12", default-features=false, features = ["gtk4", "async-std"] }
 async-process = "2.2"
 async-recursion = "1.1"
 base64 = "0.22"
@@ -28,8 +28,8 @@ strum_macros = "0.26"
 url = "2.5"
 uuid = { version = "1.10", features = ["v4"] }
 
-gtk = { package = "gtk4", version = "0.9", features = ["gnome_47"] }
-adw = { package = "libadwaita", version = "0.7", features = ["v1_7"] }
+gtk = { package = "gtk4", version = "0.10", features = ["gnome_47"] }
+adw = { package = "libadwaita", version = "0.8", features = ["v1_7"] }
 
 [dependencies.transmission-gobject]
 version = "0.1.6"
diff --git a/data/gtk/add_connection_dialog.ui b/data/gtk/add_connection_dialog.ui
index 53262e2..0740827 100644
--- a/data/gtk/add_connection_dialog.ui
+++ b/data/gtk/add_connection_dialog.ui
@@ -133,10 +133,7 @@
               <object class="GtkStackPage">
                 <property name="name">loading</property>
                 <property name="child">
-                  <object class="GtkSpinner" id="spinner">
-                    <property name="halign">center</property>
-                    <property name="valign">center</property>
-                  </object>
+                  <object class="AdwSpinner" />
                 </property>
               </object>
             </child>
diff --git a/data/gtk/connection_box.ui b/data/gtk/connection_box.ui
index 133d09d..92b9b72 100644
--- a/data/gtk/connection_box.ui
+++ b/data/gtk/connection_box.ui
@@ -25,10 +25,7 @@
               <object class="GtkStackPage">
                 <property name="name">loading</property>
                 <property name="child">
-                  <object class="GtkSpinner" id="spinner">
-                    <property name="halign">center</property>
-                    <property name="width-request">32</property>
-                  </object>
+                  <object class="AdwSpinner" />
                 </property>
               </object>
             </child>
diff --git a/data/gtk/torrent_row.ui b/data/gtk/torrent_row.ui
index 9af2c33..5e1a62b 100644
--- a/data/gtk/torrent_row.ui
+++ b/data/gtk/torrent_row.ui
@@ -50,9 +50,7 @@
                   <object class="GtkStackPage">
                     <property name="name">check</property>
                     <property name="child">
-                      <object class="GtkSpinner" id="spinner">
-                        <property name="halign">center</property>
-                        <property name="valign">center</property>
+                      <object class="AdwSpinner">
                         <style>
                           <class name="bubble"/>
                         </style>
diff --git a/src/actions.rs b/src/actions.rs
index 1f4211a..a38890a 100644
--- a/src/actions.rs
+++ b/src/actions.rs
@@ -1,5 +1,5 @@
 // Fragments - actions.rs
-// Copyright (C) 2023-2024  Felix Häcker <haeckerfelix@gnome.org>
+// Copyright (C) 2023-2025  Felix Häcker <haeckerfelix@gnome.org>
 //
 // This program is free software: you can redistribute it and/or modify
 // it under the terms of the GNU General Public License as published by
@@ -38,119 +38,195 @@ pub fn install_torrent_actions(torrent: &TrTorrent, widget: gtk::Widget) {
 
     // torrent.continue
     let continue_action = SimpleAction::new("continue", None);
-    continue_action.connect_activate(clone!(@weak torrent => move |_, _| {
-        let fut = async move {
-            torrent.start(false).await.expect("Unable to start torrent");
-        };
-        glib::spawn_future_local(fut);
-    }));
+    continue_action.connect_activate(clone!(
+        #[weak]
+        torrent,
+        move |_, _| {
+            let fut = async move {
+                torrent.start(false).await.expect("Unable to start torrent");
+            };
+            glib::spawn_future_local(fut);
+        }
+    ));
     actions.add_action(&continue_action);
 
     // torrent.pause
     let pause_action = SimpleAction::new("pause", None);
-    pause_action.connect_activate(clone!(@weak torrent => move |_, _| {
-        let fut = async move {
-            torrent.stop().await.expect("Unable to stop torrent");
-        };
-        glib::spawn_future_local(fut);
-    }));
+    pause_action.connect_activate(clone!(
+        #[weak]
+        torrent,
+        move |_, _| {
+            let fut = async move {
+                torrent.stop().await.expect("Unable to stop torrent");
+            };
+            glib::spawn_future_local(fut);
+        }
+    ));
     actions.add_action(&pause_action);
 
     // Disable `continue`/`pause` action depending on the torrent status
-    torrent.connect_status_notify(
-        clone!(@weak torrent, @weak continue_action, @weak pause_action => move |_|{
+    torrent.connect_status_notify(clone!(
+        #[weak]
+        torrent,
+        #[weak]
+        continue_action,
+        #[weak]
+        pause_action,
+        move |_| {
             update_status_action(&torrent, &continue_action, &pause_action);
-        }),
-    );
+        }
+    ));
     update_status_action(torrent, &continue_action, &pause_action);
 
     // torrent.remove
     let remove_action = SimpleAction::new("remove", None);
-    remove_action.connect_activate(clone!(@weak torrent, @weak widget => move |_, _| {
-        show_remove_torrent_dialog(&torrent, widget);
-    }));
+    remove_action.connect_activate(clone!(
+        #[weak]
+        torrent,
+        #[weak]
+        widget,
+        move |_, _| {
+            show_remove_torrent_dialog(&torrent, widget);
+        }
+    ));
     actions.add_action(&remove_action);
 
     // torrent.copy-magnet
     let copy_magnet_action = SimpleAction::new("copy-magnet", None);
-    copy_magnet_action.connect_activate(clone!(@weak torrent, @weak widget => move |_, _| {
-        let display = gdk::Display::default().unwrap();
-        let clipboard = display.clipboard();
-
-        let content = gdk::ContentProvider::for_value(&torrent.magnet_link().to_value());
-        if let Err(err) = clipboard.set_content(Some(&content)){
-            utils::inapp_notification(&i18n("Unable to copy magnet link to clipboard"), Some(&widget));
-            warn!("Unable to copy magnet link to clipboard: {}", err.to_string());
-        }
+    copy_magnet_action.connect_activate(clone!(
+        #[weak]
+        torrent,
+        #[weak]
+        widget,
+        move |_, _| {
+            let display = gdk::Display::default().unwrap();
+            let clipboard = display.clipboard();
+
+            let content = gdk::ContentProvider::for_value(&torrent.magnet_link().to_value());
+            if let Err(err) = clipboard.set_content(Some(&content)) {
+                utils::inapp_notification(
+                    &i18n("Unable to copy magnet link to clipboard"),
+                    Some(&widget),
+                );
+                warn!(
+                    "Unable to copy magnet link to clipboard: {}",
+                    err.to_string()
+                );
+            }
 
-        utils::inapp_notification(&i18n("Copied magnet link to clipboard"), Some(&widget));
-    }));
+            utils::inapp_notification(&i18n("Copied magnet link to clipboard"), Some(&widget));
+        }
+    ));
     actions.add_action(&copy_magnet_action);
 
     // torrent.manual-update
     let manual_update_action = SimpleAction::new("manual-update", None);
-    manual_update_action.connect_activate(clone!(@weak torrent => move |_, _| {
-        let fut = async move {
-            torrent.reannounce().await.expect("Unable to reannounce torrent");
-        };
-        glib::spawn_future_local(fut);
-    }));
+    manual_update_action.connect_activate(clone!(
+        #[weak]
+        torrent,
+        move |_, _| {
+            let fut = async move {
+                torrent
+                    .reannounce()
+                    .await
+                    .expect("Unable to reannounce torrent");
+            };
+            glib::spawn_future_local(fut);
+        }
+    ));
     actions.add_action(&manual_update_action);
 
     // torrent.queue-up
     let queue_up_action = SimpleAction::new("queue-up", None);
-    queue_up_action.connect_activate(clone!(@weak torrent => move |_, _| {
-        move_queue(false, torrent);
-    }));
+    queue_up_action.connect_activate(clone!(
+        #[weak]
+        torrent,
+        move |_, _| {
+            move_queue(false, torrent);
+        }
+    ));
     actions.add_action(&queue_up_action);
 
     // torrent.queue-down
     let queue_down_action = SimpleAction::new("queue-down", None);
-    queue_down_action.connect_activate(clone!(@weak torrent => move |_, _| {
-        move_queue(true, torrent);
-    }));
+    queue_down_action.connect_activate(clone!(
+        #[weak]
+        torrent,
+        move |_, _| {
+            move_queue(true, torrent);
+        }
+    ));
     actions.add_action(&queue_down_action);
 
     // Disable queue actions for non queued torrents
-    torrent.connect_status_notify(
-        clone!(@weak torrent, @weak queue_up_action, @weak queue_down_action => move |_|{
+    torrent.connect_status_notify(clone!(
+        #[weak]
+        torrent,
+        #[weak]
+        queue_up_action,
+        #[weak]
+        queue_down_action,
+        move |_| {
             update_queue_action(&torrent, &queue_up_action, &queue_down_action);
-        }),
-    );
+        }
+    ));
     update_queue_action(torrent, &queue_up_action, &queue_down_action);
 
     // torrent.show-dialog
     let show_dialog_action = SimpleAction::new("show-dialog", None);
-    show_dialog_action.connect_activate(clone!(@weak torrent, @weak widget => move |_, _| {
-        let dialog = FrgTorrentDialog::new(&torrent);
-        dialog.present(Some(&widget));
-    }));
+    show_dialog_action.connect_activate(clone!(
+        #[weak]
+        torrent,
+        #[weak]
+        widget,
+        move |_, _| {
+            let dialog = FrgTorrentDialog::new(&torrent);
+            dialog.present(Some(&widget));
+        }
+    ));
     actions.add_action(&show_dialog_action);
 
     // torrent.open
     let open_action = SimpleAction::new("open", None);
-    open_action.connect_activate(clone!(@weak torrent, @weak widget => move |_, _| {
-        let name = if let Some(top_level) = torrent.files().top_level() {
-            top_level.name()
-        } else {
-            warn!("Top level file is not available, falling back to torrent name for opening");
-            torrent.name()
-        };
-
-        let path = Path::new(&torrent.download_dir()).join(name);
-        open(path, &widget.native().unwrap(), false, Some(widget));
-    }));
+    open_action.connect_activate(clone!(
+        #[weak]
+        torrent,
+        #[weak]
+        widget,
+        move |_, _| {
+            let name = if let Some(top_level) = torrent.files().top_level() {
+                top_level.name()
+            } else {
+                warn!("Top level file is not available, falling back to torrent name for opening");
+                torrent.name()
+            };
+
+            let path = Path::new(&torrent.download_dir()).join(name);
+            open(path, &widget.native().unwrap(), false, Some(widget));
+        }
+    ));
     actions.add_action(&open_action);
 
     // Only downloaded torrents can be opened
-    torrent.connect_downloaded_notify(clone!(@weak torrent, @weak open_action => move |_|{
-        update_torrent_open_action(&torrent, &open_action);
-    }));
+    torrent.connect_downloaded_notify(clone!(
+        #[weak]
+        torrent,
+        #[weak]
+        open_action,
+        move |_| {
+            update_torrent_open_action(&torrent, &open_action);
+        }
+    ));
     update_torrent_open_action(torrent, &open_action);
 
     // torrent.set-location
     let set_location_action = SimpleAction::new("set-location", None);
-    set_location_action.connect_activate(clone!(@weak torrent, @weak widget => move |_, _| {
+    set_location_action.connect_activate(clone!(
+        #[weak]
+        torrent,
+        #[weak]
+        widget,
+        move |_, _| {
             show_set_torrent_location(&torrent, &widget);
         }
     ));
@@ -158,12 +234,18 @@ pub fn install_torrent_actions(torrent: &TrTorrent, widget: gtk::Widget) {
     actions.add_action(&set_location_action);
 
     // Disable filesystem actions when connected with a remote session
-    current_connection.connect_is_fragments_notify(
-        clone!(@weak torrent, @weak open_action, @weak set_location_action => move |c|{
+    current_connection.connect_is_fragments_notify(clone!(
+        #[weak]
+        torrent,
+        #[weak]
+        open_action,
+        #[weak]
+        set_location_action,
+        move |c| {
             set_location_action.set_enabled(c.is_fragments());
             update_torrent_open_action(&torrent, &open_action);
-        }),
-    );
+        }
+    ));
 }
 
 pub fn install_file_actions(file: &TrFile, widget: gtk::Widget) {
@@ -174,44 +256,73 @@ pub fn install_file_actions(file: &TrFile, widget: gtk::Widget) {
 
     // file.open
     let open_action = SimpleAction::new("open", None);
-    open_action.connect_activate(clone!(@weak file, @weak widget => move |_, _| {
-        let path = Path::new(&file.torrent().download_dir()).join(file.name());
-        open(path, &widget.native().unwrap(), false, Some(widget));
-    }));
+    open_action.connect_activate(clone!(
+        #[weak]
+        file,
+        #[weak]
+        widget,
+        move |_, _| {
+            let path = Path::new(&file.torrent().download_dir()).join(file.name());
+            open(path, &widget.native().unwrap(), false, Some(widget));
+        }
+    ));
     actions.add_action(&open_action);
 
     // file.open-containing-folder
     let open_containing_action = SimpleAction::new("open-containing-folder", None);
-    open_containing_action.connect_activate(clone!(@weak file, @weak widget => move |_, _| {
-        let path = Path::new(&file.torrent().download_dir()).join(file.name());
-        open(path, &widget.native().unwrap(), true, Some(widget));
-    }));
+    open_containing_action.connect_activate(clone!(
+        #[weak]
+        file,
+        #[weak]
+        widget,
+        move |_, _| {
+            let path = Path::new(&file.torrent().download_dir()).join(file.name());
+            open(path, &widget.native().unwrap(), true, Some(widget));
+        }
+    ));
     actions.add_action(&open_containing_action);
 
     // Only downloaded files can be opened
-    file.connect_bytes_completed_notify(clone!(@weak file, @weak actions => move |_|{
-        update_file_open_actions(&file, &actions);
-    }));
+    file.connect_bytes_completed_notify(clone!(
+        #[weak]
+        file,
+        #[weak]
+        actions,
+        move |_| {
+            update_file_open_actions(&file, &actions);
+        }
+    ));
     update_file_open_actions(file, &actions);
 
     // Disable filesystem actions when connected with a remote session
-    current_connection.connect_is_fragments_notify(
-        clone!(@weak file, @weak open_action, @weak actions => move |c|{
+    current_connection.connect_is_fragments_notify(clone!(
+        #[weak]
+        file,
+        #[weak]
+        actions,
+        move |c| {
             open_containing_action.set_enabled(c.is_fragments());
             update_file_open_actions(&file, &actions);
-        }),
-    );
+        }
+    ));
 }
 
 fn move_queue(down: bool, torrent: TrTorrent) {
-    let fut = clone!(@weak torrent => async move {
-        let pos = if down{
-            torrent.download_queue_position() + 1
-        }else{
-            torrent.download_queue_position() - 1
-        };
-        torrent.set_download_queue_position(pos).await.expect("Unable to set download queue position");
-    });
+    let fut = clone!(
+        #[weak]
+        torrent,
+        async move {
+            let pos = if down {
+                torrent.download_queue_position() + 1
+            } else {
+                torrent.download_queue_position() - 1
+            };
+            torrent
+                .set_download_queue_position(pos)
+                .await
+                .expect("Unable to set download queue position");
+        }
+    );
     glib::spawn_future_local(fut);
 }
 
@@ -282,22 +393,36 @@ fn show_remove_torrent_dialog(torrent: &TrTorrent, parent: gtk::Widget) {
     check_button.set_halign(gtk::Align::Center);
     dialog.set_extra_child(Some(&check_button));
 
-    dialog.connect_response(None,
-        clone!(@strong dialog, @weak torrent, @weak parent, @weak check_button => move |_ , resp| {
-            if resp == "remove" {
-                let fut = async move {
-                    torrent.remove(check_button.is_active()).await.expect("Unable to remove torrent");
-                };
+    dialog.connect_response(
+        None,
+        clone!(
+            #[strong]
+            dialog,
+            #[weak]
+            torrent,
+            #[weak]
+            parent,
+            #[weak]
+            check_button,
+            move |_, resp| {
+                if resp == "remove" {
+                    let fut = async move {
+                        torrent
+                            .remove(check_button.is_active())
+                            .await
+                            .expect("Unable to remove torrent");
+                    };
+
+                    if let Ok(dialog) = parent.downcast::<adw::Dialog>() {
+                        dialog.close();
+                    }
 
-                if let Ok(dialog) = parent.downcast::<adw::Dialog>(){
-                    dialog.close();
+                    glib::spawn_future_local(fut);
                 }
 
-                glib::spawn_future_local(fut);
+                dialog.close();
             }
-
-            dialog.close();
-        }),
+        ),
     );
 
     dialog.present(Some(&parent));
@@ -312,7 +437,7 @@ fn show_set_torrent_location(torrent: &TrTorrent, parent: &gtk::Widget) {
     dialog.set_accept_label(Some(&i18n("_Select")));
 
     let root = parent.root().unwrap().downcast::<gtk::Window>().unwrap();
-    dialog.select_folder(Some(&root), gio::Cancellable::NONE, clone!(@weak torrent, @weak parent, @strong dialog => move |result| {
+    dialog.select_folder(Some(&root), gio::Cancellable::NONE, clone!(#[weak] torrent, #[weak] parent, #[strong(rename_to = _dialog)] dialog, move |result| {
         match result {
             Ok(folder) => {
                 debug!("Selected torrent directory: {:?}", folder.path());
@@ -330,9 +455,9 @@ fn show_set_torrent_location(torrent: &TrTorrent, parent: &gtk::Widget) {
                     dialog.present(Some(&parent));
                     dialog.connect_response(
                         None,
-                        clone!(@strong dialog, @strong torrent, @strong folder => move |_ , resp| {
+                        clone!(#[strong(rename_to = _dialog)] dialog, #[strong] torrent, #[strong] folder, move |_ , resp| {
                             let resp = resp.to_string();
-                            let fut = clone!(@strong resp, @strong torrent, @strong folder => async move {
+                            let fut = clone!(#[strong] resp, #[strong] torrent, #[strong] folder, async move {
                                 let _ = torrent.set_location(folder, resp == "move-data").await;
                             });
 
@@ -364,28 +489,51 @@ fn open(
     open_containing_folder: bool,
     widget: Option<gtk::Widget>,
 ) {
-    let fut = clone!(@strong path, @weak native, @strong open_containing_folder, @strong widget => async move {
-        debug!("Opening: {:?} (containing folder: {})", path, open_containing_folder);
-        match File::open(&path){
-            Ok(file) => {
-                let identifier = WindowIdentifier::from_native(&native).await;
-
-                let res = if open_containing_folder || path.is_dir() {
-                    OpenDirectoryRequest::default().identifier(identifier).send(&file.as_fd()).await
-                } else {
-                    OpenFileRequest::default().identifier(identifier).ask(true).send_file(&file.as_fd()).await
-                };
+    let fut = clone!(
+        #[strong]
+        path,
+        #[weak]
+        native,
+        #[strong]
+        open_containing_folder,
+        #[strong]
+        widget,
+        async move {
+            debug!(
+                "Opening: {:?} (containing folder: {})",
+                path, open_containing_folder
+            );
+            match File::open(&path) {
+                Ok(file) => {
+                    let identifier = WindowIdentifier::from_native(&native).await;
+
+                    let res = if open_containing_folder || path.is_dir() {
+                        OpenDirectoryRequest::default()
+                            .identifier(identifier)
+                            .send(&file.as_fd())
+                            .await
+                    } else {
+                        OpenFileRequest::default()
+                            .identifier(identifier)
+                            .ask(true)
+                            .send_file(&file.as_fd())
+                            .await
+                    };
 
-                if let Err(err) = res {
-                    warn!("Could not open folder / file: {}", err.to_string());
-                    utils::inapp_notification(&i18n("Unable to open file / folder"), widget.as_ref());
+                    if let Err(err) = res {
+                        warn!("Could not open folder / file: {}", err.to_string());
+                        utils::inapp_notification(
+                            &i18n("Unable to open file / folder"),
+                            widget.as_ref(),
+                        );
+                    }
+                }
+                Err(err) => {
+                    warn!("Could not open file: {}", err.to_string());
+                    utils::inapp_notification(&i18n("Could not open file"), widget.as_ref());
                 }
-            },
-            Err(err) => {
-                warn!("Could not open file: {}", err.to_string());
-                utils::inapp_notification(&i18n("Could not open file"), widget.as_ref());
             }
         }
-    });
+    );
     glib::spawn_future_local(fut);
 }
diff --git a/src/app.rs b/src/app.rs
index 992a547..7011c68 100644
--- a/src/app.rs
+++ b/src/app.rs
@@ -90,20 +90,24 @@ mod imp {
             }
 
             // Connect to last used connection
-            let fut = clone!(@weak self as this => async move {
-                let cm = FrgConnectionManager::default();
+            let fut = clone!(
+                #[weak(rename_to = this)]
+                self,
+                async move {
+                    let cm = FrgConnectionManager::default();
 
-                let local_connection = FrgConnection::default();
-                cm.connections().insert(0, &local_connection);
+                    let local_connection = FrgConnection::default();
+                    cm.connections().insert(0, &local_connection);
 
-                if let Err(err) = secret_store::open_keyring().await {
-                    error!("Could not unlock keyring: {err}");
-                };
+                    if let Err(err) = secret_store::open_keyring().await {
+                        error!("Could not unlock keyring: {err}");
+                    };
 
-                // Restore last used connection
-                let uuid = settings_manager::string(Key::ClientLastConnection);
-                this.set_connection(uuid).await;
-            });
+                    // Restore last used connection
+                    let uuid = settings_manager::string(Key::ClientLastConnection);
+                    this.set_connection(uuid).await;
+                }
+            );
             glib::spawn_future_local(fut);
 
             // No window available -> we have to create one
@@ -117,44 +121,60 @@ mod imp {
 
             // app.add-torrent
             let action = gio::SimpleAction::new("add-torrent", None);
-            action.connect_activate(clone!(@weak self as app => move |_, _| {
-                app.open_torrent_filechooser();
-            }));
+            action.connect_activate(clone!(
+                #[weak(rename_to = app)]
+                self,
+                move |_, _| {
+                    app.open_torrent_filechooser();
+                }
+            ));
             utils::bind_connected_property(&action);
             obj.add_action(&action);
             obj.set_accels_for_action("app.add-torrent", &["<primary>o"]);
 
             // app.add-link
             let action = gio::SimpleAction::new("add-link", string_ty);
-            action.connect_activate(clone!(@weak self as this => move |_, target| {
-                if let Some(torrent_link) = target.and_then(|x| x.get::<String>()) {
-                    // Both magnet and torrent links are supported
-                    this.add_torrent_by_link(torrent_link);
+            action.connect_activate(clone!(
+                #[weak(rename_to = this)]
+                self,
+                move |_, target| {
+                    if let Some(torrent_link) = target.and_then(|x| x.get::<String>()) {
+                        // Both magnet and torrent links are supported
+                        this.add_torrent_by_link(torrent_link);
+                    }
                 }
-            }));
+            ));
             utils::bind_connected_property(&action);
             obj.add_action(&action);
 
             // app.search
             let action = gio::SimpleAction::new("search", None);
-            action.connect_activate(clone!(@weak window => move |_, _| {
-                window.show_search(false);
-            }));
+            action.connect_activate(clone!(
+                #[weak]
+                window,
+                move |_, _| {
+                    window.show_search(false);
+                }
+            ));
             utils::bind_connected_property(&action);
             obj.add_action(&action);
             obj.set_accels_for_action("app.search", &["<primary>f"]);
 
             // app.toggle-search
             let action = gio::SimpleAction::new("toggle-search", None);
-            action.connect_activate(clone!(@weak window => move |_, _| {
-                window.show_search(true);
-            }));
+            action.connect_activate(clone!(
+                #[weak]
+                window,
+                move |_, _| {
+                    window.show_search(true);
+                }
+            ));
             utils::bind_connected_property(&action);
             obj.add_action(&action);
 
             // app.resume-torrents
             let action = gio::SimpleAction::new("resume-torrents", None);
-            action.connect_activate(clone!(@weak self as app => move |_, _| {
+            action.connect_activate(move |_, _| {
                 let fut = async move {
                     let client = FrgConnectionManager::default().client();
                     if let Err(err) = client.start_torrents().await {
@@ -162,12 +182,12 @@ mod imp {
                     };
                 };
                 glib::spawn_future_local(fut);
-            }));
+            });
             obj.add_action(&action);
 
             // app.pause-torrents
             let action = gio::SimpleAction::new("pause-torrents", None);
-            action.connect_activate(clone!(@weak self as app => move |_, _| {
+            action.connect_activate(move |_, _| {
                 let fut = async move {
                     let client = FrgConnectionManager::default().client();
                     if let Err(err) = client.stop_torrents().await {
@@ -175,73 +195,107 @@ mod imp {
                     };
                 };
                 glib::spawn_future_local(fut);
-
-            }));
+            });
             obj.add_action(&action);
 
             // app.remove-torrents
             let action = gio::SimpleAction::new("remove-torrents", None);
-            action.connect_activate(clone!(@weak self as this => move |_, _| {
-                this.show_remove_torrents_dialog();
-            }));
+            action.connect_activate(clone!(
+                #[weak(rename_to = this)]
+                self,
+                move |_, _| {
+                    this.show_remove_torrents_dialog();
+                }
+            ));
             obj.add_action(&action);
 
             // app.show-stats
             let action = gio::SimpleAction::new("show-stats", None);
-            action.connect_activate(clone!(@weak window => move |_, _| {
-                FrgStatsDialog::new().present(Some(&window));
-            }));
+            action.connect_activate(clone!(
+                #[weak]
+                window,
+                move |_, _| {
+                    FrgStatsDialog::new().present(Some(&window));
+                }
+            ));
             utils::bind_connected_property(&action);
             obj.add_action(&action);
 
             // app.set-connection
             let action = gio::SimpleAction::new("set-connection", string_ty);
-            action.connect_activate(clone!(@weak self as this => move |_, target| {
-                if let Some(connection_uuid) = target.and_then(|x| x.get::<String>()) {
-                    let fut = async move {
-                        this.set_connection(connection_uuid).await;
-                    };
-                    glib::spawn_future_local(fut);
+            action.connect_activate(clone!(
+                #[weak(rename_to = this)]
+                self,
+                move |_, target| {
+                    if let Some(connection_uuid) = target.and_then(|x| x.get::<String>()) {
+                        let fut = async move {
+                            this.set_connection(connection_uuid).await;
+                        };
+                        glib::spawn_future_local(fut);
+                    }
                 }
-            }));
+            ));
             obj.add_action(&action);
 
             // app.reconnect
             let action = gio::SimpleAction::new("reconnect", None);
-            action.connect_activate(clone!(@weak self as this => move |_, _| {
-                let cm = FrgConnectionManager::default();
-                this.obj().activate_action("set-connection", Some(&cm.current_connection().uuid().to_variant()));
-            }));
+            action.connect_activate(clone!(
+                #[weak(rename_to = this)]
+                self,
+                move |_, _| {
+                    let cm = FrgConnectionManager::default();
+                    this.obj().activate_action(
+                        "set-connection",
+                        Some(&cm.current_connection().uuid().to_variant()),
+                    );
+                }
+            ));
             obj.add_action(&action);
 
             // app.add-remote-connection
             let action = gio::SimpleAction::new("add-remote-connection", None);
-            action.connect_activate(clone!(@weak window => move |_, _| {
-                let dialog = FrgAddConnectionDialog::new();
-                dialog.present(Some(&window));
-            }));
+            action.connect_activate(clone!(
+                #[weak]
+                window,
+                move |_, _| {
+                    let dialog = FrgAddConnectionDialog::new();
+                    dialog.present(Some(&window));
+                }
+            ));
             obj.add_action(&action);
 
             // app.show-preferences
             let action = gio::SimpleAction::new("show-preferences", None);
-            action.connect_activate(clone!(@weak window => move |_, _| {
-                FrgPreferencesDialog::default().present(Some(&window));
-            }));
+            action.connect_activate(clone!(
+                #[weak]
+                window,
+                move |_, _| {
+                    FrgPreferencesDialog::default().present(Some(&window));
+                }
+            ));
             obj.set_accels_for_action("app.show-preferences", &["<primary>comma"]);
             obj.add_action(&action);
 
             // app.about
             let action = gio::SimpleAction::new("about", None);
-            action.connect_activate(clone!(@weak window => move |_, _| {
-                about_dialog::show(&window);
-            }));
+            action.connect_activate(clone!(
+                #[weak]
+                window,
+                move |_, _| {
+                    about_dialog::show(&window);
+                }
+            ));
             obj.add_action(&action);
 
             // app.quit
             let action = gio::SimpleAction::new("quit", None);
-            action.connect_activate(clone!(@weak window => move |_, _| {
-                window.close();
-            }));
+            action.connect_activate(clone!(
+                #[weak]
+                window,
+                move |_, _| {
+                    window.close();
+                }
+            ));
             obj.set_accels_for_action("app.quit", &["<primary>q"]);
             obj.add_action(&action);
 
@@ -249,25 +303,45 @@ mod imp {
 
             // Update state of `pause-torrents` and `remove-torrents` action
             let stats = FrgConnectionManager::default().client().session_stats();
-            stats.connect_active_torrent_count_notify(clone!(@weak self as this => move |_|{
-                this.obj().update_inhibit_state();
-            }));
-            stats.connect_downloaded_torrent_count_notify(clone!(@weak self as this => move |_|{
-                this.update_session_gactions();
-                this.obj().update_inhibit_state();
-            }));
-            stats.connect_paused_torrent_count_notify(clone!(@weak self as this => move |_|{
-                this.update_session_gactions();
-                this.obj().update_inhibit_state();
-            }));
-            stats.connect_torrent_count_notify(clone!(@weak self as this => move |_|{
-                this.update_session_gactions();
-            }));
+            stats.connect_active_torrent_count_notify(clone!(
+                #[weak(rename_to = this)]
+                self,
+                move |_| {
+                    this.obj().update_inhibit_state();
+                }
+            ));
+            stats.connect_downloaded_torrent_count_notify(clone!(
+                #[weak(rename_to = this)]
+                self,
+                move |_| {
+                    this.update_session_gactions();
+                    this.obj().update_inhibit_state();
+                }
+            ));
+            stats.connect_paused_torrent_count_notify(clone!(
+                #[weak(rename_to = this)]
+                self,
+                move |_| {
+                    this.update_session_gactions();
+                    this.obj().update_inhibit_state();
+                }
+            ));
+            stats.connect_torrent_count_notify(clone!(
+                #[weak(rename_to = this)]
+                self,
+                move |_| {
+                    this.update_session_gactions();
+                }
+            ));
 
             let client = FrgConnectionManager::default().client();
-            client.connect_is_connected_notify(clone!(@weak self as this => move |_|{
-                this.update_session_gactions();
-            }));
+            client.connect_is_connected_notify(clone!(
+                #[weak(rename_to = this)]
+                self,
+                move |_| {
+                    this.update_session_gactions();
+                }
+            ));
 
             self.update_session_gactions();
         }
@@ -285,8 +359,14 @@ mod imp {
                 // Wait till we're connected with something to add the files
                 let signal_id: Rc<RefCell<Option<glib::SignalHandlerId>>> = Rc::default();
                 let files = files.to_vec().clone();
-                *signal_id.borrow_mut() = Some(client.connect_is_connected_notify(
-                    clone!(@weak application, @strong signal_id, @strong files => move |client| {
+                *signal_id.borrow_mut() = Some(client.connect_is_connected_notify(clone!(
+                    #[weak]
+                    application,
+                    #[strong]
+                    signal_id,
+                    #[strong]
+                    files,
+                    move |client| {
                         if client.is_connected() {
                             application.add_torrents_from_files(&files);
 
@@ -294,8 +374,8 @@ mod imp {
                             let signal_id = signal_id.borrow_mut().take().unwrap();
                             glib::object::ObjectExt::disconnect(client, signal_id);
                         }
-                    }),
-                ));
+                    }
+                )));
             }
         }
     }
@@ -387,32 +467,46 @@ mod imp {
                 }
             }
 
-            let fut = clone!(@strong file => async move {
-                match fs::read(&file).await {
-                    Ok(content) => {
-                        let encoded = STANDARD.encode(content);
-
-                        let cm = FrgConnectionManager::default();
-                        let res = cm.add_torrent_by_metainfo(encoded).await;
-                        if let Err(err) = res {
-                            debug!("Could not add file torrent: {:?}", err);
-                            utils::inapp_notification("Unable to add file torrent", gtk::Widget::NONE);
-                        }
+            let fut = clone!(
+                #[strong]
+                file,
+                async move {
+                    match fs::read(&file).await {
+                        Ok(content) => {
+                            let encoded = STANDARD.encode(content);
+
+                            let cm = FrgConnectionManager::default();
+                            let res = cm.add_torrent_by_metainfo(encoded).await;
+                            if let Err(err) = res {
+                                debug!("Could not add file torrent: {:?}", err);
+                                utils::inapp_notification(
+                                    "Unable to add file torrent",
+                                    gtk::Widget::NONE,
+                                );
+                            }
 
-                        if settings_manager::boolean(Key::TrashOriginalTorrentFiles){
-                            let gfile = gio::File::for_path(file);
-                            if let Err(err) = gfile.trash_future(glib::Priority::DEFAULT).await {
-                                debug!("Unable to trash torrent file: {:?}", err);
-                                utils::inapp_notification("Unable to trash torrent file", gtk::Widget::NONE);
+                            if settings_manager::boolean(Key::TrashOriginalTorrentFiles) {
+                                let gfile = gio::File::for_path(file);
+                                if let Err(err) = gfile.trash_future(glib::Priority::DEFAULT).await
+                                {
+                                    debug!("Unable to trash torrent file: {:?}", err);
+                                    utils::inapp_notification(
+                                        "Unable to trash torrent file",
+                                        gtk::Widget::NONE,
+                                    );
+                                }
                             }
                         }
-                    }
-                    Err(err) => {
-                        debug!("Unable to read file content: {:?}", err);
-                        utils::inapp_notification("Unable to read file content", gtk::Widget::NONE);
+                        Err(err) => {
+                            debug!("Unable to read file content: {:?}", err);
+                            utils::inapp_notification(
+                                "Unable to read file content",
+                                gtk::Widget::NONE,
+                            );
+                        }
                     }
                 }
-            });
+            );
 
             glib::spawn_future_local(fut);
         }
@@ -460,16 +554,22 @@ mod imp {
             dialog.open_multiple(
                 Some(&window),
                 gio::Cancellable::NONE,
-                clone!(@strong dialog, @weak self as this => move |result| {
-                    if let Ok(files) = result {
-                        let files = files
-                            .snapshot()
-                            .iter()
-                            .map(|o| o.downcast_ref::<gio::File>().unwrap().clone())
-                            .collect();
-                        this.obj().add_torrents_from_files(&files);
+                clone!(
+                    #[strong(rename_to = _dialog)]
+                    dialog,
+                    #[weak(rename_to = this)]
+                    self,
+                    move |result| {
+                        if let Ok(files) = result {
+                            let files = files
+                                .snapshot()
+                                .iter()
+                                .map(|o| o.downcast_ref::<gio::File>().unwrap().clone())
+                                .collect();
+                            this.obj().add_torrents_from_files(&files);
+                        }
                     }
-                }),
+                ),
             );
         }
 
diff --git a/src/backend/connection.rs b/src/backend/connection.rs
index 614957d..82dd317 100644
--- a/src/backend/connection.rs
+++ b/src/backend/connection.rs
@@ -1,5 +1,5 @@
 // Fragments - connection.rs
-// Copyright (C) 2022-2023  Felix Häcker <haeckerfelix@gnome.org>
+// Copyright (C) 2022-2025  Felix Häcker <haeckerfelix@gnome.org>
 //
 // This program is free software: you can redistribute it and/or modify
 // it under the terms of the GNU General Public License as published by
diff --git a/src/backend/connection_manager.rs b/src/backend/connection_manager.rs
index d747aeb..b7e2ac8 100644
--- a/src/backend/connection_manager.rs
+++ b/src/backend/connection_manager.rs
@@ -1,5 +1,5 @@
 // Fragments - connection_manager.rs
-// Copyright (C) 2022-2024  Felix Häcker <haeckerfelix@gnome.org>
+// Copyright (C) 2022-2025  Felix Häcker <haeckerfelix@gnome.org>
 //
 // This program is free software: you can redistribute it and/or modify
 // it under the terms of the GNU General Public License as published by
@@ -112,15 +112,22 @@ mod imp {
             self.parent_constructed();
 
             let nm = gio::NetworkMonitor::default();
-            nm.connect_network_metered_notify(clone!(@weak self as this => move |_| {
-                this.check_for_stop_reason();
-            }));
+            nm.connect_network_metered_notify(clone!(
+                #[weak(rename_to = this)]
+                self,
+                move |_| {
+                    this.check_for_stop_reason();
+                }
+            ));
 
             self.read_connections();
-            self.connections
-                .connect_items_changed(clone!(@weak self as this => move |_, _, _, _|{
+            self.connections.connect_items_changed(clone!(
+                #[weak(rename_to = this)]
+                self,
+                move |_, _, _, _| {
                     this.write_connections();
-                }));
+                }
+            ));
         }
 
         fn signals() -> &'static [Signal] {
@@ -214,22 +221,38 @@ mod imp {
                 }
 
                 info!("Reason for stopping daemon: {:?}", reason);
-                let fut = clone!(@weak self as this, @strong reason => async move {
-                    let res = this.stop_daemon(&reason).await;
-                    if let Err(e) = res {
-                        utils::inapp_notification(&i18n("Unable to stop transmission-daemon"), gtk::Widget::NONE);
-                        error!("Unable to stop transmission-daemon: {}", e.to_string());
+                let fut = clone!(
+                    #[weak(rename_to = this)]
+                    self,
+                    #[strong]
+                    reason,
+                    async move {
+                        let res = this.stop_daemon(&reason).await;
+                        if let Err(e) = res {
+                            utils::inapp_notification(
+                                &i18n("Unable to stop transmission-daemon"),
+                                gtk::Widget::NONE,
+                            );
+                            error!("Unable to stop transmission-daemon: {}", e.to_string());
+                        }
                     }
-                });
+                );
                 glib::spawn_future_local(fut);
             } else if stop_reason.is_none() && is_fragments {
-                let fut = clone!(@weak self as this => async move {
-                    let res = this.start_daemon().await;
-                    if let Err(e) = res {
-                        utils::inapp_notification(&i18n("Unable to start transmission-daemon"), gtk::Widget::NONE);
-                        error!("Unable to start transmission-daemon: {}", e.to_string());
+                let fut = clone!(
+                    #[weak(rename_to = this)]
+                    self,
+                    async move {
+                        let res = this.start_daemon().await;
+                        if let Err(e) = res {
+                            utils::inapp_notification(
+                                &i18n("Unable to start transmission-daemon"),
+                                gtk::Widget::NONE,
+                            );
+                            error!("Unable to start transmission-daemon: {}", e.to_string());
+                        }
                     }
-                });
+                );
                 glib::spawn_future_local(fut);
             }
         }
diff --git a/src/backend/daemon.rs b/src/backend/daemon.rs
index 8afee2e..61a7179 100644
--- a/src/backend/daemon.rs
+++ b/src/backend/daemon.rs
@@ -1,3 +1,19 @@
+// Fragments - daemon.rs
+// Copyright (C) 2022-2025  Felix Häcker <haeckerfelix@gnome.org>
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program.  If not, see <https://www.gnu.org/licenses/>.
+
 use std::cell::RefCell;
 use std::fs;
 use std::path::Path;
diff --git a/src/ui/add_connection_dialog.rs b/src/ui/add_connection_dialog.rs
index b83597d..2984bf9 100644
--- a/src/ui/add_connection_dialog.rs
+++ b/src/ui/add_connection_dialog.rs
@@ -1,5 +1,5 @@
 // Fragments - add_connection_dialog.rs
-// Copyright (C) 2022-2023  Felix Häcker <haeckerfelix@gnome.org>
+// Copyright (C) 2022-2025  Felix Häcker <haeckerfelix@gnome.org>
 //
 // This program is free software: you can redistribute it and/or modify
 // it under the terms of the GNU General Public License as published by
@@ -48,8 +48,6 @@ mod imp {
         connect_button: TemplateChild<gtk::Button>,
         #[template_child]
         error_label: TemplateChild<gtk::Label>,
-        #[template_child]
-        spinner: TemplateChild<gtk::Spinner>,
 
         title_ok: Cell<bool>,
         address_ok: Cell<bool>,
@@ -91,36 +89,41 @@ mod imp {
 
         #[template_callback]
         fn connect_button_clicked(&self) {
-            let fut = clone!(@weak self as this => async move {
-                let title = this.title_row.text();
-                let address = this.address.borrow().clone();
-
-                let connection = FrgConnection::new(&title, &address);
-
-                this.stack.set_visible_child_name("loading");
-                this.spinner.set_spinning(true);
-                this.connect_button.set_sensitive(false);
-
-                let res = TrClient::test_connectivity(connection.address(), None).await;
-                match res {
-                    Ok(_) => this.add_connection(connection),
-                    Err(ref err) => {
-                        // Skip "Unauthorized" errors since we can handle those
-                        if matches!(err, ClientError::TransmissionUnauthorized) {
-                            this.add_connection(connection);
-                        } else {
-                            let msg = i18n_f("Could not connect with “{}”:\n{}", &[&this.address.borrow(), &err.to_string()]);
-
-                            this.error_label.set_visible(true);
-                            this.error_label.set_text(&msg);
-
-                            this.stack.set_visible_child_name("input");
-                            this.spinner.set_spinning(false);
-                            this.connect_button.set_sensitive(true);
+            let fut = clone!(
+                #[weak(rename_to = this)]
+                self,
+                async move {
+                    let title = this.title_row.text();
+                    let address = this.address.borrow().clone();
+
+                    let connection = FrgConnection::new(&title, &address);
+
+                    this.stack.set_visible_child_name("loading");
+                    this.connect_button.set_sensitive(false);
+
+                    let res = TrClient::test_connectivity(connection.address(), None).await;
+                    match res {
+                        Ok(_) => this.add_connection(connection),
+                        Err(ref err) => {
+                            // Skip "Unauthorized" errors since we can handle those
+                            if matches!(err, ClientError::TransmissionUnauthorized) {
+                                this.add_connection(connection);
+                            } else {
+                                let msg = i18n_f(
+                                    "Could not connect with “{}”:\n{}",
+                                    &[&this.address.borrow(), &err.to_string()],
+                                );
+
+                                this.error_label.set_visible(true);
+                                this.error_label.set_text(&msg);
+
+                                this.stack.set_visible_child_name("input");
+                                this.connect_button.set_sensitive(true);
+                            }
                         }
                     }
                 }
-            });
+            );
             glib::spawn_future_local(fut);
         }
 
@@ -179,7 +182,8 @@ mod imp {
 
 glib::wrapper! {
     pub struct FrgAddConnectionDialog(ObjectSubclass<imp::FrgAddConnectionDialog>)
-        @extends gtk::Widget, adw::Dialog;
+        @extends gtk::Widget, adw::Dialog,
+        @implements gtk::Accessible, gtk::Buildable, gtk::ConstraintTarget;
 }
 
 impl FrgAddConnectionDialog {
diff --git a/src/ui/connection_box.rs b/src/ui/connection_box.rs
index 711ae6f..3d528db 100644
--- a/src/ui/connection_box.rs
+++ b/src/ui/connection_box.rs
@@ -1,5 +1,5 @@
 // Fragments - connection_box.rs
-// Copyright (C) 2022-2024  Felix Häcker <haeckerfelix@gnome.org>
+// Copyright (C) 2022-2025  Felix Häcker <haeckerfelix@gnome.org>
 //
 // This program is free software: you can redistribute it and/or modify
 // it under the terms of the GNU General Public License as published by
@@ -49,8 +49,6 @@ mod imp {
         #[template_child]
         pub torrent_page: TemplateChild<FrgTorrentPage>,
         #[template_child]
-        spinner: TemplateChild<gtk::Spinner>,
-        #[template_child]
         welcome_status_page: TemplateChild<adw::StatusPage>,
         #[template_child]
         failure_status_page: TemplateChild<adw::StatusPage>,
@@ -99,51 +97,70 @@ mod imp {
             self.set_view(View::Loading);
 
             // Show a message when the currently configured directory is not accessible
-            cm.connect_invalid_dir_notify(clone!(@weak self as this => move |cm|{
-                let res: Option<String> = match cm.invalid_dir(){
-                    FrgDirectoryType::Download => Some(
-                        i18n("The configured download directory cannot be accessed."),
-                    ),
-                    FrgDirectoryType::Incomplete => Some(
-                        i18n("The configured incomplete directory cannot be accessed."),
-                    ),
-                    _ => None,
-                };
+            cm.connect_invalid_dir_notify(clone!(
+                #[weak(rename_to = this)]
+                self,
+                move |cm| {
+                    let res: Option<String> = match cm.invalid_dir() {
+                        FrgDirectoryType::Download => Some(i18n(
+                            "The configured download directory cannot be accessed.",
+                        )),
+                        FrgDirectoryType::Incomplete => Some(i18n(
+                            "The configured incomplete directory cannot be accessed.",
+                        )),
+                        _ => None,
+                    };
 
-                if let Some(res) = res{
-                    this.invalid_dir_banner.set_revealed(true);
-                    this.invalid_dir_banner.set_title(&res);
-                }else{
-                    this.invalid_dir_banner.set_revealed(false);
+                    if let Some(res) = res {
+                        this.invalid_dir_banner.set_revealed(true);
+                        this.invalid_dir_banner.set_title(&res);
+                    } else {
+                        this.invalid_dir_banner.set_revealed(false);
+                    }
                 }
-            }));
+            ));
 
             cm.connect_local(
                 "daemon-started",
                 false,
-                clone!(@weak self as this => @default-panic, move |_| {
-                    this.update_torrents_view();
-                    None
-                }),
+                clone!(
+                    #[weak(rename_to = this)]
+                    self,
+                    #[upgrade_or_panic]
+                    move |_| {
+                        this.update_torrents_view();
+                        None
+                    }
+                ),
             );
 
             cm.connect_local(
                 "daemon-stopped",
                 false,
-                clone!(@weak self as this => @default-panic, move |val| {
-                    let reason = val[1].get::<FrgDaemonStopReason>().unwrap();
-                    this.show_stop_reason(reason);
-                    None
-                }),
+                clone!(
+                    #[weak(rename_to = this)]
+                    self,
+                    #[upgrade_or_panic]
+                    move |val| {
+                        let reason = val[1].get::<FrgDaemonStopReason>().unwrap();
+                        this.show_stop_reason(reason);
+                        None
+                    }
+                ),
             );
 
             client.torrents().connect_local(
                 "items-changed",
                 false,
-                clone!(@weak self as this => @default-panic, move |_| {
-                    this.update_torrents_view();
-                    None
-                }),
+                clone!(
+                    #[weak(rename_to = this)]
+                    self,
+                    #[upgrade_or_panic]
+                    move |_| {
+                        this.update_torrents_view();
+                        None
+                    }
+                ),
             );
 
             client.connect_local("connection-failure", false, move |_| {
@@ -165,31 +182,35 @@ mod imp {
             let drop_target = self.drag_overlay.drop_target();
             drop_target.set_types(&[gdk::FileList::static_type()]);
 
-            drop_target.connect_accept(clone!(@weak client => @default-return false, move |_, _| {
-                // Only accept drops when we're connected to a Transmission session
-                client.is_connected()
-            }));
-
-            drop_target.connect_drop(
-                clone!(@weak cm => @default-return false, move |_, value, _, _| {
-                    let files = match value.get::<gdk::FileList>() {
-                        Ok(list) => list.files(),
-                        Err(err) => {
-                            error!("Issue with drop value: {err}");
-                            return false;
-                        }
-                    };
+            drop_target.connect_accept(clone!(
+                #[weak]
+                client,
+                #[upgrade_or]
+                false,
+                move |_, _| {
+                    // Only accept drops when we're connected to a Transmission session
+                    client.is_connected()
+                }
+            ));
 
-                    if !files.is_empty() {
-                        let app = FrgApplication::default();
-                        app.add_torrents_from_files(&files);
-                        return true;
-                    } else {
-                        error!("Dropped FileList was empty");
+            drop_target.connect_drop(move |_, value, _, _| {
+                let files = match value.get::<gdk::FileList>() {
+                    Ok(list) => list.files(),
+                    Err(err) => {
+                        error!("Issue with drop value: {err}");
                         return false;
                     }
-                }),
-            );
+                };
+
+                if !files.is_empty() {
+                    let app = FrgApplication::default();
+                    app.add_torrents_from_files(&files);
+                    return true;
+                } else {
+                    error!("Dropped FileList was empty");
+                    return false;
+                }
+            });
 
             self.stack.add_controller(drop_target.clone());
         }
@@ -236,8 +257,6 @@ mod imp {
         }
 
         pub fn set_view(&self, view: View) {
-            self.spinner.set_spinning(false);
-
             let name = match view {
                 View::Authentication => {
                     // Unset previous entry text
@@ -245,10 +264,7 @@ mod imp {
                     self.password_row.set_text("");
                     "authentication"
                 }
-                View::Loading => {
-                    self.spinner.set_spinning(true);
-                    "loading"
-                }
+                View::Loading => "loading",
                 View::Ready => "ready",
                 View::Torrents => "torrents",
                 View::Metered => "metered",
@@ -300,7 +316,8 @@ mod imp {
 glib::wrapper! {
     pub struct FrgConnectionBox(
         ObjectSubclass<imp::FrgConnectionBox>)
-        @extends gtk::Widget, gtk::Box;
+        @extends gtk::Widget, gtk::Box,
+        @implements gtk::Accessible, gtk::Buildable, gtk::ConstraintTarget;
 }
 
 impl FrgConnectionBox {
diff --git a/src/ui/connection_popover.rs b/src/ui/connection_popover.rs
index 3730dd9..1a8b5ea 100644
--- a/src/ui/connection_popover.rs
+++ b/src/ui/connection_popover.rs
@@ -1,5 +1,5 @@
 // Fragments - connection_popover.rs
-// Copyright (C) 2022-2023  Felix Häcker <haeckerfelix@gnome.org>
+// Copyright (C) 2022-2025  Felix Häcker <haeckerfelix@gnome.org>
 //
 // This program is free software: you can redistribute it and/or modify
 // it under the terms of the GNU General Public License as published by
@@ -53,14 +53,20 @@ mod imp {
             self.parent_constructed();
             let cm = FrgConnectionManager::default();
 
-            self.listbox
-                .connect_row_activated(clone!(@weak self as this => move |_, row|{
+            self.listbox.connect_row_activated(clone!(
+                #[weak(rename_to = this)]
+                self,
+                move |_, row| {
                     let row = row.downcast_ref::<FrgConnectionRow>().unwrap();
                     let app = FrgApplication::default();
 
-                    app.activate_action("set-connection", Some(&row.connection().uuid().to_variant()));
+                    app.activate_action(
+                        "set-connection",
+                        Some(&row.connection().uuid().to_variant()),
+                    );
                     this.obj().popdown();
-                }));
+                }
+            ));
 
             self.listbox
                 .bind_model(Some(&cm.connections()), |connection| {
@@ -68,21 +74,31 @@ mod imp {
                         .upcast()
                 });
 
-            cm.connect_current_connection_notify(clone!(@weak self as this => move |_|{
-                this.update_ui();
-            }));
+            cm.connect_current_connection_notify(clone!(
+                #[weak(rename_to = this)]
+                self,
+                move |_| {
+                    this.update_ui();
+                }
+            ));
 
-            cm.client()
-                .connect_is_busy_notify(clone!(@weak self as this => move |client|{
+            cm.client().connect_is_busy_notify(clone!(
+                #[weak(rename_to = this)]
+                self,
+                move |client| {
                     // Don't allow changing the active connection
                     // when the client is still busy establishing a connection
                     this.obj().set_sensitive(!client.is_busy());
-                }));
+                }
+            ));
 
-            cm.connections()
-                .connect_items_changed(clone!(@weak self as this => move |_,_,_,_|{
+            cm.connections().connect_items_changed(clone!(
+                #[weak(rename_to = this)]
+                self,
+                move |_, _, _, _| {
                     this.update_ui();
-                }));
+                }
+            ));
 
             self.update_ui();
         }
@@ -111,5 +127,6 @@ mod imp {
 glib::wrapper! {
     pub struct FrgConnectionPopover(
         ObjectSubclass<imp::FrgConnectionPopover>)
-        @extends gtk::Widget, gtk::Popover;
+        @extends gtk::Widget, gtk::Popover,
+        @implements gtk::Native, gtk::ShortcutManager, gtk::Accessible, gtk::Buildable, gtk::ConstraintTarget;
 }
diff --git a/src/ui/connection_row.rs b/src/ui/connection_row.rs
index ef477be..3a3cb7b 100644
--- a/src/ui/connection_row.rs
+++ b/src/ui/connection_row.rs
@@ -1,5 +1,5 @@
 // Fragments - connection_row.rs
-// Copyright (C) 2022-2023  Felix Häcker <haeckerfelix@gnome.org>
+// Copyright (C) 2022-2025  Felix Häcker <haeckerfelix@gnome.org>
 //
 // This program is free software: you can redistribute it and/or modify
 // it under the terms of the GNU General Public License as published by
@@ -126,7 +126,8 @@ mod imp {
 glib::wrapper! {
     pub struct FrgConnectionRow(
         ObjectSubclass<imp::FrgConnectionRow>)
-        @extends gtk::Widget, gtk::ListBoxRow;
+        @extends gtk::Widget, gtk::ListBoxRow,
+        @implements gtk::Actionable, gtk::Accessible, gtk::Buildable, gtk::ConstraintTarget;
 }
 
 impl FrgConnectionRow {
diff --git a/src/ui/drag_overlay.rs b/src/ui/drag_overlay.rs
index ad82315..ad5f20c 100644
--- a/src/ui/drag_overlay.rs
+++ b/src/ui/drag_overlay.rs
@@ -1,5 +1,5 @@
 // Fragments - drag_overlay.rs
-// Copyright (C) 2023  Felix Häcker <haeckerfelix@gnome.org>
+// Copyright (C) 2022-2025  Felix Häcker <haeckerfelix@gnome.org>
 //
 // This program is free software: you can redistribute it and/or modify
 // it under the terms of the GNU General Public License as published by
@@ -57,12 +57,14 @@ mod imp {
             self.revealer.set_reveal_child(false);
 
             self.drop_target.set_actions(gtk::gdk::DragAction::COPY);
-            self.drop_target.connect_current_drop_notify(
-                glib::clone!(@weak self.revealer as revealer => move |target| {
+            self.drop_target.connect_current_drop_notify(glib::clone!(
+                #[weak(rename_to = revealer)]
+                self.revealer,
+                move |target| {
                     let reveal = target.current_drop().is_some();
                     revealer.set_reveal_child(reveal);
-                }),
-            );
+                }
+            ));
 
             let bin = adw::Bin::new();
             bin.set_can_target(false);
@@ -90,5 +92,6 @@ mod imp {
 glib::wrapper! {
     pub struct FrgDragOverlay(
         ObjectSubclass<imp::FrgDragOverlay>)
-        @extends gtk::Widget, adw::Bin;
+        @extends gtk::Widget, adw::Bin,
+        @implements gtk::Accessible, gtk::Buildable, gtk::ConstraintTarget;
 }
diff --git a/src/ui/file_page.rs b/src/ui/file_page.rs
index b4bcb5c..6f4092c 100644
--- a/src/ui/file_page.rs
+++ b/src/ui/file_page.rs
@@ -1,5 +1,5 @@
 // Fragments - file_page.rs
-// Copyright (C) 2023  Felix Häcker <haeckerfelix@gnome.org>
+// Copyright (C) 2022-2025  Felix Häcker <haeckerfelix@gnome.org>
 //
 // This program is free software: you can redistribute it and/or modify
 // it under the terms of the GNU General Public License as published by
@@ -66,7 +66,7 @@ mod imp {
 
             self.status_header.set_title(file.title());
 
-            let mimetype = gio::content_type_guess(Some(&file.name()), &[])
+            let mimetype = gio::content_type_guess(Some(&file.name()), None)
                 .0
                 .to_string();
             self.status_header.set_mimetype(mimetype);
@@ -106,7 +106,8 @@ mod imp {
 
 glib::wrapper! {
     pub struct FrgFilePage(ObjectSubclass<imp::FrgFilePage>)
-        @extends gtk::Widget, adw::NavigationPage;
+        @extends gtk::Widget, adw::NavigationPage,
+        @implements gtk::Accessible, gtk::Buildable, gtk::ConstraintTarget;
 }
 
 impl FrgFilePage {
diff --git a/src/ui/file_row.rs b/src/ui/file_row.rs
index 48226e4..6552373 100644
--- a/src/ui/file_row.rs
+++ b/src/ui/file_row.rs
@@ -1,5 +1,5 @@
 // Fragments - file_row.rs
-// Copyright (C) 2022-2023  Felix Häcker <haeckerfelix@gnome.org>
+// Copyright (C) 2022-2025  Felix Häcker <haeckerfelix@gnome.org>
 //
 // This program is free software: you can redistribute it and/or modify
 // it under the terms of the GNU General Public License as published by
@@ -106,7 +106,7 @@ mod imp {
                     self.mimetype_image.set_icon_name(Some("folder-symbolic"));
                     self.folder_arrow_image.set_visible(true);
                 } else {
-                    let mimetype = gio::content_type_guess(Some(&file.name()), &[])
+                    let mimetype = gio::content_type_guess(Some(&file.name()), None)
                         .0
                         .to_string();
                     utils::set_mimetype_image(&mimetype, &self.mimetype_image.get())
@@ -142,19 +142,27 @@ mod imp {
                 // Progress & Subtitle
                 let id = file.connect_notify_local(
                     Some("bytes-completed"),
-                    clone!(@weak self as this => move|_,_|{
-                        this.update_progressbar();
-                        this.update_subtitle();
-                    }),
+                    clone!(
+                        #[weak(rename_to = this)]
+                        self,
+                        move |_, _| {
+                            this.update_progressbar();
+                            this.update_subtitle();
+                        }
+                    ),
                 );
                 self.signal_handlers.borrow_mut().push(id);
 
                 let id = file.connect_notify_local(
                     Some("wanted"),
-                    clone!(@weak self as this => move|_,_|{
-                        this.update_progressbar();
-                        this.update_subtitle();
-                    }),
+                    clone!(
+                        #[weak(rename_to = this)]
+                        self,
+                        move |_, _| {
+                            this.update_progressbar();
+                            this.update_subtitle();
+                        }
+                    ),
                 );
                 self.signal_handlers.borrow_mut().push(id);
 
@@ -213,7 +221,8 @@ mod imp {
 
 glib::wrapper! {
     pub struct FrgFileRow(ObjectSubclass<imp::FrgFileRow>)
-        @extends gtk::Widget, adw::Bin;
+        @extends gtk::Widget, adw::Bin,
+        @implements gtk::Accessible, gtk::Buildable, gtk::ConstraintTarget;
 }
 
 impl FrgFileRow {
diff --git a/src/ui/folder_page.rs b/src/ui/folder_page.rs
index 607eb5a..8a4f55b 100644
--- a/src/ui/folder_page.rs
+++ b/src/ui/folder_page.rs
@@ -1,5 +1,5 @@
 // Fragments - folder_page.rs
-// Copyright (C) 2023  Felix Häcker <haeckerfelix@gnome.org>
+// Copyright (C) 2022-2025  Felix Häcker <haeckerfelix@gnome.org>
 //
 // This program is free software: you can redistribute it and/or modify
 // it under the terms of the GNU General Public License as published by
@@ -82,7 +82,8 @@ mod imp {
 
 glib::wrapper! {
     pub struct FrgFolderPage(ObjectSubclass<imp::FrgFolderPage>)
-        @extends gtk::Widget, adw::NavigationPage;
+        @extends gtk::Widget, adw::NavigationPage,
+        @implements gtk::Accessible, gtk::Buildable, gtk::ConstraintTarget;
 }
 
 impl FrgFolderPage {
diff --git a/src/ui/folder_page_contents.rs b/src/ui/folder_page_contents.rs
index af08535..79714dc 100644
--- a/src/ui/folder_page_contents.rs
+++ b/src/ui/folder_page_contents.rs
@@ -1,5 +1,5 @@
 // Fragments - folder_page_contents.rs
-// Copyright (C) 2023  Felix Häcker <haeckerfelix@gnome.org>
+// Copyright (C) 2022-2025  Felix Häcker <haeckerfelix@gnome.org>
 //
 // This program is free software: you can redistribute it and/or modify
 // it under the terms of the GNU General Public License as published by
@@ -81,13 +81,21 @@ mod imp {
                 .set_model(Some(&self.obj().file().related()));
 
             // Needed, since listview needs a moment to populate the list
-            glib::idle_add_local_once(clone!(@weak self as this => move|| {
-                this.scrolled_window.vadjustment().set_value(*this.scroll_pos.get().unwrap());
-            }));
+            glib::idle_add_local_once(clone!(
+                #[weak(rename_to = this)]
+                self,
+                move || {
+                    this.scrolled_window
+                        .vadjustment()
+                        .set_value(*this.scroll_pos.get().unwrap());
+                }
+            ));
 
             // Search
-            self.searchbar.connect_search_mode_enabled_notify(
-                clone!(@weak self as this => move |sb| {
+            self.searchbar.connect_search_mode_enabled_notify(clone!(
+                #[weak(rename_to = this)]
+                self,
+                move |sb| {
                     let file = this.obj().file();
 
                     let model: gio::ListModel = if sb.is_search_mode() {
@@ -99,8 +107,8 @@ mod imp {
                     };
 
                     this.sort_model.set_model(Some(&model));
-                }),
-            );
+                }
+            ));
             self.searchentry
                 .bind_property("text", &self.filter.get(), "search")
                 .build();
@@ -113,16 +121,24 @@ mod imp {
 
             // view.select-all
             action = SimpleAction::new("select-all", None);
-            action.connect_activate(clone!(@weak self as this => move |_, _| {
-                this.obj().file().set_wanted(true);
-            }));
+            action.connect_activate(clone!(
+                #[weak(rename_to = this)]
+                self,
+                move |_, _| {
+                    this.obj().file().set_wanted(true);
+                }
+            ));
             self.view_actions.add_action(&action);
 
             // view.deselect-all
             action = SimpleAction::new("deselect-all", None);
-            action.connect_activate(clone!(@weak self as this => move |_, _| {
-                this.obj().file().set_wanted(false);
-            }));
+            action.connect_activate(clone!(
+                #[weak(rename_to = this)]
+                self,
+                move |_, _| {
+                    this.obj().file().set_wanted(false);
+                }
+            ));
             self.view_actions.add_action(&action);
         }
     }
@@ -157,7 +173,8 @@ mod imp {
 
 glib::wrapper! {
     pub struct FrgFolderPageContents(ObjectSubclass<imp::FrgFolderPageContents>)
-        @extends gtk::Widget, adw::Bin;
+        @extends gtk::Widget, adw::Bin,
+        @implements gtk::Accessible, gtk::Buildable, gtk::ConstraintTarget;
 }
 
 impl FrgFolderPageContents {
diff --git a/src/ui/preferences_dialog.rs b/src/ui/preferences_dialog.rs
index a4b0cdb..67fc06c 100644
--- a/src/ui/preferences_dialog.rs
+++ b/src/ui/preferences_dialog.rs
@@ -1,5 +1,5 @@
 // Fragments - preferences_dialog.rs
-// Copyright (C) 2022-2024  Felix Häcker <haeckerfelix@gnome.org>
+// Copyright (C) 2022-2025  Felix Häcker <haeckerfelix@gnome.org>
 //
 // This program is free software: you can redistribute it and/or modify
 // it under the terms of the GNU General Public License as published by
@@ -118,11 +118,14 @@ mod imp {
             let session = client.session();
 
             // We show/hide few widgets depending on the connection type (remote/local)
-            self.connection_manager.connect_current_connection_notify(
-                clone!(@weak self as this => move |_|{
-                    this.update_remote_buttons();
-                }),
-            );
+            self.connection_manager
+                .connect_current_connection_notify(clone!(
+                    #[weak(rename_to = this)]
+                    self,
+                    move |_| {
+                        this.update_remote_buttons();
+                    }
+                ));
 
             // General page
             //
@@ -155,23 +158,39 @@ mod imp {
                 &*self.remote_access_switch,
                 "active",
             );
-            self.remote_access_switch.connect_state_set(clone!(@weak self as this => @default-panic, move |switch, _| {
-                settings_manager::set_boolean(Key::RemoteAccess, switch.is_active());
-                switch.set_sensitive(false);
-
-                let fut = clone!(@weak switch, @weak this => async move {
-                    if let Err(err) = FrgConnectionManager::default().reconnect().await{
-                        utils::inapp_notification(&i18n("Unable to restart Transmission daemon"), Some(this.obj().upcast_ref::<gtk::Widget>()));
-                        warn!("Unable to restart Transmission daemon: {}", err.to_string());
-                    }
-
-                    switch.set_sensitive(true);
-                });
-                glib::spawn_future_local(fut);
-                glib::Propagation::Proceed
-            }));
-            self.open_webinterface_row
-                .connect_activated(clone!(@weak self as this => move |_| {
+            self.remote_access_switch.connect_state_set(clone!(
+                #[weak(rename_to = this)]
+                self,
+                #[upgrade_or_panic]
+                move |switch, _| {
+                    settings_manager::set_boolean(Key::RemoteAccess, switch.is_active());
+                    switch.set_sensitive(false);
+
+                    let fut = clone!(
+                        #[weak]
+                        switch,
+                        #[weak]
+                        this,
+                        async move {
+                            if let Err(err) = FrgConnectionManager::default().reconnect().await {
+                                utils::inapp_notification(
+                                    &i18n("Unable to restart Transmission daemon"),
+                                    Some(this.obj().upcast_ref::<gtk::Widget>()),
+                                );
+                                warn!("Unable to restart Transmission daemon: {}", err.to_string());
+                            }
+
+                            switch.set_sensitive(true);
+                        }
+                    );
+                    glib::spawn_future_local(fut);
+                    glib::Propagation::Proceed
+                }
+            ));
+            self.open_webinterface_row.connect_activated(clone!(
+                #[weak(rename_to = this)]
+                self,
+                move |_| {
                     let fut = async move {
                         let uri = url::Url::parse("http://127.0.0.1:9091/").unwrap();
                         let native = this.obj().native().unwrap();
@@ -182,21 +201,33 @@ mod imp {
                             .await
                         {
                             debug!("Unable to open webinterface: {:?}", err);
-                            utils::inapp_notification("Unable to open webinterface", Some(this.obj().upcast_ref::<gtk::Widget>()));
+                            utils::inapp_notification(
+                                "Unable to open webinterface",
+                                Some(this.obj().upcast_ref::<gtk::Widget>()),
+                            );
                         }
                     };
                     glib::spawn_future_local(fut);
-                }));
+                }
+            ));
 
             // Downloading page
             //
-            client.connect_is_connected_notify(clone!(@weak self as this => move |_|{
-                this.update_paths();
-            }));
+            client.connect_is_connected_notify(clone!(
+                #[weak(rename_to = this)]
+                self,
+                move |_| {
+                    this.update_paths();
+                }
+            ));
 
-            session.connect_download_dir_notify(glib::clone!(@weak self as this => move |_| {
-                this.update_paths();
-            }));
+            session.connect_download_dir_notify(glib::clone!(
+                #[weak(rename_to = this)]
+                self,
+                move |_| {
+                    this.update_paths();
+                }
+            ));
 
             self.download_dir_button.connect_clicked(move |btn| {
                 let parent = btn.root().unwrap().downcast::<gtk::Window>().unwrap();
@@ -212,15 +243,17 @@ mod imp {
                 .flags(glib::BindingFlags::SYNC_CREATE | glib::BindingFlags::BIDIRECTIONAL)
                 .build();
 
-            session.connect_incomplete_dir_enabled_notify(
-                glib::clone!(@weak self as this => move |_| {
-                    FrgConnectionManager::default().check_directories();
-                }),
-            );
+            session.connect_incomplete_dir_enabled_notify(glib::clone!(move |_| {
+                FrgConnectionManager::default().check_directories()
+            },));
 
-            session.connect_incomplete_dir_notify(glib::clone!(@weak self as this => move |_| {
-                this.update_paths();
-            }));
+            session.connect_incomplete_dir_notify(glib::clone!(
+                #[weak(rename_to = this)]
+                self,
+                move |_| {
+                    this.update_paths();
+                }
+            ));
 
             self.incomplete_dir_button.connect_clicked(move |btn| {
                 let parent = btn.root().unwrap().downcast::<gtk::Window>().unwrap();
@@ -378,7 +411,8 @@ mod imp {
 glib::wrapper! {
     pub struct FrgPreferencesDialog(
         ObjectSubclass<imp::FrgPreferencesDialog>)
-        @extends gtk::Widget, adw::Dialog, adw::PreferencesDialog;
+        @extends gtk::Widget, adw::Dialog, adw::PreferencesDialog,
+        @implements gtk::Accessible, gtk::Buildable, gtk::ConstraintTarget;
 }
 
 impl Default for FrgPreferencesDialog {
diff --git a/src/ui/stats_dialog.rs b/src/ui/stats_dialog.rs
index 1adf6e9..58d4d52 100644
--- a/src/ui/stats_dialog.rs
+++ b/src/ui/stats_dialog.rs
@@ -1,5 +1,5 @@
 // Fragments - stats_dialog.rs
-// Copyright (C) 2022-2023  Felix Häcker <haeckerfelix@gnome.org>
+// Copyright (C) 2022-2025  Felix Häcker <haeckerfelix@gnome.org>
 //
 // This program is free software: you can redistribute it and/or modify
 // it under the terms of the GNU General Public License as published by
@@ -91,11 +91,13 @@ mod imp {
         fn constructed(&self) {
             self.parent_constructed();
 
-            FrgConnectionManager::default().connect_current_connection_notify(
-                clone!(@weak self as this => move |_|{
+            FrgConnectionManager::default().connect_current_connection_notify(clone!(
+                #[weak(rename_to = this)]
+                self,
+                move |_| {
                     this.update_remote_notice();
-                }),
-            );
+                }
+            ));
 
             self.bind_properties();
             self.update_format_strings();
@@ -179,41 +181,77 @@ mod imp {
                 .flags(BindingFlags::SYNC_CREATE)
                 .build();
 
-            stats.connect_download_speed_notify(clone!(@weak self as this => move |_|{
-                this.update_format_strings();
-            }));
-
-            stats.connect_upload_speed_notify(clone!(@weak self as this => move |_|{
-                this.update_format_strings();
-            }));
-
-            current.connect_seconds_active_notify(clone!(@weak self as this => move |_|{
-                this.update_format_strings();
-            }));
-
-            current.connect_downloaded_bytes_notify(clone!(@weak self as this => move |_|{
-                this.update_format_strings();
-            }));
-
-            current.connect_uploaded_bytes_notify(clone!(@weak self as this => move |_|{
-                this.update_format_strings();
-            }));
-
-            cumulative.connect_seconds_active_notify(clone!(@weak self as this => move |_|{
-                this.update_format_strings();
-            }));
-
-            cumulative.connect_downloaded_bytes_notify(clone!(@weak self as this => move |_|{
-                this.update_format_strings();
-            }));
-
-            cumulative.connect_uploaded_bytes_notify(clone!(@weak self as this => move |_|{
-                this.update_format_strings();
-            }));
-
-            cumulative.connect_session_count_notify(clone!(@weak self as this => move |_|{
-                this.update_format_strings();
-            }));
+            stats.connect_download_speed_notify(clone!(
+                #[weak(rename_to = this)]
+                self,
+                move |_| {
+                    this.update_format_strings();
+                }
+            ));
+
+            stats.connect_upload_speed_notify(clone!(
+                #[weak(rename_to = this)]
+                self,
+                move |_| {
+                    this.update_format_strings();
+                }
+            ));
+
+            current.connect_seconds_active_notify(clone!(
+                #[weak(rename_to = this)]
+                self,
+                move |_| {
+                    this.update_format_strings();
+                }
+            ));
+
+            current.connect_downloaded_bytes_notify(clone!(
+                #[weak(rename_to = this)]
+                self,
+                move |_| {
+                    this.update_format_strings();
+                }
+            ));
+
+            current.connect_uploaded_bytes_notify(clone!(
+                #[weak(rename_to = this)]
+                self,
+                move |_| {
+                    this.update_format_strings();
+                }
+            ));
+
+            cumulative.connect_seconds_active_notify(clone!(
+                #[weak(rename_to = this)]
+                self,
+                move |_| {
+                    this.update_format_strings();
+                }
+            ));
+
+            cumulative.connect_downloaded_bytes_notify(clone!(
+                #[weak(rename_to = this)]
+                self,
+                move |_| {
+                    this.update_format_strings();
+                }
+            ));
+
+            cumulative.connect_uploaded_bytes_notify(clone!(
+                #[weak(rename_to = this)]
+                self,
+                move |_| {
+                    this.update_format_strings();
+                }
+            ));
+
+            cumulative.connect_session_count_notify(clone!(
+                #[weak(rename_to = this)]
+                self,
+                move |_| {
+                    this.update_format_strings();
+                }
+            ));
         }
 
         fn update_format_strings(&self) {
@@ -272,7 +310,8 @@ mod imp {
 
 glib::wrapper! {
     pub struct FrgStatsDialog(ObjectSubclass<imp::FrgStatsDialog>)
-        @extends gtk::Widget, adw::Dialog;
+        @extends gtk::Widget, adw::Dialog,
+        @implements gtk::Accessible, gtk::Buildable, gtk::ConstraintTarget;
 }
 
 impl FrgStatsDialog {
diff --git a/src/ui/status_header.rs b/src/ui/status_header.rs
index 403a554..1030297 100644
--- a/src/ui/status_header.rs
+++ b/src/ui/status_header.rs
@@ -1,5 +1,5 @@
 // Fragments - status_header.rs
-// Copyright (C) s2023  Felix Häcker <haeckerfelix@gnome.org>
+// Copyright (C) 2022-2025  Felix Häcker <haeckerfelix@gnome.org>
 //
 // This program is free software: you can redistribute it and/or modify
 // it under the terms of the GNU General Public License as published by
@@ -120,7 +120,8 @@ mod imp {
 
 glib::wrapper! {
     pub struct FrgStatusHeader(ObjectSubclass<imp::FrgStatusHeader>)
-        @extends gtk::Widget, adw::Bin;
+        @extends gtk::Widget, adw::Bin,
+        @implements gtk::Accessible, gtk::Buildable, gtk::ConstraintTarget;
 }
 
 impl FrgStatusHeader {
diff --git a/src/ui/torrent_dialog.rs b/src/ui/torrent_dialog.rs
index d4b2aee..31b6f80 100644
--- a/src/ui/torrent_dialog.rs
+++ b/src/ui/torrent_dialog.rs
@@ -1,5 +1,5 @@
 // Fragments - torrent_dialog.rs
-// Copyright (C) 2022-2024  Felix Häcker <haeckerfelix@gnome.org>
+// Copyright (C) 2022-2025  Felix Häcker <haeckerfelix@gnome.org>
 //
 // This program is free software: you can redistribute it and/or modify
 // it under the terms of the GNU General Public License as published by
@@ -170,16 +170,24 @@ mod imp {
             if torrent.files().is_ready() {
                 self.show_overview_files();
             }
-            torrent
-                .files()
-                .connect_is_ready_notify(clone!(@weak self as this => move|_| {
+            torrent.files().connect_is_ready_notify(clone!(
+                #[weak(rename_to = this)]
+                self,
+                move |_| {
                     this.show_overview_files();
-                }));
+                }
+            ));
 
             // Files listbox row activation
-            self.file_listbox
-                .connect_row_activated(clone!(@weak self as this => move |_, row|{
-                    let row = row.downcast_ref::<adw::PreferencesRow>().unwrap().child().unwrap();
+            self.file_listbox.connect_row_activated(clone!(
+                #[weak(rename_to = this)]
+                self,
+                move |_, row| {
+                    let row = row
+                        .downcast_ref::<adw::PreferencesRow>()
+                        .unwrap()
+                        .child()
+                        .unwrap();
                     let file_row = row.downcast_ref::<FrgFileRow>().unwrap();
                     let file = file_row.file();
 
@@ -190,7 +198,8 @@ mod imp {
                             this.show_file(&file);
                         }
                     }
-                }));
+                }
+            ));
 
             // Setup view actions
             obj.insert_action_group("view", Some(&self.view_actions));
@@ -202,16 +211,18 @@ mod imp {
                 VariantTy::STRING.into(),
                 &"name".to_variant(),
             );
-            action.connect_change_state(
-                clone!(@weak self.files_sorter as files_sorter => move |action, state|{
+            action.connect_change_state(clone!(
+                #[weak(rename_to = files_sorter)]
+                self.files_sorter,
+                move |action, state| {
                     if let Some(state) = state {
                         action.set_state(state);
 
                         let sorter: FrgFileSorting = state.str().unwrap().into();
                         files_sorter.set_sorting(sorter);
                     }
-                }),
-            );
+                }
+            ));
             self.view_actions.add_action(&action);
 
             // view.order
@@ -220,73 +231,92 @@ mod imp {
                 VariantTy::STRING.into(),
                 &"ascending".to_variant(),
             );
-            action.connect_change_state(
-                clone!(@weak self.files_sorter as files_sorter => move |action, state|{
+            action.connect_change_state(clone!(
+                #[weak(rename_to = files_sorter)]
+                self.files_sorter,
+                move |action, state| {
                     if let Some(state) = state {
                         action.set_state(state);
 
                         let descending = state.str().unwrap() == "descending";
                         files_sorter.set_descending(descending);
                     }
-                }),
-            );
+                }
+            ));
             self.view_actions.add_action(&action);
 
             // view.folder-before-files
             action = SimpleAction::new_stateful("folder-before-files", None, &true.to_variant());
-            action.connect_change_state(
-                clone!(@weak self.files_sorter as files_sorter => move |action, state|{
+            action.connect_change_state(clone!(
+                #[weak(rename_to = files_sorter)]
+                self.files_sorter,
+                move |action, state| {
                     if let Some(state) = state {
                         action.set_state(state);
 
                         let value = state.get::<bool>().unwrap();
                         files_sorter.set_folders_before_files(value);
                     }
-                }),
-            );
+                }
+            ));
             self.view_actions.add_action(&action);
 
             // view.show-all
             action = SimpleAction::new("show-all", None);
-            action.connect_activate(clone!(@weak self as this => move |_, _|{
-                if let Some(top_level) = this.obj().torrent().files().top_level(){
-                    this.show_folder(&top_level);
-                } else {
-                    warn!("Unable to show contents, no top level file available");
+            action.connect_activate(clone!(
+                #[weak(rename_to = this)]
+                self,
+                move |_, _| {
+                    if let Some(top_level) = this.obj().torrent().files().top_level() {
+                        this.show_folder(&top_level);
+                    } else {
+                        warn!("Unable to show contents, no top level file available");
+                    }
                 }
-
-            }));
+            ));
             self.view_actions.add_action(&action);
 
             // view.show-folder
             action = SimpleAction::new("show-folder", VariantTy::STRING.into());
-            action.connect_activate(clone!(@weak self as this => move |_, param|{
-                if let Some(param) = param {
-                    let name = param.get::<String>().unwrap();
-                    let folder = this.obj().torrent().files().file_by_name(&name).unwrap();
-                    this.show_folder(&folder);
+            action.connect_activate(clone!(
+                #[weak(rename_to = this)]
+                self,
+                move |_, param| {
+                    if let Some(param) = param {
+                        let name = param.get::<String>().unwrap();
+                        let folder = this.obj().torrent().files().file_by_name(&name).unwrap();
+                        this.show_folder(&folder);
+                    }
                 }
-            }));
+            ));
             self.view_actions.add_action(&action);
 
             // view.show-file
             action = SimpleAction::new("show-file", VariantTy::STRING.into());
-            action.connect_activate(clone!(@weak self as this => move |_, param|{
-                if let Some(param) = param {
-                    let name = param.get::<String>().unwrap();
-                    let file = this.obj().torrent().files().file_by_name(&name).unwrap();
-                    this.show_file(&file);
+            action.connect_activate(clone!(
+                #[weak(rename_to = this)]
+                self,
+                move |_, param| {
+                    if let Some(param) = param {
+                        let name = param.get::<String>().unwrap();
+                        let file = this.obj().torrent().files().file_by_name(&name).unwrap();
+                        this.show_file(&file);
+                    }
                 }
-            }));
+            ));
             self.view_actions.add_action(&action);
 
             // Update dialog on torrent changes
             torrent.connect_notify_local(
                 None,
-                clone!(@weak self as this => move |_, _| {
-                    this.update_labels();
-                    this.update_widgets();
-                }),
+                clone!(
+                    #[weak(rename_to = this)]
+                    self,
+                    move |_, _| {
+                        this.update_labels();
+                        this.update_widgets();
+                    }
+                ),
             );
 
             self.update_labels();
@@ -463,7 +493,8 @@ mod imp {
 
 glib::wrapper! {
     pub struct FrgTorrentDialog(ObjectSubclass<imp::FrgTorrentDialog>)
-        @extends gtk::Widget, adw::Dialog;
+        @extends gtk::Widget, adw::Dialog,
+        @implements gtk::Accessible, gtk::Buildable, gtk::ConstraintTarget;
 }
 
 impl FrgTorrentDialog {
diff --git a/src/ui/torrent_group.rs b/src/ui/torrent_group.rs
index d9d3f03..2e5bb56 100644
--- a/src/ui/torrent_group.rs
+++ b/src/ui/torrent_group.rs
@@ -1,5 +1,5 @@
 // Fragments - torrent_group.rs
-// Copyright (C) 2022-2023  Felix Häcker <haeckerfelix@gnome.org>
+// Copyright (C) 2022-2025  Felix Häcker <haeckerfelix@gnome.org>
 //
 // This program is free software: you can redistribute it and/or modify
 // it under the terms of the GNU General Public License as published by
@@ -60,7 +60,8 @@ mod imp {
 glib::wrapper! {
     pub struct FrgTorrentGroup(
         ObjectSubclass<imp::FrgTorrentGroup>)
-        @extends gtk::Widget, adw::PreferencesGroup;
+        @extends gtk::Widget, adw::PreferencesGroup,
+        @implements gtk::Accessible, gtk::Buildable, gtk::ConstraintTarget;
 }
 
 impl FrgTorrentGroup {
@@ -81,8 +82,10 @@ impl FrgTorrentGroup {
             row.activate_action("torrent.show-dialog", None).unwrap();
         });
 
-        model.connect_items_changed(
-            glib::clone!(@weak self as this, @weak listbox => move |_, _, _, _| {
+        model.connect_items_changed(glib::clone!(
+            #[weak(rename_to = this)]
+            self,
+            move |_, _, _, _| {
                 let imp = this.imp();
 
                 // Hide this group if all models are empty
@@ -95,14 +98,14 @@ impl FrgTorrentGroup {
                 this.set_visible(!is_empty);
 
                 // Hide empty listboxes
-                let mut child = imp.listbox_box.first_child ();
+                let mut child = imp.listbox_box.first_child();
                 while let Some(widget) = child {
                     let listbox = widget.downcast_ref::<gtk::ListBox>().unwrap();
                     listbox.set_visible(listbox.row_at_index(0).is_some());
                     child = listbox.next_sibling();
                 }
-            }),
-        );
+            }
+        ));
 
         imp.models.borrow_mut().insert(0, model);
     }
diff --git a/src/ui/torrent_page.rs b/src/ui/torrent_page.rs
index 5824759..2308ef4 100644
--- a/src/ui/torrent_page.rs
+++ b/src/ui/torrent_page.rs
@@ -1,5 +1,5 @@
 // Fragments - torrent_page.rs
-// Copyright (C) 2022-2024  Felix Häcker <haeckerfelix@gnome.org>
+// Copyright (C) 2022-2025  Felix Häcker <haeckerfelix@gnome.org>
 //
 // This program is free software: you can redistribute it and/or modify
 // it under the terms of the GNU General Public License as published by
@@ -110,11 +110,19 @@ mod imp {
             model.connect_local(
                 "status-changed",
                 false,
-                clone!(@weak group_filter, @weak sorter => @default-return None, move |_| {
-                    group_filter.changed(gtk::FilterChange::Different);
-                    sorter.changed(gtk::SorterChange::Different);
-                    None
-                }),
+                clone!(
+                    #[weak]
+                    group_filter,
+                    #[weak]
+                    sorter,
+                    #[upgrade_or]
+                    None,
+                    move |_| {
+                        group_filter.changed(gtk::FilterChange::Different);
+                        sorter.changed(gtk::SorterChange::Different);
+                        None
+                    }
+                ),
             );
 
             let group_model = FilterListModel::new(Some(model), Some(group_filter));
@@ -128,7 +136,8 @@ mod imp {
 glib::wrapper! {
     pub struct FrgTorrentPage(
         ObjectSubclass<imp::FrgTorrentPage>)
-        @extends gtk::Widget, adw::PreferencesPage;
+        @extends gtk::Widget, adw::PreferencesPage,
+        @implements gtk::Accessible, gtk::Buildable, gtk::ConstraintTarget;
 }
 
 impl FrgTorrentPage {
diff --git a/src/ui/torrent_row.rs b/src/ui/torrent_row.rs
index c399b11..71446f2 100644
--- a/src/ui/torrent_row.rs
+++ b/src/ui/torrent_row.rs
@@ -1,4 +1,5 @@
-// Copyright (C) 2022-2024  Felix Häcker <haeckerfelix@gnome.org>
+// Fragments - torrent_row.rs
+// Copyright (C) 2022-2025  Felix Häcker <haeckerfelix@gnome.org>
 //
 // This program is free software: you can redistribute it and/or modify
 // it under the terms of the GNU General Public License as published by
@@ -42,8 +43,6 @@ mod imp {
         #[template_child]
         progress_bar: TemplateChild<gtk::ProgressBar>,
         #[template_child]
-        spinner: TemplateChild<gtk::Spinner>,
-        #[template_child]
         queue_box: TemplateChild<gtk::Box>,
         #[template_child]
         index_stack: TemplateChild<gtk::Stack>,
@@ -96,10 +95,14 @@ mod imp {
 
             self.obj().torrent().connect_notify_local(
                 None,
-                clone!(@weak self as this => move |_, _| {
-                    this.update_labels();
-                    this.update_widgets();
-                }),
+                clone!(
+                    #[weak(rename_to = this)]
+                    self,
+                    move |_, _| {
+                        this.update_labels();
+                        this.update_widgets();
+                    }
+                ),
             );
 
             // Shift+F10 / `Menu` button to open context menu
@@ -107,12 +110,16 @@ mod imp {
                 gtk::KeyvalTrigger::new(gdk::Key::F10, gdk::ModifierType::SHIFT_MASK),
                 gtk::KeyvalTrigger::new(gdk::Key::Menu, gdk::ModifierType::empty()),
             );
-            let action = gtk::CallbackAction::new(
-                clone!(@weak self as this => @default-return glib::Propagation::Stop, move |_, _| {
+            let action = gtk::CallbackAction::new(clone!(
+                #[weak(rename_to = this)]
+                self,
+                #[upgrade_or]
+                glib::Propagation::Stop,
+                move |_, _| {
                     this.show_context_menu(None::<&gtk::Gesture>, 40.0, 40.0);
                     glib::Propagation::Stop
-                }),
-            );
+                }
+            ));
             let shortcut = gtk::Shortcut::new(Some(trigger), Some(action));
 
             let controller = gtk::ShortcutController::new();
@@ -122,17 +129,21 @@ mod imp {
             // Right click to open context menu
             let controller = gtk::GestureClick::new();
             controller.set_button(gdk::BUTTON_SECONDARY);
-            controller.connect_pressed(
-                clone!(@weak self as this => move |c, _, x, y| this.show_context_menu(Some(c), x, y)),
-            );
+            controller.connect_pressed(clone!(
+                #[weak(rename_to = this)]
+                self,
+                move |c, _, x, y| this.show_context_menu(Some(c), x, y)
+            ));
             self.obj().add_controller(controller);
 
             // Touch long-press to open context menu
             let controller = gtk::GestureLongPress::new();
             controller.set_touch_only(true);
-            controller.connect_pressed(
-                clone!(@weak self as this => move |c, x, y| this.show_context_menu(Some(c), x, y)),
-            );
+            controller.connect_pressed(clone!(
+                #[weak(rename_to = this)]
+                self,
+                move |c, x, y| this.show_context_menu(Some(c), x, y)
+            ));
             self.obj().add_controller(controller);
 
             self.update_labels();
@@ -183,7 +194,6 @@ mod imp {
 
         fn update_widgets(&self) {
             self.queue_box.set_visible(false);
-            self.spinner.set_spinning(false);
 
             self.obj().remove_css_class("inactive");
             self.description_label.remove_css_class("error");
@@ -208,7 +218,6 @@ mod imp {
                     action_name = "pause".into();
 
                     self.obj().add_css_class("inactive");
-                    self.spinner.set_spinning(true);
                 }
                 TrTorrentStatus::Stopped => {
                     index_name = "stopped".into();
@@ -280,7 +289,8 @@ mod imp {
 glib::wrapper! {
     pub struct FrgTorrentRow(
         ObjectSubclass<imp::FrgTorrentRow>)
-        @extends gtk::Widget, gtk::ListBoxRow;
+        @extends gtk::Widget, gtk::ListBoxRow,
+        @implements gtk::Actionable, gtk::Accessible, gtk::Buildable, gtk::ConstraintTarget;
 }
 
 impl FrgTorrentRow {
diff --git a/src/ui/window.rs b/src/ui/window.rs
index 2f20179..eea19e6 100644
--- a/src/ui/window.rs
+++ b/src/ui/window.rs
@@ -101,26 +101,33 @@ mod imp {
             self.search_bar.connect_entry(&self.search_entry.get());
 
             // Recolor headerbar purple for remote connections
-            cm.connect_current_connection_notify(
-                clone!(@weak self as this => move |manager|{
-                    if !manager.current_connection().is_fragments(){
-                        let subtitle = i18n_f("Remote control \"{}\"", &[&manager.current_connection().title()]);
+            cm.connect_current_connection_notify(clone!(
+                #[weak(rename_to = this)]
+                self,
+                move |manager| {
+                    if !manager.current_connection().is_fragments() {
+                        let subtitle = i18n_f(
+                            "Remote control \"{}\"",
+                            &[&manager.current_connection().title()],
+                        );
                         this.window_title.set_subtitle(&subtitle);
                         this.obj().add_css_class("remote");
-                    }else{
+                    } else {
                         this.window_title.set_subtitle("");
                         this.obj().remove_css_class("remote");
                     }
-                }),
-            );
+                }
+            ));
 
             // Show connection button if we have more than the standard local connection
-            cm.connections().connect_items_changed(
-                clone!(@weak self as this => move |model,_,_,_|{
+            cm.connections().connect_items_changed(clone!(
+                #[weak(rename_to = this)]
+                self,
+                move |model, _, _, _| {
                     let show = model.n_items() > 1;
                     this.connection_menu.set_visible(show);
-                }),
-            );
+                }
+            ));
 
             // Check clipboard content for magnet links
             obj.connect_is_active_notify(move |window| {
@@ -128,73 +135,108 @@ mod imp {
                     let display = gdk::Display::default().unwrap();
                     let clipboard = display.primary_clipboard();
 
-                    let fut = clone!(@weak window, @weak clipboard => async move {
-                        let content = clipboard.read_text_future().await;
-                        let mut clear_clipboard = false;
-
-                        if let Ok(Some(text)) = content {
-                            // Check for magnet link
-                            if let Ok(magnet_link) = MagnetURI::from_str(&text) {
-                                let fallback = String::new();
-                                let info_hash = magnet_link.info_hash().unwrap_or(&fallback);
-                                let cm = FrgConnectionManager::default();
-
-                                // Transmission lowercases info hashes internally
-                                if cm.client().torrents().torrent_by_hash(info_hash.to_lowercase()).is_none() {
-                                    debug!("Detected new magnet link: {}", &text);
-                                    window.imp().magnet_link_notification(&text, magnet_link.name());
-                                    clear_clipboard = true;
-                                } else {
-                                    debug!("Ignore magnet link, torrent is already added.");
+                    let fut = clone!(
+                        #[weak]
+                        window,
+                        #[weak]
+                        clipboard,
+                        async move {
+                            let content = clipboard.read_text_future().await;
+                            let mut clear_clipboard = false;
+
+                            if let Ok(Some(text)) = content {
+                                // Check for magnet link
+                                if let Ok(magnet_link) = MagnetURI::from_str(&text) {
+                                    let fallback = String::new();
+                                    let info_hash = magnet_link.info_hash().unwrap_or(&fallback);
+                                    let cm = FrgConnectionManager::default();
+
+                                    // Transmission lowercases info hashes internally
+                                    if cm
+                                        .client()
+                                        .torrents()
+                                        .torrent_by_hash(info_hash.to_lowercase())
+                                        .is_none()
+                                    {
+                                        debug!("Detected new magnet link: {}", &text);
+                                        window
+                                            .imp()
+                                            .magnet_link_notification(&text, magnet_link.name());
+                                        clear_clipboard = true;
+                                    } else {
+                                        debug!("Ignore magnet link, torrent is already added.");
+                                    }
                                 }
-                            }
 
-                            // Check for torrent link
-                            if let Ok(url) = Url::parse(&text) {
-                                // We only support http/https, and no file links because of sandboxing
-                                if url.path().ends_with(".torrent") && (url.scheme() == "http" || url.scheme() == "https") {
-                                    debug!("Detected torrent link: {}", &text);
-                                    window.imp().torrent_link_notification(&url);
-                                    clear_clipboard = true;
+                                // Check for torrent link
+                                if let Ok(url) = Url::parse(&text) {
+                                    // We only support http/https, and no file links because of
+                                    // sandboxing
+                                    if url.path().ends_with(".torrent")
+                                        && (url.scheme() == "http" || url.scheme() == "https")
+                                    {
+                                        debug!("Detected torrent link: {}", &text);
+                                        window.imp().torrent_link_notification(&url);
+                                        clear_clipboard = true;
+                                    }
                                 }
                             }
-                        }
 
-                        if clear_clipboard {
-                            // To avoid that the clipboard toast is shown multiple times, we clear the clipboard
-                            clipboard.set_text("");
+                            if clear_clipboard {
+                                // To avoid that the clipboard toast is shown multiple times, we
+                                // clear the clipboard
+                                clipboard.set_text("");
+                            }
                         }
-                    });
+                    );
                     glib::spawn_future_local(fut);
                 }
             });
 
             // torrent-added notification
-            cm.client().connect_local("torrent-added", false, clone!(@weak self as this => @default-return None, move |torrent|{
-                if settings_manager::boolean(Key::EnableNotificationsNewTorrent){
-                    let torrent: TrTorrent = torrent[1].get().unwrap();
-                    utils::system_notification(i18n("New torrent added"), torrent.name(), Some("folder-download-symbolic"));
-                }
-                None
-            }));
+            cm.client()
+                .connect_local("torrent-added", false, move |torrent| {
+                    if settings_manager::boolean(Key::EnableNotificationsNewTorrent) {
+                        let torrent: TrTorrent = torrent[1].get().unwrap();
+                        utils::system_notification(
+                            i18n("New torrent added"),
+                            torrent.name(),
+                            Some("folder-download-symbolic"),
+                        );
+                    }
+                    None
+                });
 
             // torrent-downloaded notification
-            cm.client().connect_local("torrent-downloaded", false, clone!(@weak self as this => @default-return None, move |torrent|{
-                if settings_manager::boolean(Key::EnableNotificationsDownloaded){
-                    let torrent: TrTorrent = torrent[1].get().unwrap();
-                    utils::system_notification(i18n("Torrent completely downloaded"), torrent.name(), Some("folder-download-symbolic"));
-                }
-                None
-            }));
+            cm.client()
+                .connect_local("torrent-downloaded", false, move |torrent| {
+                    if settings_manager::boolean(Key::EnableNotificationsDownloaded) {
+                        let torrent: TrTorrent = torrent[1].get().unwrap();
+                        utils::system_notification(
+                            i18n("Torrent completely downloaded"),
+                            torrent.name(),
+                            Some("folder-download-symbolic"),
+                        );
+                    }
+                    None
+                });
 
             // Lower polling rate when window isn't active
-            obj.connect_is_active_notify(clone!(@weak self as this => move |_| {
-                this.update_polling_mode();
-            }));
+            obj.connect_is_active_notify(clone!(
+                #[weak(rename_to = this)]
+                self,
+                move |_| {
+                    this.update_polling_mode();
+                }
+            ));
 
-            obj.connect_suspended_notify(clone!(@weak self as this => move |_| {
-                this.update_polling_mode();
-            }));
+            obj.connect_suspended_notify(clone!(
+                #[weak(rename_to = this)]
+                self,
+                move |_| {
+                    this.update_polling_mode();
+                }
+            ));
         }
     }
 
@@ -211,16 +253,21 @@ mod imp {
             settings_manager::set_integer(Key::WindowWidth, width);
             settings_manager::set_integer(Key::WindowHeight, height);
 
-            let fut = clone!(@weak window => async move {
-                window.set_visible(false);
+            let fut = clone!(
+                #[weak]
+                window,
+                async move {
+                    window.set_visible(false);
 
-                // Wait till transmission daemon is stopped
-                FrgConnectionManager::default().disconnect()
-                    .await
-                    .expect("Unable to stop the transmission daemon");
+                    // Wait till transmission daemon is stopped
+                    FrgConnectionManager::default()
+                        .disconnect()
+                        .await
+                        .expect("Unable to stop the transmission daemon");
 
-                FrgApplication::default().quit();
-            });
+                    FrgApplication::default().quit();
+                }
+            );
             glib::spawn_future_local(fut);
 
             glib::Propagation::Stop
@@ -286,7 +333,7 @@ glib::wrapper! {
     pub struct FrgApplicationWindow(
         ObjectSubclass<imp::FrgApplicationWindow>)
         @extends gtk::Widget, gtk::Window, gtk::ApplicationWindow, adw::ApplicationWindow,
-        @implements gio::ActionMap, gio::ActionGroup;
+        @implements gtk::Root, gtk::Native, gtk::ShortcutManager, gio::ActionMap, gio::ActionGroup, gtk::Accessible, gtk::Buildable, gtk::ConstraintTarget;
 }
 
 // FrgApplicationWindow implementation itself
diff --git a/src/utils.rs b/src/utils.rs
index 6b931ac..498ffa4 100644
--- a/src/utils.rs
+++ b/src/utils.rs
@@ -1,5 +1,5 @@
 // Fragments - utils.rs
-// Copyright (C) 2022-2023  Felix Häcker <haeckerfelix@gnome.org>
+// Copyright (C) 2022-2025  Felix Häcker <haeckerfelix@gnome.org>
 //
 // This program is free software: you can redistribute it and/or modify
 // it under the terms of the GNU General Public License as published by
@@ -18,7 +18,7 @@ use std::path::PathBuf;
 
 use adw::prelude::*;
 use gio::SimpleAction;
-use glib::{clone, BindingFlags};
+use glib::BindingFlags;
 use gtk::{gio, glib};
 use transmission_gobject::{TrTorrent, TrTorrentStatus};
 
@@ -166,21 +166,19 @@ pub fn session_dir_filechooser(parent: &gtk::Window, directory_type: FrgDirector
     dialog.select_folder(
         Some(parent),
         gio::Cancellable::NONE,
-        clone!(@weak dialog, @weak parent => move |result| {
-            match result {
-                Ok(folder) => {
-                    debug!("Selected directory: {:?}", folder.path());
-
-                    let session = FrgConnectionManager::default().client().session();
-                    session.set_property(property_name, folder);
-
-                    FrgConnectionManager::default().check_directories();
-                }
-                Err(err) => {
-                    warn!("Selected directory could not be accessed {:?}", err);
-                }
+        move |result| match result {
+            Ok(folder) => {
+                debug!("Selected directory: {:?}", folder.path());
+
+                let session = FrgConnectionManager::default().client().session();
+                session.set_property(property_name, folder);
+
+                FrgConnectionManager::default().check_directories();
+            }
+            Err(err) => {
+                warn!("Selected directory could not be accessed {:?}", err);
             }
-        }),
+        },
     );
 }
 
