From 7bd67e8a7cbe2a132a500b48ba3a6c4ae7fcbf14 Mon Sep 17 00:00:00 2001 From: Dave Patrick Caberto Date: Sat, 13 Jul 2024 09:23:19 +0800 Subject: [PATCH] refactor: use new clone macro syntax --- src/application.rs | 12 +- src/inspector_page.rs | 9 +- src/player.rs | 61 ++++-- src/preferences_dialog.rs | 23 +- src/recognizer/mod.rs | 58 +++-- src/recognizer/recorder.rs | 11 +- src/recognizer/recordings.rs | 46 ++-- src/song_list.rs | 40 ++-- src/window/album_cover.rs | 24 ++- src/window/history_view.rs | 334 +++++++++++++++++------------ src/window/mod.rs | 118 ++++++---- src/window/progress_icon.rs | 9 +- src/window/recognized_page.rs | 10 +- src/window/recognized_page_tile.rs | 14 +- src/window/recognizer_status.rs | 31 ++- src/window/recognizer_view.rs | 40 ++-- src/window/song_bar.rs | 70 +++--- src/window/song_page.rs | 38 ++-- src/window/song_tile.rs | 83 ++++--- 19 files changed, 639 insertions(+), 392 deletions(-) diff --git a/src/application.rs b/src/application.rs index 594ec8cc..85360d3e 100644 --- a/src/application.rs +++ b/src/application.rs @@ -216,10 +216,14 @@ impl Application { dialog.set_response_appearance(QUIT_RESPONSE_ID, adw::ResponseAppearance::Suggested); dialog.connect_response( Some(QUIT_RESPONSE_ID), - clone!(@weak self as obj => move |_, response| match response { - QUIT_RESPONSE_ID => obj.quit(), - _ => unreachable!(), - }), + clone!( + #[weak(rename_to = obj)] + self, + move |_, response| match response { + QUIT_RESPONSE_ID => obj.quit(), + _ => unreachable!(), + } + ), ); dialog.present(Some(parent)); } diff --git a/src/inspector_page.rs b/src/inspector_page.rs index d4b81613..cbd294a9 100644 --- a/src/inspector_page.rs +++ b/src/inspector_page.rs @@ -128,8 +128,10 @@ impl InspectorPage { } }), ))); - imp.provider_row - .connect_selected_notify(clone!(@weak self as obj => move |provider_row| { + imp.provider_row.connect_selected_notify(clone!( + #[weak(rename_to = obj)] + self, + move |provider_row| { if let Some(ref item) = provider_row.selected_item() { ProviderSettings::lock().active = item .downcast_ref::() @@ -142,7 +144,8 @@ impl InspectorPage { ProviderSettings::lock().active = ProviderType::default(); } obj.update_test_rows_sensitivity(); - })); + } + )); imp.test_provider_mode_row.set_selected( imp.test_provider_mode_model diff --git a/src/player.rs b/src/player.rs index b6c37635..e97fe5ff 100644 --- a/src/player.rs +++ b/src/player.rs @@ -89,10 +89,14 @@ mod imp { let obj = self.obj(); - let bus_watch_guard = self.gst_play + let bus_watch_guard = self + .gst_play .message_bus() - .add_watch_local( - clone!(@weak obj => @default-return glib::ControlFlow::Continue, move |_, message| { + .add_watch_local(clone!( + #[weak] + obj, + #[upgrade_or_panic] + move |_, message| { if gst_play::Play::is_play_message(message) { let play_message = gst_play::PlayMessage::parse(message).unwrap(); obj.handle_gst_play_message(play_message); @@ -100,8 +104,8 @@ mod imp { tracing::trace!("Received other bus message: {:?}", message.view()); } glib::ControlFlow::Continue - }), - ) + } + )) .unwrap(); self.bus_watch_guard.set(bus_watch_guard).unwrap(); } @@ -238,36 +242,47 @@ impl Player { fn mpris_properties_changed(&self, property: impl IntoIterator + 'static) { utils::spawn( glib::Priority::default(), - clone!(@weak self as obj => async move { - match obj.mpris_server().await { - Ok(server) => { - if let Err(err) = server.properties_changed(property).await { - tracing::error!("Failed to emit MPRIS properties changed: {:?}", err); + clone!( + #[weak(rename_to = obj)] + self, + async move { + match obj.mpris_server().await { + Ok(server) => { + if let Err(err) = server.properties_changed(property).await { + tracing::error!( + "Failed to emit MPRIS properties changed: {:?}", + err + ); + } + } + Err(err) => { + tracing::error!("Failed to get MPRIS server: {:?}", err); } - } - Err(err) => { - tracing::error!("Failed to get MPRIS server: {:?}", err); } } - }), + ), ); } fn mpris_seeked(&self, position: Time) { utils::spawn( glib::Priority::default(), - clone!(@weak self as obj => async move { - match obj.mpris_server().await { - Ok(server) => { - if let Err(err) = server.emit(Signal::Seeked { position }).await { - tracing::error!("Failed to emit MPRIS seeked: {:?}", err); + clone!( + #[weak(rename_to = obj)] + self, + async move { + match obj.mpris_server().await { + Ok(server) => { + if let Err(err) = server.emit(Signal::Seeked { position }).await { + tracing::error!("Failed to emit MPRIS seeked: {:?}", err); + } + } + Err(err) => { + tracing::error!("Failed to get MPRIS server: {:?}", err); } - } - Err(err) => { - tracing::error!("Failed to get MPRIS server: {:?}", err); } } - }), + ), ); } diff --git a/src/preferences_dialog.rs b/src/preferences_dialog.rs index f8d65269..fa40ae71 100644 --- a/src/preferences_dialog.rs +++ b/src/preferences_dialog.rs @@ -104,20 +104,23 @@ impl PreferencesDialog { ]))); imp.audio_source_type_row .set_selected(settings.audio_source_type().as_position()); - imp.audio_source_type_row.connect_selected_notify( - clone!(@weak self as obj => move |provider_row| { + imp.audio_source_type_row.connect_selected_notify(clone!( + #[weak(rename_to = obj)] + self, + move |provider_row| { obj.settings() - .set_audio_source_type(AudioSourceType::from_position( - provider_row.selected(), - )); - }), - ); + .set_audio_source_type(AudioSourceType::from_position(provider_row.selected())); + } + )); imp.aud_d_api_token_row .set_text(&settings.aud_d_api_token()); - imp.aud_d_api_token_row - .connect_apply(clone!(@weak self as obj => move |row| { + imp.aud_d_api_token_row.connect_apply(clone!( + #[weak(rename_to = obj)] + self, + move |row| { obj.settings().set_aud_d_api_token(&row.text()); - })); + } + )); } } diff --git a/src/recognizer/mod.rs b/src/recognizer/mod.rs index 2e95c7d8..d14cf86b 100644 --- a/src/recognizer/mod.rs +++ b/src/recognizer/mod.rs @@ -155,18 +155,22 @@ impl Recognizer { .expect("saved recordings must be bound only once"); let network_monitor = gio::NetworkMonitor::default(); - network_monitor.connect_connectivity_notify(clone!(@weak self as obj => move |monitor| { - tracing::debug!(connectivity = ?monitor.connectivity()); - - obj.update_offline_mode(); - - // TODO don't just call when network is available, but also for every - // interval if there is network, there are still saved recordings, and - // there is currently no recognition in progress. - // - // This should also be triggered when token is updated. - obj.try_recognize_saved_recordings(); - })); + network_monitor.connect_connectivity_notify(clone!( + #[weak(rename_to = obj)] + self, + move |monitor| { + tracing::debug!(connectivity = ?monitor.connectivity()); + + obj.update_offline_mode(); + + // TODO don't just call when network is available, but also for every + // interval if there is network, there are still saved recordings, and + // there is currently no recognition in progress. + // + // This should also be triggered when token is updated. + obj.try_recognize_saved_recordings(); + } + )); self.update_offline_mode(); @@ -250,16 +254,24 @@ impl Recognizer { imp.recorder .start( Application::get().settings().audio_source_type(), - clone!(@weak self as obj => move |peak| { - obj.emit_recording_peak_changed(peak); - }), + clone!( + #[weak(rename_to = obj)] + self, + move |peak| { + obj.emit_recording_peak_changed(peak); + } + ), ) .context("Failed to start recording")?; let recorded_time = DateTime::now_utc(); - cancellable.connect_cancelled_local(clone!(@weak _finally => move |_| { - let _ = _finally.take(); - })); + cancellable.connect_cancelled_local(clone!( + #[weak] + _finally, + move |_| { + let _ = _finally.take(); + } + )); let provider = ProviderSettings::lock().active.to_provider(); let listen_duration = provider.listen_duration(); @@ -343,9 +355,13 @@ impl Recognizer { // TODO recognize recordings concurrently, but not too many at once (at most 3?) utils::spawn( glib::Priority::default(), - clone!(@weak self as obj => async move { - obj.try_recognize_saved_recordings_inner().await; - }), + clone!( + #[weak(rename_to = obj)] + self, + async move { + obj.try_recognize_saved_recordings_inner().await; + } + ), ); } diff --git a/src/recognizer/recorder.rs b/src/recognizer/recorder.rs index 9c2b7f7d..2478d997 100644 --- a/src/recognizer/recorder.rs +++ b/src/recognizer/recorder.rs @@ -43,11 +43,12 @@ impl Recorder { let bus_watch_guard = pipeline .bus() .unwrap() - .add_watch_local( - clone!(@weak pipeline => @default-return glib::ControlFlow::Break, move |_, message| { - handle_bus_message(&pipeline, message, &peak_callback) - }), - ) + .add_watch_local(clone!( + #[weak] + pipeline, + #[upgrade_or_panic] + move |_, message| handle_bus_message(&pipeline, message, &peak_callback) + )) .unwrap(); self.pipeline .replace(Some((pipeline.clone(), bus_watch_guard, output_stream))); diff --git a/src/recognizer/recordings.rs b/src/recognizer/recordings.rs index 4fa46ec9..1a913b19 100644 --- a/src/recognizer/recordings.rs +++ b/src/recognizer/recordings.rs @@ -206,27 +206,33 @@ impl Recordings { unsafe { let handler_id = recording.connect_notify_local( None, - clone!(@weak self as obj, @strong recording_id => move |recording, pspec| { - tracing::debug!("Recording property `{}` notified", pspec.name()); - - let (env, db) = obj.db(); - if let Err(err) = env.with_write_txn(|wtxn| { - debug_assert!( - db.get(wtxn, &recording_id).unwrap().is_some(), - "recording must exist in the db" - ); - - db.put(wtxn, &recording_id, recording) - .context("Failed to put recording to db")?; - - Ok(()) - }) { - tracing::error!("Failed to update recording in database: {:?}", err); + clone!( + #[weak(rename_to = obj)] + self, + #[strong] + recording_id, + move |recording, pspec| { + tracing::debug!("Recording property `{}` notified", pspec.name()); + + let (env, db) = obj.db(); + if let Err(err) = env.with_write_txn(|wtxn| { + debug_assert!( + db.get(wtxn, &recording_id).unwrap().is_some(), + "recording must exist in the db" + ); + + db.put(wtxn, &recording_id, recording) + .context("Failed to put recording to db")?; + + Ok(()) + }) { + tracing::error!("Failed to update recording in database: {:?}", err); + } + + let index = obj.imp().list.borrow().get_index_of(&recording_id).unwrap(); + obj.items_changed(index as u32, 1, 1); } - - let index = obj.imp().list.borrow().get_index_of(&recording_id).unwrap(); - obj.items_changed(index as u32, 1, 1); - }), + ), ); recording.set_data(RECORDING_NOTIFY_HANDLER_ID_KEY, handler_id); } diff --git a/src/song_list.rs b/src/song_list.rs index 60503592..cea58f24 100644 --- a/src/song_list.rs +++ b/src/song_list.rs @@ -261,24 +261,28 @@ impl SongList { unsafe { let handler_id = song.connect_notify_local( None, - clone!(@weak self as obj => move |song, pspec| { - tracing::debug!("Song property `{}` notified", pspec.name()); - - let (env, db) = obj.db(); - if let Err(err) = env.with_write_txn(|wtxn| { - debug_assert!( - db.get(wtxn, song.id_ref()).unwrap().is_some(), - "song must exist in the db" - ); - - db.put(wtxn, song.id_ref(), song) - .context("Failed to put song to db")?; - - Ok(()) - }) { - tracing::error!("Failed to update song in database: {:?}", err); - }; - }), + clone!( + #[weak(rename_to = obj)] + self, + move |song, pspec| { + tracing::debug!("Song property `{}` notified", pspec.name()); + + let (env, db) = obj.db(); + if let Err(err) = env.with_write_txn(|wtxn| { + debug_assert!( + db.get(wtxn, song.id_ref()).unwrap().is_some(), + "song must exist in the db" + ); + + db.put(wtxn, song.id_ref(), song) + .context("Failed to put song to db")?; + + Ok(()) + }) { + tracing::error!("Failed to update song in database: {:?}", err); + }; + } + ), ); song.set_data(SONG_NOTIFY_HANDLER_ID_KEY, handler_id); } diff --git a/src/window/album_cover.rs b/src/window/album_cover.rs index d08dbe4f..11277b10 100644 --- a/src/window/album_cover.rs +++ b/src/window/album_cover.rs @@ -126,17 +126,23 @@ impl AlbumCover { let join_handle = utils::spawn( glib::Priority::LOW, - clone!(@weak self as obj, @weak album_art => async move { - match album_art.texture().await { - Ok(texture) => { - obj.set_paintable(Some(texture)); - } - Err(err) => { - tracing::warn!("Failed to load texture: {:?}", err); - obj.set_paintable(gdk::Paintable::NONE); + clone!( + #[weak(rename_to = obj)] + self, + #[weak] + album_art, + async move { + match album_art.texture().await { + Ok(texture) => { + obj.set_paintable(Some(texture)); + } + Err(err) => { + tracing::warn!("Failed to load texture: {:?}", err); + obj.set_paintable(gdk::Paintable::NONE); + } } } - }), + ), ); imp.join_handle.replace(Some(join_handle)); } else { diff --git a/src/window/history_view.rs b/src/window/history_view.rs index 43b5fab4..2c95d105 100644 --- a/src/window/history_view.rs +++ b/src/window/history_view.rs @@ -167,8 +167,10 @@ mod imp { let obj = self.obj(); - self.navigation_view - .connect_pushed(clone!(@weak obj => move |view| { + self.navigation_view.connect_pushed(clone!( + #[weak] + obj, + move |view| { let imp = obj.imp(); let visible_page = view @@ -188,25 +190,28 @@ mod imp { } } } - })); - self.navigation_view - .connect_popped(clone!(@weak obj => move |_, page| { + } + )); + self.navigation_view.connect_popped(clone!( + #[weak] + obj, + move |_, page| { obj.imp() .navigation_forward_stack .borrow_mut() .push(page.clone()); - })); - self.navigation_view.connect_get_next_page( - clone!(@weak obj => @default-panic, move |_| { - let next_page = obj.imp() - .navigation_forward_stack - .borrow() - .last() - .cloned(); + } + )); + self.navigation_view.connect_get_next_page(clone!( + #[weak] + obj, + #[upgrade_or_panic] + move |_| { + let next_page = obj.imp().navigation_forward_stack.borrow().last().cloned(); next_page - }), - ); + } + )); self.content_empty_page.set_icon_name(Some(APP_ID)); obj.setup_grid(); @@ -281,11 +286,13 @@ impl HistoryView { unsafe { recognized_page.set_data( RECOGNIZED_PAGE_SONG_ACTIVATED_HANDLER_ID_KEY, - recognized_page.connect_song_activated( - clone!(@weak self as obj => move |_, song| { + recognized_page.connect_song_activated(clone!( + #[weak(rename_to = obj)] + self, + move |_, song| { obj.push_song_page(song); - }), - ), + } + )), ); recognized_page.set_data( RECOGNIZED_PAGE_ADAPTIVE_MODE_BINDING_KEY, @@ -321,15 +328,19 @@ impl HistoryView { unsafe { song_page.set_data( SONG_PAGE_SONG_REMOVE_REQUEST_HANDLER_ID_KEY, - song_page.connect_song_remove_request(clone!(@weak self as obj => move |_, song| { - if let Err(err) = obj.remove_songs(&[song.id_ref()]) { - tracing::error!("Failed to remove song: {:?}", err); - Application::get().add_message_toast(&gettext("Failed to remove song")); - return; - } + song_page.connect_song_remove_request(clone!( + #[weak(rename_to = obj)] + self, + move |_, song| { + if let Err(err) = obj.remove_songs(&[song.id_ref()]) { + tracing::error!("Failed to remove song: {:?}", err); + Application::get().add_message_toast(&gettext("Failed to remove song")); + return; + } - obj.show_undo_remove_song_toast(); - })), + obj.show_undo_remove_song_toast(); + } + )), ); song_page.set_data( SONG_PAGE_ADAPTIVE_MODE_BINDING_KEY, @@ -359,63 +370,89 @@ impl HistoryView { pub fn bind_song_list(&self, song_list: &SongList) { let imp = self.imp(); - song_list.connect_items_changed(clone!(@weak self as obj => move |_, _, _, _| { - obj.update_content_stack_visible_child(); - })); + song_list.connect_items_changed(clone!( + #[weak(rename_to = obj)] + self, + move |_, _, _, _| { + obj.update_content_stack_visible_child(); + } + )); let filter = SongFilter::new(); let sorter = SongSorter::new(); let filter_model = gtk::FilterListModel::new(Some(song_list.clone()), Some(filter.clone())); - filter_model.connect_items_changed(clone!(@weak self as obj => move |_, _, _, _| { - obj.update_content_stack_visible_child(); - })); - - imp.search_entry.connect_search_changed( - clone!(@weak self as obj, @weak filter, @weak sorter => move |search_entry| { + filter_model.connect_items_changed(clone!( + #[weak(rename_to = obj)] + self, + move |_, _, _, _| { + obj.update_content_stack_visible_child(); + } + )); + + imp.search_entry.connect_search_changed(clone!( + #[weak(rename_to = obj)] + self, + #[weak] + filter, + #[weak] + sorter, + move |search_entry| { let text = search_entry.text(); filter.set_search(text.trim()); sorter.set_search(text.trim()); obj.update_content_stack_visible_child(); - }), - ); + } + )); let sort_model = gtk::SortListModel::new(Some(filter_model.clone()), Some(sorter)); // FIXME save selection even when the song are filtered from FilterListModel let selection_model = gtk::MultiSelection::new(Some(sort_model)); - selection_model.connect_selection_changed(clone!(@weak self as obj => move |model, _, _| { - if obj.is_selection_mode_active() { - if model.selection().size() == 0 { - obj.set_selection_mode_active(false); - } + selection_model.connect_selection_changed(clone!( + #[weak(rename_to = obj)] + self, + move |model, _, _| { + if obj.is_selection_mode_active() { + if model.selection().size() == 0 { + obj.set_selection_mode_active(false); + } - obj.update_selection_actions(); - } - })); - selection_model.connect_items_changed(clone!(@weak self as obj => move |model, _, _, _| { - if obj.is_selection_mode_active() { - if model.selection().size() == 0 { - obj.set_selection_mode_active(false); + obj.update_selection_actions(); } + } + )); + selection_model.connect_items_changed(clone!( + #[weak(rename_to = obj)] + self, + move |model, _, _, _| { + if obj.is_selection_mode_active() { + if model.selection().size() == 0 { + obj.set_selection_mode_active(false); + } - obj.update_selection_actions(); + obj.update_selection_actions(); + } } - })); + )); let grid = imp.grid.get(); grid.set_model(Some(&selection_model)); - grid.connect_activate( - clone!(@weak self as obj, @weak selection_model => move |_, index| { + grid.connect_activate(clone!( + #[weak(rename_to = obj)] + self, + #[weak] + selection_model, + move |_, index| { match selection_model.item(index) { Some(ref item) => { let song = item.downcast_ref::().unwrap(); obj.push_song_page(song); } - None => unreachable!("selection model must have item at index `{}`", index) + None => unreachable!("selection model must have item at index `{}`", index), } - }), - ); + } + )); imp.song_list.set(song_list.downgrade()).unwrap(); imp.filter_model.set(filter_model.downgrade()).unwrap(); @@ -432,15 +469,19 @@ impl HistoryView { imp.recognizer_status.bind_recognizer(recognizer); - imp.recognizer_status.connect_show_results_requested( - clone!(@weak self as obj, @weak recognizer => move |_| { + imp.recognizer_status.connect_show_results_requested(clone!( + #[weak(rename_to = obj)] + self, + #[weak] + recognizer, + move |_| { if let Err(err) = obj.show_recognizer_results(&recognizer) { tracing::error!("Failed to show recognizer results: {:?}", err); Application::get() .add_message_toast(&gettext("Failed to show recognizer results")); } - }), - ); + } + )); } pub fn scroll_to_top(&self) -> bool { @@ -547,21 +588,29 @@ impl HistoryView { .button_label(gettext("_Undo")) .build(); - toast.connect_button_clicked(clone!(@weak self as obj => move |_| { - if let Err(err) = obj - .song_list() - .insert_many(obj.imp().songs_purgatory.take()) - { - tracing::error!("Failed to undo remove song: {:?}", err); - Application::get().add_message_toast(&gettext("Failed to undo")); + toast.connect_button_clicked(clone!( + #[weak(rename_to = obj)] + self, + move |_| { + if let Err(err) = obj + .song_list() + .insert_many(obj.imp().songs_purgatory.take()) + { + tracing::error!("Failed to undo remove song: {:?}", err); + Application::get().add_message_toast(&gettext("Failed to undo")); + } } - })); + )); - toast.connect_dismissed(clone!(@weak self as obj => move |_| { - let imp = obj.imp(); - imp.songs_purgatory.borrow_mut().clear(); - imp.undo_remove_song_toast.take(); - })); + toast.connect_dismissed(clone!( + #[weak(rename_to = obj)] + self, + move |_| { + let imp = obj.imp(); + imp.songs_purgatory.borrow_mut().clear(); + imp.undo_remove_song_toast.take(); + } + )); Application::get().add_toast(toast.clone()); @@ -725,69 +774,88 @@ impl HistoryView { fn setup_grid(&self) { let factory = gtk::SignalListItemFactory::new(); - factory.connect_setup(clone!(@weak self as obj => move |_, list_item| { - let list_item = list_item.downcast_ref::().unwrap(); - - let song_tile = SongTile::new(); - song_tile.set_shows_select_button_on_hover(true); - song_tile.bind_player(&obj.player()); - - let selection_mode_active_binding = obj - .bind_property("is-selection-mode-active", &song_tile, "is-selection-mode-active") - .sync_create() - .build(); - let adaptive_mode_binding = obj - .bind_property("adaptive-mode", &song_tile, "adaptive-mode") - .sync_create() - .build(); + factory.connect_setup(clone!( + #[weak(rename_to = obj)] + self, + move |_, list_item| { + let list_item = list_item.downcast_ref::().unwrap(); + + let song_tile = SongTile::new(); + song_tile.set_shows_select_button_on_hover(true); + song_tile.bind_player(&obj.player()); + + let selection_mode_active_binding = obj + .bind_property( + "is-selection-mode-active", + &song_tile, + "is-selection-mode-active", + ) + .sync_create() + .build(); + let adaptive_mode_binding = obj + .bind_property("adaptive-mode", &song_tile, "adaptive-mode") + .sync_create() + .build(); + + song_tile.connect_is_active_notify(clone!( + #[weak] + obj, + #[weak] + list_item, + move |tile| { + let selection_model = obj + .imp() + .selection_model + .get() + .and_then(|model| model.upgrade()) + .expect("selection model should exist"); + if tile.is_active() { + selection_model.select_item(list_item.position(), false); + } else { + selection_model.unselect_item(list_item.position()); + } + } + )); + song_tile.connect_selection_mode_requested(clone!( + #[weak] + obj, + move |_| { + obj.set_selection_mode_active(true); + } + )); - song_tile.connect_is_active_notify(clone!(@weak obj, @weak list_item => move |tile| { - let selection_model = obj - .imp() - .selection_model - .get() - .and_then(|model| model.upgrade()) - .expect("selection model should exist"); - if tile.is_active() { - selection_model.select_item(list_item.position(), false); - } else { - selection_model.unselect_item(list_item.position()); + let song_watch = list_item.property_expression("item").bind( + &song_tile, + "song", + glib::Object::NONE, + ); + let selected_watch = gtk::ClosureExpression::new::( + [ + list_item.property_expression("selected"), + obj.property_expression("is-selection-mode-active"), + ], + closure!(|_: Option, + is_selected: bool, + is_selection_mode_active: bool| { + is_selected && is_selection_mode_active + }), + ) + .bind(&song_tile, "is-selected", glib::Object::NONE); + + unsafe { + list_item.set_data( + GRID_LIST_ITEM_BINDINGS_KEY, + vec![selection_mode_active_binding, adaptive_mode_binding], + ); + list_item.set_data( + GRID_LIST_ITEM_EXPRESSION_WATCHES_KEY, + vec![song_watch, selected_watch], + ); } - })); - song_tile.connect_selection_mode_requested(clone!(@weak obj => move |_| { - obj.set_selection_mode_active(true); - })); - - let song_watch = - list_item - .property_expression("item") - .bind(&song_tile, "song", glib::Object::NONE); - let selected_watch = gtk::ClosureExpression::new::( - [ - list_item.property_expression("selected"), - obj.property_expression("is-selection-mode-active"), - ], - closure!(|_: Option, - is_selected: bool, - is_selection_mode_active: bool| { - is_selected && is_selection_mode_active - }), - ) - .bind(&song_tile, "is-selected", glib::Object::NONE); - unsafe { - list_item.set_data( - GRID_LIST_ITEM_BINDINGS_KEY, - vec![selection_mode_active_binding, adaptive_mode_binding], - ); - list_item.set_data( - GRID_LIST_ITEM_EXPRESSION_WATCHES_KEY, - vec![song_watch, selected_watch], - ); + list_item.set_child(Some(&song_tile)); } - - list_item.set_child(Some(&song_tile)); - })); + )); factory.connect_teardown(|_, list_item| { let list_item = list_item.downcast_ref::().unwrap(); diff --git a/src/window/mod.rs b/src/window/mod.rs index b9bd7e36..502fe780 100644 --- a/src/window/mod.rs +++ b/src/window/mod.rs @@ -185,12 +185,17 @@ impl Window { imp.main_view.bind_recognizer(&imp.recognizer); imp.recognizer_view.bind_recognizer(&imp.recognizer); - imp.recognizer - .connect_state_notify(clone!(@weak self as obj => move |_| { + imp.recognizer.connect_state_notify(clone!( + #[weak(rename_to = obj)] + self, + move |_| { obj.update_stack(); - })); - imp.recognizer - .connect_song_recognized(clone!(@weak self as obj => move |_, song| { + } + )); + imp.recognizer.connect_song_recognized(clone!( + #[weak(rename_to = obj)] + self, + move |_, song| { let history = obj.song_history(); // If the song is not found in the history, set it as newly heard @@ -212,11 +217,15 @@ impl Window { let main_view = obj.imp().main_view.get(); main_view.push_song_page(song); main_view.scroll_to_top(); - })); - imp.recognizer - .connect_recording_saved(clone!(@weak self as obj => move |_, cause| { + } + )); + imp.recognizer.connect_recording_saved(clone!( + #[weak(rename_to = obj)] + self, + move |_, cause| { obj.present_recording_saved_message(cause); - })); + } + )); } pub fn add_toast(&self, toast: adw::Toast) { @@ -261,7 +270,7 @@ impl Window { dialog.connect_response( Some(OPEN_RESPONSE_ID), - clone!(@weak self as obj => move |_, id| { + clone!(#[weak(rename_to = obj)] self,move |_, id| { debug_assert_eq!(id, OPEN_RESPONSE_ID); gtk::UriLauncher::new("https://github.com/SeaDve/Mousai/issues/new?assignees=&labels=&template=bug_report.md").launch( @@ -295,12 +304,16 @@ impl Window { dialog.connect_response( Some(TRY_AGAIN_RESPONSE_ID), - clone!(@weak self as obj => move |_, id| { - debug_assert_eq!(id, TRY_AGAIN_RESPONSE_ID); - - debug_assert_eq!(obj.imp().recognizer.state(), RecognizerState::Null); - WidgetExt::activate_action(&obj, "win.toggle-recognize", None).unwrap(); - }), + clone!( + #[weak(rename_to = obj)] + self, + move |_, id| { + debug_assert_eq!(id, TRY_AGAIN_RESPONSE_ID); + + debug_assert_eq!(obj.imp().recognizer.state(), RecognizerState::Null); + WidgetExt::activate_action(&obj, "win.toggle-recognize", None).unwrap(); + } + ), ); } RecognizeErrorKind::Connection @@ -360,15 +373,19 @@ impl Window { dialog.connect_response( Some(OPEN_RESPONSE_ID), - clone!(@weak self as obj => move |_, id| { - debug_assert_eq!(id, OPEN_RESPONSE_ID); - - let dialog = PreferencesDialog::new(Application::get().settings()); - dialog.present(Some(&obj)); - - let is_focused = dialog.focus_aud_d_api_token_row(); - debug_assert!(is_focused, "token row must be focused"); - }), + clone!( + #[weak(rename_to = obj)] + self, + move |_, id| { + debug_assert_eq!(id, OPEN_RESPONSE_ID); + + let dialog = PreferencesDialog::new(Application::get().settings()); + dialog.present(Some(&obj)); + + let is_focused = dialog.focus_aud_d_api_token_row(); + debug_assert!(is_focused, "token row must be focused"); + } + ), ); } RecognizeErrorKind::NoMatches @@ -433,31 +450,46 @@ impl Window { fn setup_signals(&self) { let imp = self.imp(); - imp.player - .connect_song_notify(clone!(@weak self as obj => move |_| { + imp.player.connect_song_notify(clone!( + #[weak(rename_to = obj)] + self, + move |_| { obj.update_toggle_playback_action(); obj.update_song_bar_revealer(); - })); - imp.player - .connect_error(clone!(@weak self as obj => move |_, _| { + } + )); + imp.player.connect_error(clone!( + #[weak(rename_to = obj)] + self, + move |_, _| { obj.add_message_toast(&gettext("An error occurred in the player")); - })); + } + )); - imp.song_bar - .connect_activated(clone!(@weak self as obj => move |_, song| { + imp.song_bar.connect_activated(clone!( + #[weak(rename_to = obj)] + self, + move |_, song| { obj.imp().main_view.push_song_page(song); - })); - - imp.main_view.connect_is_selection_mode_active_notify( - clone!(@weak self as obj => move |_| { - obj.update_song_bar_revealer(); - }), - ); + } + )); + + imp.main_view + .connect_is_selection_mode_active_notify(clone!( + #[weak(rename_to = obj)] + self, + move |_| { + obj.update_song_bar_revealer(); + } + )); - imp.stack - .connect_visible_child_notify(clone!(@weak self as obj => move |_| { + imp.stack.connect_visible_child_notify(clone!( + #[weak(rename_to = obj)] + self, + move |_| { obj.update_toggle_search_action(); - })); + } + )); } fn update_song_bar_revealer(&self) { diff --git a/src/window/progress_icon.rs b/src/window/progress_icon.rs index 3b335f52..b4f83e47 100644 --- a/src/window/progress_icon.rs +++ b/src/window/progress_icon.rs @@ -41,12 +41,15 @@ mod imp { let obj = self.obj(); - let animation_target = - adw::CallbackAnimationTarget::new(clone!(@weak obj => move |value| { + let animation_target = adw::CallbackAnimationTarget::new(clone!( + #[weak] + obj, + move |value| { let imp = obj.imp(); imp.display_progress.set(value); obj.queue_draw(); - })); + } + )); let animation = adw::TimedAnimation::builder() .widget(&*obj) .duration(ANIMATION_DURATION_MS) diff --git a/src/window/recognized_page.rs b/src/window/recognized_page.rs index 9c5a21c3..99ec27fe 100644 --- a/src/window/recognized_page.rs +++ b/src/window/recognized_page.rs @@ -160,9 +160,13 @@ impl RecognizedPage { for song in songs { let tile = RecognizedPageTile::new(song); tile.bind_player(&player); - tile.connect_activated(clone!(@weak self as obj => move |tile| { - obj.emit_by_name::<()>("song-activated", &[&tile.song()]); - })); + tile.connect_activated(clone!( + #[weak(rename_to = obj)] + self, + move |tile| { + obj.emit_by_name::<()>("song-activated", &[&tile.song()]); + } + )); imp.carousel.append(&tile); imp.tiles.borrow_mut().push(tile); diff --git a/src/window/recognized_page_tile.rs b/src/window/recognized_page_tile.rs index 8943bdae..f516b3c2 100644 --- a/src/window/recognized_page_tile.rs +++ b/src/window/recognized_page_tile.rs @@ -67,12 +67,16 @@ mod imp { let gesture_click = gtk::GestureClick::builder() .button(gdk::BUTTON_PRIMARY) .build(); - gesture_click.connect_released(clone!(@weak obj => move |gesture, _, x, y| { - gesture.set_state(gtk::EventSequenceState::Claimed); - if gesture.widget().is_some_and(|w| w.contains(x, y)) { - obj.emit_by_name::<()>("activated", &[]); + gesture_click.connect_released(clone!( + #[weak] + obj, + move |gesture, _, x, y| { + gesture.set_state(gtk::EventSequenceState::Claimed); + if gesture.widget().is_some_and(|w| w.contains(x, y)) { + obj.emit_by_name::<()>("activated", &[]); + } } - })); + )); self.song_tile.add_controller(gesture_click); } diff --git a/src/window/recognizer_status.rs b/src/window/recognizer_status.rs index d74a7615..408ef530 100644 --- a/src/window/recognizer_status.rs +++ b/src/window/recognizer_status.rs @@ -67,10 +67,13 @@ mod imp { let obj = self.obj(); - self.show_results_button - .connect_clicked(clone!(@weak obj => move |_| { + self.show_results_button.connect_clicked(clone!( + #[weak] + obj, + move |_| { obj.emit_by_name::<()>("show-results-requested", &[]); - })); + } + )); } fn dispose(&self) { @@ -106,15 +109,21 @@ impl RecognizerStatus { /// Must be called only once pub fn bind_recognizer(&self, recognizer: &Recognizer) { - recognizer.connect_is_offline_mode_notify(clone!(@weak self as obj => move |_| { - obj.update_offline_mode_ui(); - })); - - recognizer.saved_recordings().connect_items_changed( - clone!(@weak self as obj => move |_, _, _, _| { + recognizer.connect_is_offline_mode_notify(clone!( + #[weak(rename_to = obj)] + self, + move |_| { + obj.update_offline_mode_ui(); + } + )); + + recognizer.saved_recordings().connect_items_changed(clone!( + #[weak(rename_to = obj)] + self, + move |_, _, _, _| { obj.update_progress_and_show_results_ui(); - }), - ); + } + )); self.imp().recognizer.set(recognizer.clone()).unwrap(); diff --git a/src/window/recognizer_view.rs b/src/window/recognizer_view.rs index a3f16f47..d95cb374 100644 --- a/src/window/recognizer_view.rs +++ b/src/window/recognizer_view.rs @@ -65,17 +65,29 @@ impl RecognizerView { /// Must be only called once pub fn bind_recognizer(&self, recognizer: &Recognizer) { - recognizer.connect_is_offline_mode_notify(clone!(@weak self as obj => move |_| { - obj.update_offline_mode_ui(); - })); + recognizer.connect_is_offline_mode_notify(clone!( + #[weak(rename_to = obj)] + self, + move |_| { + obj.update_offline_mode_ui(); + } + )); - recognizer.connect_state_notify(clone!(@weak self as obj => move |_| { - obj.update_ui(); - })); + recognizer.connect_state_notify(clone!( + #[weak(rename_to = obj)] + self, + move |_| { + obj.update_ui(); + } + )); - recognizer.connect_recording_peak_changed(clone!(@weak self as obj => move |_, peak| { - obj.imp().waveform.push_peak(peak); - })); + recognizer.connect_recording_peak_changed(clone!( + #[weak(rename_to = obj)] + self, + move |_, peak| { + obj.imp().waveform.push_peak(peak); + } + )); self.imp().recognizer.set(recognizer.clone()).unwrap(); @@ -98,11 +110,13 @@ impl RecognizerView { .value_from(0.0) .value_to(0.8) .duration(1500) - .target(&adw::CallbackAnimationTarget::new( - clone!(@weak self as obj => move |value| { + .target(&adw::CallbackAnimationTarget::new(clone!( + #[weak(rename_to = obj)] + self, + move |value| { obj.imp().waveform.push_peak(value); - }), - )) + } + ))) .easing(adw::Easing::EaseOutBack) .repeat_count(u32::MAX) .alternate(true) diff --git a/src/window/song_bar.rs b/src/window/song_bar.rs index 9e73fdc2..b085094e 100644 --- a/src/window/song_bar.rs +++ b/src/window/song_bar.rs @@ -91,11 +91,13 @@ mod imp { let obj = self.obj(); self.scale_handler_id - .set(self.playback_position_scale.connect_value_changed( - clone!(@weak obj => move |scale| { + .set(self.playback_position_scale.connect_value_changed(clone!( + #[weak] + obj, + move |scale| { obj.on_playback_position_scale_value_changed(scale); - }), - )) + } + ))) .unwrap(); } @@ -136,23 +138,39 @@ impl SongBar { imp.player.set(player.clone()).unwrap(); - player.connect_song_notify(clone!(@weak self as obj => move |_| { - obj.update_song_ui(); - })); - - player.connect_state_notify(clone!(@weak self as obj => move |_| { - obj.update_playback_button(); - })); - - player.connect_position_notify(clone!(@weak self as obj => move |_| { - obj.update_playback_position(); - obj.update_playback_position_duration_label(); - })); + player.connect_song_notify(clone!( + #[weak(rename_to = obj)] + self, + move |_| { + obj.update_song_ui(); + } + )); - player.connect_duration_notify(clone!(@weak self as obj => move |_| { - obj.update_playback_position_scale_range(); - obj.update_playback_position_duration_label(); - })); + player.connect_state_notify(clone!( + #[weak(rename_to = obj)] + self, + move |_| { + obj.update_playback_button(); + } + )); + + player.connect_position_notify(clone!( + #[weak(rename_to = obj)] + self, + move |_| { + obj.update_playback_position(); + obj.update_playback_position_duration_label(); + } + )); + + player.connect_duration_notify(clone!( + #[weak(rename_to = obj)] + self, + move |_| { + obj.update_playback_position_scale_range(); + obj.update_playback_position_duration_label(); + } + )); self.update_song_ui(); self.update_playback_button(); @@ -187,10 +205,14 @@ impl SongBar { imp.seek_timeout_id .replace(Some(glib::timeout_add_local_once( Duration::from_millis(20), - clone!(@weak self as obj => move || { - obj.imp().seek_timeout_id.replace(None); - obj.player().seek(gst::ClockTime::from_seconds_f64(value)); - }), + clone!( + #[weak(rename_to = obj)] + self, + move || { + obj.imp().seek_timeout_id.replace(None); + obj.player().seek(gst::ClockTime::from_seconds_f64(value)); + } + ), ))); } diff --git a/src/window/song_page.rs b/src/window/song_page.rs index f87b0079..b829560c 100644 --- a/src/window/song_page.rs +++ b/src/window/song_page.rs @@ -101,16 +101,22 @@ mod imp { let obj = self.obj(); - self.remove_button - .connect_clicked(clone!(@weak obj => move |_| { + self.remove_button.connect_clicked(clone!( + #[weak] + obj, + move |_| { if let Some(ref song) = obj.song() { obj.emit_by_name::<()>("song-remove-request", &[song]); } - })); - self.playback_button - .connect_clicked(clone!(@weak obj => move |_| { + } + )); + self.playback_button.connect_clicked(clone!( + #[weak] + obj, + move |_| { obj.toggle_playback(); - })); + } + )); self.external_links_box.connect_child_activated(|_, child| { let external_link_tile = child.downcast_ref::().unwrap(); @@ -212,9 +218,13 @@ impl SongPage { /// Must only be called when no player was already bound. pub fn bind_player(&self, player: &Player) { - let handler_id = player.connect_state_notify(clone!(@weak self as obj => move |_| { - obj.update_playback_ui(); - })); + let handler_id = player.connect_state_notify(clone!( + #[weak(rename_to = obj)] + self, + move |_| { + obj.update_playback_ui(); + } + )); self.imp() .player @@ -233,11 +243,13 @@ impl SongPage { /// Must only be called when no song list was already bound. pub fn bind_song_list(&self, song_list: &SongList) { - let handler_id = song_list.connect_items_changed( - clone!(@weak self as obj => move |_, _index, _removed, _added| { + let handler_id = song_list.connect_items_changed(clone!( + #[weak(rename_to = obj)] + self, + move |_, _index, _removed, _added| { obj.update_remove_button_sensitivity(); - }), - ); + } + )); self.imp() .song_list diff --git a/src/window/song_tile.rs b/src/window/song_tile.rs index d9883ab3..cab99fb5 100644 --- a/src/window/song_tile.rs +++ b/src/window/song_tile.rs @@ -107,49 +107,66 @@ mod imp { let obj = self.obj(); let motion_controller = gtk::EventControllerMotion::new(); - motion_controller.connect_enter(clone!(@weak obj => move |_, _, _| { - obj.imp().contains_pointer.set(true); - obj.update_select_button_visibility(); - })); - motion_controller.connect_leave(clone!(@weak obj => move |_| { - obj.imp().contains_pointer.set(false); - obj.update_select_button_visibility(); - })); + motion_controller.connect_enter(clone!( + #[weak] + obj, + move |_, _, _| { + obj.imp().contains_pointer.set(true); + obj.update_select_button_visibility(); + } + )); + motion_controller.connect_leave(clone!( + #[weak] + obj, + move |_| { + obj.imp().contains_pointer.set(false); + obj.update_select_button_visibility(); + } + )); obj.add_controller(motion_controller); let gesture_click = gtk::GestureClick::builder() .button(gdk::BUTTON_SECONDARY) .build(); - gesture_click.connect_released(clone!(@weak obj => move |gesture, _, x, y| { - gesture.set_state(gtk::EventSequenceState::Claimed); - if obj.contains(x, y) { - obj.emit_by_name::<()>("selection-mode-requested", &[]); + gesture_click.connect_released(clone!( + #[weak] + obj, + move |gesture, _, x, y| { + gesture.set_state(gtk::EventSequenceState::Claimed); + if obj.contains(x, y) { + obj.emit_by_name::<()>("selection-mode-requested", &[]); + } } - })); + )); obj.add_controller(gesture_click); let gesture_long_press = gtk::GestureLongPress::builder() .propagation_phase(gtk::PropagationPhase::Capture) .build(); - gesture_long_press.connect_pressed(clone!(@weak obj => move |gesture, x, y| { - gesture.set_state(gtk::EventSequenceState::Claimed); - if obj.contains(x, y) { - obj.emit_by_name::<()>("selection-mode-requested", &[]); + gesture_long_press.connect_pressed(clone!( + #[weak] + obj, + move |gesture, x, y| { + gesture.set_state(gtk::EventSequenceState::Claimed); + if obj.contains(x, y) { + obj.emit_by_name::<()>("selection-mode-requested", &[]); + } } - })); + )); obj.add_controller(gesture_long_press); self.select_button_active_notify_handler_id - .set( - self.select_button - .connect_active_notify(clone!(@weak obj => move |button| { - if button.is_active() && !obj.is_selection_mode_active() { - obj.emit_by_name::<()>("selection-mode-requested", &[]); - } - - obj.notify_is_active(); - })), - ) + .set(self.select_button.connect_active_notify(clone!( + #[weak] + obj, + move |button| { + if button.is_active() && !obj.is_selection_mode_active() { + obj.emit_by_name::<()>("selection-mode-requested", &[]); + } + + obj.notify_is_active(); + } + ))) .unwrap(); self.song_binding_group @@ -279,9 +296,13 @@ impl SongTile { /// Must only be called once. pub fn bind_player(&self, player: &Player) { - let handler_id = player.connect_state_notify(clone!(@weak self as obj => move |player| { - obj.update_playback_ui(player); - })); + let handler_id = player.connect_state_notify(clone!( + #[weak(rename_to = obj)] + self, + move |player| { + obj.update_playback_ui(player); + } + )); self.imp() .player