diff --git a/src/file_model.rs b/src/file_model.rs index abb42b2..7c5c805 100644 --- a/src/file_model.rs +++ b/src/file_model.rs @@ -99,11 +99,13 @@ impl LpFileModel { directory.monitor_directory(gio::FileMonitorFlags::WATCH_MOVES, gio::Cancellable::NONE); match monitor { Ok(monitor) => { - monitor.connect_changed( - glib::clone!(@weak self as obj => move |_monitor, file_a, file_b, event| { + monitor.connect_changed(glib::clone!( + #[weak(rename_to = obj)] + self, + move |_monitor, file_a, file_b, event| { obj.file_monitor_cb(event, file_a, file_b); - }), - ); + } + )); self.imp().monitor.set(monitor).unwrap(); } Err(err) => { diff --git a/src/widgets/drag_overlay.rs b/src/widgets/drag_overlay.rs index 30dd57b..c476c6d 100644 --- a/src/widgets/drag_overlay.rs +++ b/src/widgets/drag_overlay.rs @@ -98,12 +98,14 @@ mod imp { } pub fn set_drop_target(&self, drop_target: gtk::DropTarget) { - drop_target.connect_current_drop_notify( - glib::clone!(@weak self.revealer as revealer => move |target| { + 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); - }), - ); + } + )); self.drop_target.set(drop_target).unwrap(); diff --git a/src/widgets/error_details.rs b/src/widgets/error_details.rs index ec65873..5661dac 100644 --- a/src/widgets/error_details.rs +++ b/src/widgets/error_details.rs @@ -70,23 +70,29 @@ impl LpErrorDetails { let imp = obj.imp(); imp.message.buffer().set_text(text); - imp.copy - .connect_clicked(glib::clone!(@weak obj => move |_| { + imp.copy.connect_clicked(glib::clone!( + #[weak] + obj, + move |_| { let buffer = obj.imp().message.buffer(); let (start, end) = buffer.bounds(); obj.display() .clipboard() .set_text(buffer.text(&start, &end, true).as_str()) - })); + } + )); - imp.report - .connect_clicked(glib::clone!(@weak obj => move |_| { + imp.report.connect_clicked(glib::clone!( + #[weak] + obj, + move |_| { gtk::UriLauncher::new("https://gitlab.gnome.org/sophie-h/glycin/-/issues").launch( obj.root().and_downcast_ref::(), gio::Cancellable::NONE, |_| {}, ); - })); + } + )); obj.present(Some(root)); obj diff --git a/src/widgets/image.rs b/src/widgets/image.rs index de94504..fc21001 100644 --- a/src/widgets/image.rs +++ b/src/widgets/image.rs @@ -289,31 +289,47 @@ mod imp { surface_signals.connect_notify_local( Some("scale"), - glib::clone!(@weak obj => move |_, _| { - log::debug!("Scale changed signal"); - obj.queue_resize(); - }), + glib::clone!( + #[weak] + obj, + move |_, _| { + log::debug!("Scale changed signal"); + obj.queue_resize(); + } + ), ); - obj.connect_realize(glib::clone!(@weak surface_signals => move |obj| { - surface_signals.set_target(obj.native().and_then(|x| x.surface()).as_ref()); - })); + obj.connect_realize(glib::clone!( + #[weak] + surface_signals, + move |obj| { + surface_signals.set_target(obj.native().and_then(|x| x.surface()).as_ref()); + } + )); - obj.connect_unrealize(glib::clone!(@weak surface_signals => move |_| { - surface_signals.set_target(gdk::Surface::NONE); - })); + obj.connect_unrealize(glib::clone!( + #[weak] + surface_signals, + move |_| { + surface_signals.set_target(gdk::Surface::NONE); + } + )); - adw::StyleManager::default().connect_dark_notify(glib::clone!(@weak obj => move |_| { - glib::spawn_future_local(async move { - let imp = obj.imp(); + adw::StyleManager::default().connect_dark_notify(glib::clone!( + #[weak] + obj, + move |_| { + glib::spawn_future_local(async move { + let imp = obj.imp(); - let color = imp.background_color_guess().await; - imp.set_background_color(color); - if obj.is_mapped() { - obj.queue_draw(); - } - }); - })); + let color = imp.background_color_guess().await; + imp.set_background_color(color); + if obj.is_mapped() { + obj.queue_draw(); + } + }); + } + )); } fn dispose(&self) { diff --git a/src/widgets/image/input_handling.rs b/src/widgets/image/input_handling.rs index 75d8e8f..4cff0b0 100644 --- a/src/widgets/image/input_handling.rs +++ b/src/widgets/image/input_handling.rs @@ -29,75 +29,102 @@ impl imp::LpImage { // Needed for having the current cursor position available let motion_controller = gtk::EventControllerMotion::new(); - motion_controller.connect_enter(glib::clone!(@weak obj => move |_, x, y| { - obj.imp().pointer_position.set(Some((x, y))); - })); - motion_controller.connect_motion(glib::clone!(@weak obj => move |_, x, y| { - obj.imp().pointer_position.set(Some((x, y))); - })); - motion_controller.connect_leave(glib::clone!(@weak obj => move |_| { - obj.imp().pointer_position.set(None); - })); + motion_controller.connect_enter(glib::clone!( + #[weak] + obj, + move |_, x, y| { + obj.imp().pointer_position.set(Some((x, y))); + } + )); + motion_controller.connect_motion(glib::clone!( + #[weak] + obj, + move |_, x, y| { + obj.imp().pointer_position.set(Some((x, y))); + } + )); + motion_controller.connect_leave(glib::clone!( + #[weak] + obj, + move |_| { + obj.imp().pointer_position.set(None); + } + )); obj.add_controller(motion_controller); // Zoom via scroll wheels etc let scroll_controller = gtk::EventControllerScroll::new(gtk::EventControllerScrollFlags::BOTH_AXES); - scroll_controller.connect_scroll_end(glib::clone!(@weak obj => move |event| { - // Avoid kinetc scrolling in scrolled window after zooming - if event.current_event_state().contains(gdk::ModifierType::CONTROL_MASK) { - obj.imp().cancel_deceleration(); - } - })); - - scroll_controller.connect_scroll(glib::clone!(@weak obj => @default-return glib::Propagation::Proceed, move |event, _, y| { - let imp = obj.imp(); - let state = event.current_event_state(); - if event.current_event_device().map(|x| x.source()) == Some(gdk::InputSource::Touchpad) - { - // Touchpads do zoom via gestures, expect when Ctrl key is pressed - if !state.contains(gdk::ModifierType::CONTROL_MASK) { - // propagate event to scrolled window - return glib::Propagation::Proceed; - } - } else { - // use Ctrl key as modifier for vertical scrolling - if state.contains(gdk::ModifierType::CONTROL_MASK) - || state.contains(gdk::ModifierType::SHIFT_MASK) + scroll_controller.connect_scroll_end(glib::clone!( + #[weak] + obj, + move |event| { + // Avoid kinetc scrolling in scrolled window after zooming + if event + .current_event_state() + .contains(gdk::ModifierType::CONTROL_MASK) { - // propagate event to scrolled window - return glib::Propagation::Proceed; + obj.imp().cancel_deceleration(); } } + )); - // Use exponential scaling since zoom is always multiplicative with the existing value - // This is the right thing since `exp(n/2)^2 == exp(n)` (two small steps are the same as one larger step) - let (zoom_factor, animated) = match event.unit() { - gdk::ScrollUnit::Wheel => ( - f64::exp(-y * f64::ln(ZOOM_FACTOR_SCROLL_WHEEL)), - y.abs() >= 1., - ), - gdk::ScrollUnit::Surface => { - (f64::exp(-y * f64::ln(ZOOM_FACTOR_SCROLL_SURFACE)), false) + scroll_controller.connect_scroll(glib::clone!( + #[weak] + obj, + #[upgrade_or_else] + || glib::Propagation::Proceed, + move |event, _, y| { + let imp = obj.imp(); + let state = event.current_event_state(); + if event.current_event_device().map(|x| x.source()) + == Some(gdk::InputSource::Touchpad) + { + // Touchpads do zoom via gestures, expect when Ctrl key is pressed + if !state.contains(gdk::ModifierType::CONTROL_MASK) { + // propagate event to scrolled window + return glib::Propagation::Proceed; + } + } else { + // use Ctrl key as modifier for vertical scrolling + if state.contains(gdk::ModifierType::CONTROL_MASK) + || state.contains(gdk::ModifierType::SHIFT_MASK) + { + // propagate event to scrolled window + return glib::Propagation::Proceed; + } } - unknown_unit => { - log::warn!("Ignoring unknown scroll unit: {unknown_unit:?}"); - (1., false) + + // Use exponential scaling since zoom is always multiplicative with the existing + // value This is the right thing since `exp(n/2)^2 == exp(n)` + // (two small steps are the same as one larger step) + let (zoom_factor, animated) = match event.unit() { + gdk::ScrollUnit::Wheel => ( + f64::exp(-y * f64::ln(ZOOM_FACTOR_SCROLL_WHEEL)), + y.abs() >= 1., + ), + gdk::ScrollUnit::Surface => { + (f64::exp(-y * f64::ln(ZOOM_FACTOR_SCROLL_SURFACE)), false) + } + unknown_unit => { + log::warn!("Ignoring unknown scroll unit: {unknown_unit:?}"); + (1., false) + } + }; + + let zoom = imp.zoom_target.get() * zoom_factor; + + if animated { + obj.zoom_to(zoom); + } else { + imp.zoom_to_full(zoom, false, false, false); } - }; - let zoom = imp.zoom_target.get() * zoom_factor; - - if animated { - obj.zoom_to(zoom); - } else { - imp.zoom_to_full(zoom, false, false, false); + // do not propagate event to scrolled window + glib::Propagation::Stop } - - // do not propagate event to scrolled window - glib::Propagation::Stop - })); + )); obj.add_controller(scroll_controller); } @@ -108,8 +135,10 @@ impl imp::LpImage { // Double click for fullscreen (mouse/touchpad) or zoom (touch screen) let left_click_gesture = gtk::GestureClick::builder().button(1).build(); obj.add_controller(left_click_gesture.clone()); - left_click_gesture.connect_pressed( - glib::clone!(@weak obj => move |gesture, n_press, x, y| { + left_click_gesture.connect_pressed(glib::clone!( + #[weak] + obj, + move |gesture, n_press, x, y| { // only handle double clicks if n_press != 2 { return; @@ -129,150 +158,184 @@ impl imp::LpImage { // fullscreen obj.activate_action("win.toggle-fullscreen", None).unwrap(); } - }), - ); + } + )); // Drag for moving image around let drag_gesture = gtk::GestureDrag::builder().button(0).build(); obj.add_controller(drag_gesture.clone()); - drag_gesture.connect_drag_begin(glib::clone!(@weak obj => move |gesture, _, _| { - let imp = obj.imp(); + drag_gesture.connect_drag_begin(glib::clone!( + #[weak] + obj, + move |gesture, _, _| { + let imp = obj.imp(); - // Allow only left and middle button - if ![1, 2].contains(&gesture.current_button()) + // Allow only left and middle button + if ![1, 2].contains(&gesture.current_button()) // Drag gesture for touchscreens is handled by ScrolledWindow || gesture.device().map(|x| x.source()) == Some(gdk::InputSource::Touchscreen) - { - gesture.set_state(gtk::EventSequenceState::Denied); - return; + { + gesture.set_state(gtk::EventSequenceState::Denied); + return; + } + + if obj.is_hscrollable() || obj.is_vscrollable() { + imp.cancel_deceleration(); + obj.set_cursor(gdk::Cursor::from_name("grabbing", None).as_ref()); + imp.last_drag_value.set(Some((0., 0.))); + } else { + // let drag and drop handle the events when not scrollable + gesture.set_state(gtk::EventSequenceState::Denied); + } } + )); - if obj.is_hscrollable() || obj.is_vscrollable() { - imp.cancel_deceleration(); - obj.set_cursor(gdk::Cursor::from_name("grabbing", None).as_ref()); - imp.last_drag_value.set(Some((0., 0.))); - } else { - // let drag and drop handle the events when not scrollable - gesture.set_state(gtk::EventSequenceState::Denied); + drag_gesture.connect_drag_update(glib::clone!( + #[weak] + obj, + move |_, x1, y1| { + let imp = obj.imp(); + if let Some((x0, y0)) = obj.imp().last_drag_value.get() { + imp.set_hadj_value(imp.hadj_value() - x1 + x0); + imp.set_vadj_value(imp.vadj_value() - y1 + y0); + } + + obj.imp().last_drag_value.set(Some((x1, y1))); } - })); + )); - drag_gesture.connect_drag_update(glib::clone!(@weak obj => move |_, x1, y1| { - let imp = obj.imp(); - if let Some((x0, y0)) = obj.imp().last_drag_value.get() { - imp.set_hadj_value(imp.hadj_value() - x1 + x0); - imp.set_vadj_value(imp.vadj_value() - y1 + y0); + drag_gesture.connect_drag_end(glib::clone!( + #[weak] + obj, + move |_, _, _| { + obj.set_cursor(None); + obj.imp().last_drag_value.set(None); } - - obj.imp().last_drag_value.set(Some((x1, y1))); - })); - - drag_gesture.connect_drag_end(glib::clone!(@weak obj => move |_, _, _| { - obj.set_cursor(None); - obj.imp().last_drag_value.set(None); - })); + )); // Rotate let rotation_gesture = gtk::GestureRotate::new(); obj.add_controller(rotation_gesture.clone()); - rotation_gesture - .connect_begin(glib::clone!(@weak obj => move |_, _| obj.imp().cancel_deceleration())); + rotation_gesture.connect_begin(glib::clone!( + #[weak] + obj, + move |_, _| obj.imp().cancel_deceleration() + )); - rotation_gesture.connect_angle_changed(glib::clone!(@weak obj => move |gesture, _, _| { - let angle = gesture.angle_delta().to_degrees(); + rotation_gesture.connect_angle_changed(glib::clone!( + #[weak] + obj, + move |gesture, _, _| { + let angle = gesture.angle_delta().to_degrees(); - // Only reset rotation if scale gesture is locked in - if let Some(Gesture::Scale) = obj.imp().locked_gestured.get() { - obj.imp().rotation.set(obj.imp().rotation_target.get()); - return; - } - - // Correct angle by the the angle at the moment of passing the threshold. - // This stops the rotation from suddenly jumping when passing the threshold. - let correction = - if let Some(Gesture::Rotate(correction)) = obj.imp().locked_gestured.get() { - correction - } else if angle.abs() > ROTATE_GESTURE_LOCK_THRESHOLD { - let correction = angle.signum() * ROTATE_GESTURE_LOCK_THRESHOLD; - obj.imp() - .locked_gestured - .set(Some(Gesture::Rotate(correction))); - correction - } else { + // Only reset rotation if scale gesture is locked in + if let Some(Gesture::Scale) = obj.imp().locked_gestured.get() { + obj.imp().rotation.set(obj.imp().rotation_target.get()); return; - }; + } - obj.set_rotation(obj.imp().rotation_target.get() + angle - correction); - })); + // Correct angle by the the angle at the moment of passing the threshold. + // This stops the rotation from suddenly jumping when passing the threshold. + let correction = + if let Some(Gesture::Rotate(correction)) = obj.imp().locked_gestured.get() { + correction + } else if angle.abs() > ROTATE_GESTURE_LOCK_THRESHOLD { + let correction = angle.signum() * ROTATE_GESTURE_LOCK_THRESHOLD; + obj.imp() + .locked_gestured + .set(Some(Gesture::Rotate(correction))); + correction + } else { + return; + }; - rotation_gesture.connect_end(glib::clone!(@weak obj => move |_, _| { - log::debug!("Rotate gesture ended"); + obj.set_rotation(obj.imp().rotation_target.get() + angle - correction); + } + )); - let angle = (obj.rotation() / 90.).round() * 90. - obj.imp().rotation_target.get(); - obj.rotate_by(angle); - obj.imp().locked_gestured.set(None); - })); + rotation_gesture.connect_end(glib::clone!( + #[weak] + obj, + move |_, _| { + log::debug!("Rotate gesture ended"); + + let angle = (obj.rotation() / 90.).round() * 90. - obj.imp().rotation_target.get(); + obj.rotate_by(angle); + obj.imp().locked_gestured.set(None); + } + )); // Zoom let zoom_gesture = gtk::GestureZoom::new(); obj.add_controller(zoom_gesture.clone()); - zoom_gesture.connect_begin(glib::clone!(@weak obj => move |gesture, _| { - let imp = obj.imp(); - imp.cancel_deceleration(); - imp.zoom_gesture_center.set(gesture.bounding_box_center()); - })); + zoom_gesture.connect_begin(glib::clone!( + #[weak] + obj, + move |gesture, _| { + let imp = obj.imp(); + imp.cancel_deceleration(); + imp.zoom_gesture_center.set(gesture.bounding_box_center()); + } + )); - zoom_gesture.connect_scale_changed(glib::clone!(@weak obj => move |gesture, scale| { - let imp = obj.imp(); + zoom_gesture.connect_scale_changed(glib::clone!( + #[weak] + obj, + move |gesture, scale| { + let imp = obj.imp(); - let zoom = imp.zoom_target.get() * scale; + let zoom = imp.zoom_target.get() * scale; - // Move image with fingers on touchscreens - if gesture.device().map(|x| x.source()) == Some(gdk::InputSource::Touchscreen) { - if let p1 @ Some((x1, y1)) = gesture.bounding_box_center() { + // Move image with fingers on touchscreens + if gesture.device().map(|x| x.source()) == Some(gdk::InputSource::Touchscreen) { + if let p1 @ Some((x1, y1)) = gesture.bounding_box_center() { + if let Some((x0, y0)) = imp.zoom_gesture_center.get() { + imp.set_hadj_value(imp.hadj_value() + x0 - x1); + imp.set_vadj_value(imp.vadj_value() + y0 - y1); + } else { + log::warn!("Zoom bounding box center: No previous value"); + } - if let Some((x0, y0)) = imp.zoom_gesture_center.get() { - imp.set_hadj_value(imp.hadj_value() + x0 - x1); - imp.set_vadj_value(imp.vadj_value()+ y0 - y1); - } else { - log::warn!("Zoom bounding box center: No previous value"); + imp.zoom_gesture_center.set(p1); } - - imp.zoom_gesture_center.set(p1); } + + let zoom_out_threshold = 1. / ZOOM_GESTURE_LOCK_THRESHOLD; + let zoom_in_threshold = ZOOM_GESTURE_LOCK_THRESHOLD; + + if let Some(Gesture::Rotate(_)) = imp.locked_gestured.get() { + // Do not zoom when rotate is locked in + return; + } else if !(zoom_out_threshold..zoom_in_threshold).contains(&scale) { + // Lock in scale when leaving the scale threshold + imp.locked_gestured.set(Some(Gesture::Scale)); + } + + imp.set_zoom_aiming(zoom, imp.zoom_gesture_center.get()); } + )); - let zoom_out_threshold = 1. / ZOOM_GESTURE_LOCK_THRESHOLD; - let zoom_in_threshold = ZOOM_GESTURE_LOCK_THRESHOLD; + zoom_gesture.connect_end(glib::clone!( + #[weak] + obj, + move |_, _| { + let imp = obj.imp(); + log::debug!("Zoom gesture ended"); - if let Some(Gesture::Rotate(_)) = imp.locked_gestured.get() { - // Do not zoom when rotate is locked in - return; - } else if !(zoom_out_threshold..zoom_in_threshold).contains(&scale) { - // Lock in scale when leaving the scale threshold - imp.locked_gestured.set(Some(Gesture::Scale)); + let rotation_target = (obj.rotation() / 90.).round() * 90.; + if obj.zoom() < imp.zoom_level_best_fit_for_rotation(rotation_target) { + obj.zoom_to(imp.zoom_level_best_fit_for_rotation(rotation_target)); + } else { + // rubberband if over highest zoom level and sets `zoom_target` + obj.zoom_to(obj.zoom()); + }; + + imp.locked_gestured.set(None); } - - imp.set_zoom_aiming(zoom, imp.zoom_gesture_center.get()); - })); - - zoom_gesture.connect_end(glib::clone!(@weak obj => move |_, _| { - let imp = obj.imp(); - log::debug!("Zoom gesture ended"); - - let rotation_target = (obj.rotation() / 90.).round() * 90.; - if obj.zoom() < imp.zoom_level_best_fit_for_rotation(rotation_target) { - obj.zoom_to(imp.zoom_level_best_fit_for_rotation(rotation_target)); - } else { - // rubberband if over highest zoom level and sets `zoom_target` - obj.zoom_to(obj.zoom()); - }; - - imp.locked_gestured.set(None); - })); + )); zoom_gesture.group_with(&rotation_gesture); } diff --git a/src/widgets/image/loading.rs b/src/widgets/image/loading.rs index 49a38bd..894a98e 100644 --- a/src/widgets/image/loading.rs +++ b/src/widgets/image/loading.rs @@ -52,9 +52,13 @@ impl imp::LpImage { let monitor = file.monitor_file(gio::FileMonitorFlags::WATCH_MOVES, gio::Cancellable::NONE); if let Ok(m) = &monitor { - m.connect_changed(glib::clone!(@weak obj => move |_, file_a, file_b, event| { - obj.imp().file_changed(event, file_a, file_b); - })); + m.connect_changed(glib::clone!( + #[weak] + obj, + move |_, file_a, file_b, event| { + obj.imp().file_changed(event, file_a, file_b); + } + )); } self.file_monitor.replace(monitor.ok()); @@ -90,15 +94,19 @@ impl imp::LpImage { new_tiles }); if self.background_color.borrow().is_none() { - glib::spawn_future_local(glib::clone!(@weak obj => async move { - let imp = obj.imp(); + glib::spawn_future_local(glib::clone!( + #[weak] + obj, + async move { + let imp = obj.imp(); - let color = imp.background_color_guess().await; - imp.set_background_color(color); - if obj.is_mapped() { - obj.queue_draw(); + let color = imp.background_color_guess().await; + imp.set_background_color(color); + if obj.is_mapped() { + obj.queue_draw(); + } } - })); + )); } } DecoderUpdate::Error(err) => { @@ -109,8 +117,13 @@ impl imp::LpImage { // Just load the first frame self.frame_buffer.next_frame(); } else { - let callback_id = obj - .add_tick_callback(glib::clone!(@weak self as obj => @default-return glib::ControlFlow::Break, move |_, clock| obj.tick_callback(clock))); + let callback_id = obj.add_tick_callback(glib::clone!( + #[weak(rename_to = obj)] + self, + #[upgrade_or_else] + || glib::ControlFlow::Break, + move |_, clock| obj.tick_callback(clock) + )); self.tick_callback.replace(Some(callback_id)); } } diff --git a/src/widgets/image/scrollable.rs b/src/widgets/image/scrollable.rs index b1da248..3ebef4e 100644 --- a/src/widgets/image/scrollable.rs +++ b/src/widgets/image/scrollable.rs @@ -28,10 +28,14 @@ impl imp::LpImage { let adjustment = hadjustment.unwrap_or_default(); - adjustment.connect_value_changed(glib::clone!(@weak obj => move |_| { - obj.imp().request_tiles(); - obj.queue_draw(); - })); + adjustment.connect_value_changed(glib::clone!( + #[weak] + obj, + move |_| { + obj.imp().request_tiles(); + obj.queue_draw(); + } + )); self.hadjustment.replace(adjustment); self.configure_adjustments(); @@ -42,10 +46,14 @@ impl imp::LpImage { let adjustment = vadjustment.unwrap_or_default(); - adjustment.connect_value_changed(glib::clone!(@weak obj => move |_| { - obj.imp().request_tiles(); - obj.queue_draw(); - })); + adjustment.connect_value_changed(glib::clone!( + #[weak] + obj, + move |_| { + obj.imp().request_tiles(); + obj.queue_draw(); + } + )); self.vadjustment.replace(adjustment); self.configure_adjustments(); diff --git a/src/widgets/image/zoom.rs b/src/widgets/image/zoom.rs index 59d05ae..d78900c 100644 --- a/src/widgets/image/zoom.rs +++ b/src/widgets/image/zoom.rs @@ -264,11 +264,13 @@ impl imp::LpImage { let animation = self.zoom_animation(); if force_cursor_center { - animation.set_target(&adw::CallbackAnimationTarget::new( - glib::clone!(@weak obj => move |zoom| { + animation.set_target(&adw::CallbackAnimationTarget::new(glib::clone!( + #[weak] + obj, + move |zoom| { obj.imp().set_zoom_aiming(zoom, None); - }), - )); + } + ))); } else { // Set new point in image that should remain under the cursor while zooming if // there isn't one already @@ -304,11 +306,15 @@ impl imp::LpImage { .target(&adw::CallbackAnimationTarget::new(|_| {})) .build(); - animation.connect_done(glib::clone!(@weak obj => move |_| { - let imp = obj.imp(); - imp.zoom_cursor_target.set(None); - imp.set_zoom_target(obj.imp().zoom_target.get()); - })); + animation.connect_done(glib::clone!( + #[weak] + obj, + move |_| { + let imp = obj.imp(); + imp.zoom_cursor_target.set(None); + imp.set_zoom_target(obj.imp().zoom_target.get()); + } + )); animation }) diff --git a/src/widgets/image_page.rs b/src/widgets/image_page.rs index 096676c..54cb556 100644 --- a/src/widgets/image_page.rs +++ b/src/widgets/image_page.rs @@ -94,42 +94,57 @@ mod imp { self.parent_constructed(); - self.right_click_gesture - .connect_pressed(clone!(@weak obj => move |gesture, _, x, y| { + self.right_click_gesture.connect_pressed(clone!( + #[weak] + obj, + move |gesture, _, x, y| { obj.show_popover_at(x, y); gesture.set_state(gtk::EventSequenceState::Claimed); - })); + } + )); - self.press_gesture - .connect_pressed(clone!(@weak obj => move |gesture, x, y| { + self.press_gesture.connect_pressed(clone!( + #[weak] + obj, + move |gesture, x, y| { log::debug!("Long press triggered"); obj.show_popover_at(x, y); gesture.set_state(gtk::EventSequenceState::Claimed); - })); + } + )); - self.stack - .connect_visible_child_notify(clone!(@weak obj => move |stack| { + self.stack.connect_visible_child_notify(clone!( + #[weak] + obj, + move |stack| { let imp = obj.imp(); let spinner_visible = stack.visible_child().as_ref() == Some(imp.spinner_page.upcast_ref()); // Hide spinner for crossfading into other content since it does not look great imp.spinner.set_visible(spinner_visible); - })); + } + )); - obj.image() - .connect_is_loaded_notify(clone!(@weak obj => move |_| { + obj.image().connect_is_loaded_notify(clone!( + #[weak] + obj, + move |_| { if obj.image().is_loaded() { log::debug!("Showing image"); obj.imp() .stack .set_visible_child(&*obj.imp().image_stack_page); } - })); + } + )); - obj.image() - .connect_error_notify(clone!(@weak obj => move |_| { + obj.image().connect_error_notify(clone!( + #[weak] + obj, + move |_| { glib::spawn_future_local(async move { obj.show_error().await }); - })); + } + )); // Do not waste CPU on spinner if it is not visible self.spinner.connect_map(|s| s.start()); @@ -159,10 +174,16 @@ impl LpImagePage { obj.imp().image.init(file); - glib::spawn_future_local(clone!(@weak obj, @strong file => async move { - let imp = obj.imp(); - imp.image.load(&file).await; - })); + glib::spawn_future_local(clone!( + #[weak] + obj, + #[strong] + file, + async move { + let imp = obj.imp(); + imp.image.load(&file).await; + } + )); obj } @@ -225,10 +246,13 @@ impl LpImagePage { imp.stack.set_visible_child(&*imp.error_page); } else if let Some(err) = image.error() { - imp.error_more_info - .connect_clicked(glib::clone!(@weak self as obj => move |_| { + imp.error_more_info.connect_clicked(glib::clone!( + #[weak(rename_to = obj)] + self, + move |_| { LpErrorDetails::new(&obj.root().unwrap(), &err); - })); + } + )); imp.error_page.set_description(Some(&gettext( "Either the image file is corrupted or it contains unsupported elements.", diff --git a/src/widgets/image_view.rs b/src/widgets/image_view.rs index 8e57efa..e5baa37 100644 --- a/src/widgets/image_view.rs +++ b/src/widgets/image_view.rs @@ -136,45 +136,65 @@ mod imp { // Manually manage widget layout, see `WidgetImpl` for details obj.set_layout_manager(None::); - self.sliding_view.connect_current_page_notify( - glib::clone!(@weak obj => move |_| obj.page_changed()), - ); + self.sliding_view.connect_current_page_notify(glib::clone!( + #[weak] + obj, + move |_| obj.page_changed() + )); - self.sliding_view.connect_target_page_reached( - glib::clone!(@weak obj => move || obj.target_page_reached()), - ); + self.sliding_view.connect_target_page_reached(glib::clone!( + #[weak] + obj, + move || obj.target_page_reached() + )); let signal_group = glib::SignalGroup::new::(); obj.connect_notify_local( Some("current-page"), - clone!(@weak signal_group => move |obj, _| { - signal_group.set_target(obj.current_image().as_ref()); - }), + clone!( + #[weak] + signal_group, + move |obj, _| { + signal_group.set_target(obj.current_image().as_ref()); + } + ), ); let view = &*obj; signal_group.connect_closure( "notify::file", true, - glib::closure_local!(@watch view => move |_: LpImage, _: glib::ParamSpec| { - view.current_image_path_changed(); - }), + glib::closure_local!( + #[watch] + view, + move |_: LpImage, _: glib::ParamSpec| { + view.current_image_path_changed(); + } + ), ); signal_group.connect_closure( "notify::is-deleted", true, - glib::closure_local!(@watch view => move |_: LpImage, _: glib::ParamSpec| { - view.current_image_path_changed(); - }), + glib::closure_local!( + #[watch] + view, + move |_: LpImage, _: glib::ParamSpec| { + view.current_image_path_changed(); + } + ), ); self.current_image_signals.set(signal_group).unwrap(); self.drag_source.set_exclusive(true); - self.drag_source.connect_prepare( - glib::clone!(@weak obj => @default-return None, move |gesture, _, _| { + self.drag_source.connect_prepare(glib::clone!( + #[weak] + obj, + #[upgrade_or_else] + || None, + move |gesture, _, _| { let is_scrollable = obj .current_page() .map(|p| p.image().is_hscrollable() || p.image().is_vscrollable()); @@ -190,22 +210,30 @@ mod imp { gesture.set_state(gtk::EventSequenceState::Claimed); obj.current_page().and_then(|p| p.content_provider()) } - }), - ); + } + )); - self.drag_source.connect_drag_begin(glib::clone!(@weak obj => move |source, _| { - if let Some(paintable) = obj.current_image().and_then(|p| p.thumbnail()) { - // -6 for cursor width, +16 for margin in .drag-icon - source.set_icon(Some(&paintable), paintable.intrinsic_width() / 2 - 6 + 16, -12); - if let Some(drag) = source.drag() { - let drag_icon = gtk::DragIcon::for_drag(&drag); - // Rounds corners, adds outline and shadow - drag_icon.add_css_class("drag-icon"); - // Make border-radius clip the image - drag_icon.set_overflow(gtk::Overflow::Hidden); - } - }; - })); + self.drag_source.connect_drag_begin(glib::clone!( + #[weak] + obj, + move |source, _| { + if let Some(paintable) = obj.current_image().and_then(|p| p.thumbnail()) { + // -6 for cursor width, +16 for margin in .drag-icon + source.set_icon( + Some(&paintable), + paintable.intrinsic_width() / 2 - 6 + 16, + -12, + ); + if let Some(drag) = source.drag() { + let drag_icon = gtk::DragIcon::for_drag(&drag); + // Rounds corners, adds outline and shadow + drag_icon.add_css_class("drag-icon"); + // Make border-radius clip the image + drag_icon.set_overflow(gtk::Overflow::Hidden); + } + }; + } + )); obj.add_controller(self.drag_source.clone()); } @@ -340,16 +368,29 @@ impl LpImageView { // List other files in directory if let Some(directory) = directory { - glib::spawn_future_local(glib::clone!(@weak self as obj => async move { - if let Err(err) = obj.model().load_directory(directory.clone()).await { - log::warn!("Failed to load directory: {}", err.root_cause()); - obj.activate_action("win.show-toast", Some(&(format!("{} {}", err, err.root_cause()), adw::ToastPriority::High.into_glib()).to_variant())) + glib::spawn_future_local(glib::clone!( + #[weak(rename_to = obj)] + self, + async move { + if let Err(err) = obj.model().load_directory(directory.clone()).await { + log::warn!("Failed to load directory: {}", err.root_cause()); + obj.activate_action( + "win.show-toast", + Some( + &( + format!("{} {}", err, err.root_cause()), + adw::ToastPriority::High.into_glib(), + ) + .to_variant(), + ), + ) .unwrap(); - return; - } + return; + } - obj.update_sliding_view(&file); - })); + obj.update_sliding_view(&file); + } + )); } } @@ -498,33 +539,39 @@ impl LpImageView { page.image().connect_notify_local( Some("is-unsupported"), - glib::clone!(@weak self as obj => move |image, _| { - if image.is_unsupported() { - if obj.current_image().as_ref() == Some(image) { - log::debug!( - "Image format unsupported but not removing since current image" - ); - return; - } + glib::clone!( + #[weak(rename_to = obj)] + self, + move |image, _| { + if image.is_unsupported() { + if obj.current_image().as_ref() == Some(image) { + log::debug!( + "Image format unsupported but not removing since current image" + ); + return; + } - if let Some(file) = image.file() { - log::debug!("Removing image with unsupported format {:?}", file.uri()); - obj.model().remove(&file); - if let Some(current_file) = obj.current_file() { - obj.update_sliding_view(¤t_file); + if let Some(file) = image.file() { + log::debug!("Removing image with unsupported format {:?}", file.uri()); + obj.model().remove(&file); + if let Some(current_file) = obj.current_file() { + obj.update_sliding_view(¤t_file); + } } } } - }), + ), ); page } fn set_model(&self, model: LpFileModel) { - model.connect_changed( - glib::clone!(@weak self as obj => move || obj.model_content_changed_cb()), - ); + model.connect_changed(glib::clone!( + #[weak(rename_to = obj)] + self, + move || obj.model_content_changed_cb() + )); self.imp().model.replace(model); } diff --git a/src/widgets/print.rs b/src/widgets/print.rs index 13f5db8..2d948e2 100644 --- a/src/widgets/print.rs +++ b/src/widgets/print.rs @@ -294,8 +294,11 @@ mod imp { obj.set_modal(true); // Page alignment (center, left, ...) - self.alignment - .connect_selected_notify(glib::clone!(@weak obj => move |_| obj.draw_preview())); + self.alignment.connect_selected_notify(glib::clone!( + #[weak] + obj, + move |_| obj.draw_preview() + )); self.alignment .set_expression(Some(gtk::ClosureExpression::new::( @@ -306,27 +309,45 @@ mod imp { ))); // Margin signals - self.margin_unit.connect_selected_notify( - glib::clone!(@weak obj => move |_| obj.on_margin_unit_changed()), - ); + self.margin_unit.connect_selected_notify(glib::clone!( + #[weak] + obj, + move |_| obj.on_margin_unit_changed() + )); self.margin_unit.set_expression(Some(Unit::expression())); - self.margin_horizontal - .connect_value_notify(glib::clone!(@weak obj => move |_| obj.on_margin_changed())); - self.margin_vertical - .connect_value_notify(glib::clone!(@weak obj => move |_| obj.on_margin_changed())); + self.margin_horizontal.connect_value_notify(glib::clone!( + #[weak] + obj, + move |_| obj.on_margin_changed() + )); + self.margin_vertical.connect_value_notify(glib::clone!( + #[weak] + obj, + move |_| obj.on_margin_changed() + )); // Size signals - self.size_unit.connect_selected_notify( - glib::clone!(@weak obj => move |_| obj.on_size_unit_changed()), - ); + self.size_unit.connect_selected_notify(glib::clone!( + #[weak] + obj, + move |_| obj.on_size_unit_changed() + )); self.size_unit.set_expression(Some(Unit::expression())); - self.fill_space.connect_active_notify( - glib::clone!(@weak obj => move |_| obj.on_fill_space_changed()), - ); - self.width - .connect_value_notify(glib::clone!(@weak obj => move |_| obj.on_width_changed())); - self.height - .connect_value_notify(glib::clone!(@weak obj => move |_| obj.on_height_changed())); + self.fill_space.connect_active_notify(glib::clone!( + #[weak] + obj, + move |_| obj.on_fill_space_changed() + )); + self.width.connect_value_notify(glib::clone!( + #[weak] + obj, + move |_| obj.on_width_changed() + )); + self.height.connect_value_notify(glib::clone!( + #[weak] + obj, + move |_| obj.on_height_changed() + )); let imp = self; diff --git a/src/widgets/properties_view.rs b/src/widgets/properties_view.rs index cf385fe..69a6917 100644 --- a/src/widgets/properties_view.rs +++ b/src/widgets/properties_view.rs @@ -116,13 +116,26 @@ mod imp { image_signals.connect_notify_local( Some("image-size-available"), - glib::clone!(@weak obj => move |_, _| obj.imp().update()), + glib::clone!( + #[weak] + obj, + move |_, _| obj.imp().update() + ), ); image_signals.connect_local( "metadata-changed", false, - glib::clone!(@weak obj => @default-return None, move |_| { obj.imp().update(); None }), + glib::clone!( + #[weak] + obj, + #[upgrade_or_else] + || None, + move |_| { + obj.imp().update(); + None + } + ), ); self.update(); diff --git a/src/widgets/properties_view.ui b/src/widgets/properties_view.ui index 06f7889..801a81f 100644 --- a/src/widgets/properties_view.ui +++ b/src/widgets/properties_view.ui @@ -17,7 +17,6 @@ Folder - folder_button False @@ -210,4 +209,4 @@ - \ No newline at end of file + diff --git a/src/widgets/sliding_view.rs b/src/widgets/sliding_view.rs index e266c6f..dc908c8 100644 --- a/src/widgets/sliding_view.rs +++ b/src/widgets/sliding_view.rs @@ -159,20 +159,32 @@ mod imp { .upper_overshoot(true) .build(); - swipe_tracker.connect_begin_swipe(glib::clone!(@weak obj => move |_| { - obj.scroll_animation().pause(); - obj.step_animation().pause(); - })); - - swipe_tracker.connect_update_swipe(glib::clone!(@weak obj => move |_, position| { - obj.set_position(position); - })); - - swipe_tracker.connect_end_swipe(glib::clone!(@weak obj => move |_, velocity, to| { - if let Some(page) = obj.page_at(to) { - obj.scroll_to(&page, velocity); + swipe_tracker.connect_begin_swipe(glib::clone!( + #[weak] + obj, + move |_| { + obj.scroll_animation().pause(); + obj.step_animation().pause(); } - })); + )); + + swipe_tracker.connect_update_swipe(glib::clone!( + #[weak] + obj, + move |_, position| { + obj.set_position(position); + } + )); + + swipe_tracker.connect_end_swipe(glib::clone!( + #[weak] + obj, + move |_, velocity, to| { + if let Some(page) = obj.page_at(to) { + obj.scroll_to(&page, velocity); + } + } + )); self.swipe_tracker.set(swipe_tracker).unwrap(); @@ -180,8 +192,12 @@ mod imp { let scroll_controller = gtk::EventControllerScroll::new(gtk::EventControllerScrollFlags::HORIZONTAL); - scroll_controller.connect_scroll( - glib::clone!(@weak obj => @default-return glib::Propagation::Proceed, move |_, x, _| { + scroll_controller.connect_scroll(glib::clone!( + #[weak] + obj, + #[upgrade_or_else] + || glib::Propagation::Proceed, + move |_, x, _| { let direction_sign = if obj.imp().is_rtl() { -1. } else { 1. }; if x * direction_sign > 0. { @@ -207,8 +223,8 @@ mod imp { glib::Propagation::Stop } } - }), - ); + } + )); obj.add_controller(scroll_controller); } @@ -712,9 +728,11 @@ impl LpSlidingView { fn scroll_animation(&self) -> &adw::SpringAnimation { self.imp().scroll_animation.get_or_init(|| { - let target = adw::CallbackAnimationTarget::new( - glib::clone!(@weak self as obj => move |position| obj.set_position(position)), - ); + let target = adw::CallbackAnimationTarget::new(glib::clone!( + #[weak(rename_to = obj)] + self, + move |position| obj.set_position(position) + )); let animation = adw::SpringAnimation::builder() .spring_params(&adw::SpringParams::new( @@ -726,9 +744,11 @@ impl LpSlidingView { .target(&target) .build(); - animation.connect_done(glib::clone!(@weak self as obj => move |_| { - obj.emit_target_page_reached() - })); + animation.connect_done(glib::clone!( + #[weak(rename_to = obj)] + self, + move |_| obj.emit_target_page_reached() + )); animation }) @@ -736,12 +756,18 @@ impl LpSlidingView { fn step_animation(&self) -> &adw::TimedAnimation { self.imp().step_animation.get_or_init(|| { - let target: adw::CallbackAnimationTarget = adw::CallbackAnimationTarget::new( - glib::clone!(@weak self as obj => move |progress| { - obj.imp().position_tracking.borrow_mut().set_progress(progress); - obj.queue_allocate(); - }), - ); + let target: adw::CallbackAnimationTarget = + adw::CallbackAnimationTarget::new(glib::clone!( + #[weak(rename_to = obj)] + self, + move |progress| { + obj.imp() + .position_tracking + .borrow_mut() + .set_progress(progress); + obj.queue_allocate(); + } + )); let animation = adw::TimedAnimation::builder() .duration(STEP_DURATION) @@ -751,17 +777,23 @@ impl LpSlidingView { .target(&target) .build(); - animation.connect_done(glib::clone!(@weak self as obj => move |_| { - if let Some(current_index) = obj.current_index() { - let imp = obj.imp(); - imp.position_tracking.borrow_mut().set_progress(1.); - imp.position_tracking.borrow_mut().set_position(current_index as f64 - obj.position_shift()); - obj.queue_allocate(); - } else { - error!("No current page at end of animation"); + animation.connect_done(glib::clone!( + #[weak(rename_to = obj)] + self, + move |_| { + if let Some(current_index) = obj.current_index() { + let imp = obj.imp(); + imp.position_tracking.borrow_mut().set_progress(1.); + imp.position_tracking + .borrow_mut() + .set_position(current_index as f64 - obj.position_shift()); + obj.queue_allocate(); + } else { + error!("No current page at end of animation"); + } + obj.emit_target_page_reached(); } - obj.emit_target_page_reached(); - })); + )); animation }) diff --git a/src/window.rs b/src/window.rs index f50833c..a27a879 100644 --- a/src/window.rs +++ b/src/window.rs @@ -172,18 +172,27 @@ mod imp { let obj = self.obj(); - self.forward_click_gesture - .connect_pressed(clone!(@weak obj => move |_,_,_,_| { + self.forward_click_gesture.connect_pressed(clone!( + #[weak] + obj, + move |_, _, _, _| { obj.image_view().navigate(Direction::Forward, false); - })); - self.backward_click_gesture - .connect_pressed(clone!(@weak obj => move |_,_,_,_| { + } + )); + self.backward_click_gesture.connect_pressed(clone!( + #[weak] + obj, + move |_, _, _, _| { obj.image_view().navigate(Direction::Back, false); - })); - self.properties_button - .connect_toggled(clone!(@weak obj => move |_| { + } + )); + self.properties_button.connect_toggled(clone!( + #[weak] + obj, + move |_| { obj.on_properties_button_toggled(); - })); + } + )); if config::PROFILE == ".Devel" { obj.add_css_class("devel"); @@ -195,37 +204,58 @@ mod imp { glib::timeout_add_local_once( std::time::Duration::from_millis(SHOW_WINDOW_AFTER), - glib::clone!(@weak obj => move || if !obj.is_visible() { obj.present() }), + glib::clone!( + #[weak] + obj, + move || if !obj.is_visible() { + obj.present() + } + ), ); obj.on_current_page_changed(); obj.on_fullscreen_changed(); - self.image_view.connect_current_page_notify( - glib::clone!(@weak obj => move |_| obj.on_current_page_changed()), - ); + self.image_view.connect_current_page_notify(glib::clone!( + #[weak] + obj, + move |_| obj.on_current_page_changed() + )); - obj.connect_fullscreened_notify( - glib::clone!(@weak obj => move |_| obj.on_fullscreen_changed()), - ); + obj.connect_fullscreened_notify(glib::clone!( + #[weak] + obj, + move |_| obj.on_fullscreen_changed() + )); obj.connect_map(|win| { win.resize_default(); }); - self.properties_button.connect_active_notify( - glib::clone!(@weak obj => move |_| obj.update_headerbar_style()), - ); + self.properties_button.connect_active_notify(glib::clone!( + #[weak] + obj, + move |_| obj.update_headerbar_style() + )); let gesture_click = gtk::GestureClick::new(); - gesture_click - .connect_pressed(glib::clone!(@weak obj => move |_, _, _, _| obj.on_click())); + gesture_click.connect_pressed(glib::clone!( + #[weak] + obj, + move |_, _, _, _| obj.on_click() + )); obj.add_controller(gesture_click); - self.motion_controller - .connect_motion(glib::clone!(@weak obj => move |_, x, y| obj.on_motion((x,y)))); - self.motion_controller - .connect_enter(glib::clone!(@weak obj => move |_, x, y| obj.on_motion((x,y)))); + self.motion_controller.connect_motion(glib::clone!( + #[weak] + obj, + move |_, x, y| obj.on_motion((x, y)) + )); + self.motion_controller.connect_enter(glib::clone!( + #[weak] + obj, + move |_, x, y| obj.on_motion((x, y)) + )); obj.add_controller(self.motion_controller.clone()); let current_image_signals = self.image_view.current_image_signals(); @@ -237,17 +267,25 @@ mod imp { // Object alive, pass as @weak. Otherwise, pass // as @strong. Most of the time you will want // to use @weak. - current_image_signals.connect_bind_local(glib::clone!(@weak obj => move |_, _| { - obj.on_zoom_status_changed() - })); + current_image_signals.connect_bind_local(glib::clone!( + #[weak] + obj, + move |_, _| obj.on_zoom_status_changed() + )); current_image_signals.connect_local( "metadata-changed", true, - glib::clone!(@weak obj => @default-return None, move |_| { - obj.update_title(); - None - }), + glib::clone!( + #[weak] + obj, + #[upgrade_or_else] + || None, + move |_| { + obj.update_title(); + None + } + ), ); current_image_signals.connect_closure( @@ -256,57 +294,82 @@ mod imp { // `closure_local!` is similar to `clone`, but you use `@watch` instead of clone. // `@watch` means that this signal will be disconnected when the watched object // is dropped. - glib::closure_local!(@watch obj => move |_: &LpImage, _: &glib::ParamSpec| { - obj.on_zoom_status_changed(); - }), + glib::closure_local!( + #[watch] + obj, + move |_: &LpImage, _: &glib::ParamSpec| { + obj.on_zoom_status_changed(); + } + ), ); current_image_signals.connect_closure( "notify::is-max-zoom", false, - glib::closure_local!(@watch obj => move |_: &LpImage, _: &glib::ParamSpec| { - obj.on_zoom_status_changed(); - }), + glib::closure_local!( + #[watch] + obj, + move |_: &LpImage, _: &glib::ParamSpec| { + obj.on_zoom_status_changed(); + } + ), ); current_image_signals.connect_closure( "notify::image-size-available", false, - glib::closure_local!(@watch obj => move |_: &LpImage, _: &glib::ParamSpec| { - obj.image_size_available(); - }), + glib::closure_local!( + #[watch] + obj, + move |_: &LpImage, _: &glib::ParamSpec| { + obj.image_size_available(); + } + ), ); current_image_signals.connect_closure( "notify::error", false, - glib::closure_local!(@watch obj => move |_: &LpImage, _: &glib::ParamSpec| { - obj.image_error(); - }), + glib::closure_local!( + #[watch] + obj, + move |_: &LpImage, _: &glib::ParamSpec| { + obj.image_error(); + } + ), ); - self.toolbar_view.connect_top_bar_style_notify( - glib::clone!(@weak obj => move |_| obj.on_top_bar_style_notify()), - ); + self.toolbar_view.connect_top_bar_style_notify(glib::clone!( + #[weak] + obj, + move |_| obj.on_top_bar_style_notify() + )); // action win.previous status - self.image_view.connect_is_previous_available_notify( - glib::clone!(@weak obj => move |_| { - obj.action_set_enabled( - "win.previous", - obj.imp().image_view.is_previous_available(), - ); - }), - ); + self.image_view + .connect_is_previous_available_notify(glib::clone!( + #[weak] + obj, + move |_| { + obj.action_set_enabled( + "win.previous", + obj.imp().image_view.is_previous_available(), + ); + } + )); // action win.next status self.image_view - .connect_is_next_available_notify(glib::clone!(@weak obj => move |_| { - obj.action_set_enabled( - "win.next", - obj.imp().image_view.is_next_available(), - ); - })); + .connect_is_next_available_notify(glib::clone!( + #[weak] + obj, + move |_| { + obj.action_set_enabled( + "win.next", + obj.imp().image_view.is_next_available(), + ); + } + )); // Make widgets visible when the focus moves obj.connect_move_focus(|obj, _| { @@ -323,31 +386,41 @@ mod imp { self.drop_target.set_types(&[gdk::FileList::static_type()]); - self.drop_target.connect_accept( - clone!(@weak obj => @default-return false, move |_drop_target, drop| { + self.drop_target.connect_accept(clone!( + #[weak] + obj, + #[upgrade_or_else] + || false, + move |_drop_target, drop| { // Only accept drops from external sources or different windows - let different_source = drop.drag().is_none() || drop.drag() != obj.image_view().drag_source().drag(); + let different_source = drop.drag().is_none() + || drop.drag() != obj.image_view().drag_source().drag(); // We have to do this manually since we are overwriting the default handler let correct_format = drop.formats().contains_type(gdk::FileList::static_type()); different_source && correct_format - }), - ); + } + )); // For callbacks, you will want to reference the GTK docs on // the relevant signal to see which parameters you need. // In this case, we need only need the GValue, // so we name it `value` then use `_` for the other spots. - self.drop_target.connect_drop( - clone!(@weak obj => @default-return false, move |_, value, _, _| { - - // Here we use a GValue, which is a dynamic object that can hold different types, - // e.g. strings, numbers, or in this case objects. In order to get the GdkFileList - // from the GValue, we need to use the `get()` method. + self.drop_target.connect_drop(clone!( + #[weak] + obj, + #[upgrade_or_else] + || false, + move |_, value, _, _| { + // Here we use a GValue, which is a dynamic object that can hold different + // types, e.g. strings, numbers, or in this case objects. In + // order to get the GdkFileList from the GValue, we need to + // use the `get()` method. // - // We've added type annotations here, and written it as `let list: gdk::FileList = ...`, - // but you might also see places where type arguments are used. - // This line could have been written as `let list = value.get::().unwrap()`. + // We've added type annotations here, and written it as `let list: gdk::FileList + // = ...`, but you might also see places where type + // arguments are used. This line could have been written as + // `let list = value.get::().unwrap()`. let files = match value.get::() { Ok(list) => list.files(), Err(err) => { @@ -367,8 +440,8 @@ mod imp { obj.present(); true - }), - ); + } + )); } } @@ -563,21 +636,25 @@ impl LpWindow { .button_label(gettext("Undo")) .priority(adw::ToastPriority::High) .build(); - toast.connect_button_clicked(glib::clone!(@weak self as win => move |_| { - let path = path.clone(); - glib::spawn_future_local(async move { - win.image_view() - .set_trash_restore(Some(gio::File::for_path(&path))); - let result = crate::util::untrash(&path).await; - if let Err(err) = result { - log::error!("Failed to untrash {path:?}: {err}"); - win.show_toast( - gettext("Failed to restore image from trash"), - adw::ToastPriority::High, - ); - } - }); - })); + toast.connect_button_clicked(glib::clone!( + #[weak(rename_to = win)] + self, + move |_| { + let path = path.clone(); + glib::spawn_future_local(async move { + win.image_view() + .set_trash_restore(Some(gio::File::for_path(&path))); + let result = crate::util::untrash(&path).await; + if let Err(err) = result { + log::error!("Failed to untrash {path:?}: {err}"); + win.show_toast( + gettext("Failed to restore image from trash"), + adw::ToastPriority::High, + ); + } + }); + } + )); self.imp().toast_overlay.add_toast(toast); } Err(err) => { diff --git a/src/window/controls.rs b/src/window/controls.rs index 296939a..2fc2005 100644 --- a/src/window/controls.rs +++ b/src/window/controls.rs @@ -50,7 +50,9 @@ impl LpWindow { fn show_controls_animation(&self) -> &adw::TimedAnimation { self.imp().show_controls_animation.get_or_init(|| { let target = adw::CallbackAnimationTarget::new(glib::clone!( - @weak self as obj => move |opacity| obj.set_control_opacity(opacity, false) + #[weak(rename_to = obj)] + self, + move |opacity| obj.set_control_opacity(opacity, false) )); adw::TimedAnimation::builder() @@ -66,7 +68,9 @@ impl LpWindow { fn hide_controls_animation(&self) -> &adw::TimedAnimation { self.imp().hide_controls_animation.get_or_init(|| { let target = adw::CallbackAnimationTarget::new(glib::clone!( - @weak self as obj => move |opacity| obj.set_control_opacity(opacity, true) + #[weak(rename_to = obj)] + self, + move |opacity| obj.set_control_opacity(opacity, true) )); adw::TimedAnimation::builder() @@ -85,10 +89,14 @@ impl LpWindow { let new_timeout = glib::timeout_add_local_once( std::time::Duration::from_millis(HIDE_CONTROLS_IDLE_TIMEOUT), - glib::clone!(@weak self as win => move || { - win.imp().hide_controls_timeout.take(); - win.hide_controls(); - }), + glib::clone!( + #[weak(rename_to = win)] + self, + move || { + win.imp().hide_controls_timeout.take(); + win.hide_controls(); + } + ), ); self.imp().hide_controls_timeout.replace(Some(new_timeout));