window: Disable more actions if error is shown

Many operations don't make sense while an image is loading or can't be
shown like zoom or print. In these cases the actions should now be
disabled.

Part-of: <https://gitlab.gnome.org/GNOME/loupe/-/merge_requests/440>
This commit is contained in:
Sophie Herold 2024-07-30 18:24:39 +02:00 • committed by Marge Bot
parent ff869412c3
commit f75e64f73b
3 changed files with 83 additions and 71 deletions

View file

@ -0,0 +1 @@
Fixed that some actions are available on error pages and loading screens while they are not actually useful. This includes actions like zoom, rotate, and print.

View file

@ -148,17 +148,7 @@ mod imp {
move || obj.target_page_reached() move || obj.target_page_reached()
)); ));
let signal_group = glib::SignalGroup::new::<LpImage>(); let signal_group = obj.current_image_signals();
obj.connect_notify_local(
Some("current-page"),
clone!(
#[weak]
signal_group,
move |obj, _| {
signal_group.set_target(obj.current_image().as_ref());
}
),
);
let view = &*obj; let view = &*obj;
signal_group.connect_closure( signal_group.connect_closure(
@ -185,8 +175,6 @@ mod imp {
), ),
); );
self.current_image_signals.set(signal_group).unwrap();
self.drag_source.set_exclusive(true); self.drag_source.set_exclusive(true);
self.drag_source.connect_prepare(glib::clone!( self.drag_source.connect_prepare(glib::clone!(
@ -488,8 +476,8 @@ impl LpImageView {
} }
} }
self.notify("is-previous-available"); self.notify_is_previous_available();
self.notify("is-next-available"); self.notify_is_next_available();
} }
fn scroll_sliding_view(&self, file: &gio::File, animated: bool) { fn scroll_sliding_view(&self, file: &gio::File, animated: bool) {
@ -619,10 +607,17 @@ impl LpImageView {
} }
pub fn current_image_signals(&self) -> &glib::SignalGroup { pub fn current_image_signals(&self) -> &glib::SignalGroup {
self.imp() self.imp().current_image_signals.get_or_init(move || {
.current_image_signals let signal_group = glib::SignalGroup::new::<LpImage>();
.get() self.connect_current_page_notify(clone!(
.expect("Signal group should be set up during construction") #[weak]
signal_group,
move |obj| {
signal_group.set_target(obj.current_image().as_ref());
}
));
signal_group
})
} }
pub fn current_uri(&self) -> Option<glib::GString> { pub fn current_uri(&self) -> Option<glib::GString> {

View file

@ -264,19 +264,6 @@ mod imp {
obj.add_controller(self.motion_controller.clone()); obj.add_controller(self.motion_controller.clone());
let current_image_signals = self.image_view.current_image_signals(); let current_image_signals = self.image_view.current_image_signals();
// clone! is a macro from glib-rs that allows
// you to easily handle references in callbacks
// without refcycles or leaks.
//
// When you don't want the callback to keep the
// 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_local( current_image_signals.connect_local(
"metadata-changed", "metadata-changed",
@ -302,7 +289,7 @@ mod imp {
#[watch] #[watch]
obj, obj,
move |_: &LpImage, _: &glib::ParamSpec| { move |_: &LpImage, _: &glib::ParamSpec| {
obj.on_zoom_status_changed(); obj.update_image_actions_status();
} }
), ),
); );
@ -314,7 +301,7 @@ mod imp {
#[watch] #[watch]
obj, obj,
move |_: &LpImage, _: &glib::ParamSpec| { move |_: &LpImage, _: &glib::ParamSpec| {
obj.on_zoom_status_changed(); obj.update_image_actions_status();
} }
), ),
); );
@ -331,6 +318,18 @@ mod imp {
), ),
); );
current_image_signals.connect_closure(
"notify::is-loaded",
false,
glib::closure_local!(
#[watch]
obj,
move |_: &LpImage, _: &glib::ParamSpec| {
obj.update_image_actions_status();
}
),
);
current_image_signals.connect_closure( current_image_signals.connect_closure(
"notify::error", "notify::error",
false, false,
@ -339,6 +338,7 @@ mod imp {
obj, obj,
move |_: &LpImage, _: &glib::ParamSpec| { move |_: &LpImage, _: &glib::ParamSpec| {
obj.image_error(); obj.image_error();
obj.update_image_actions_status();
} }
), ),
); );
@ -350,7 +350,7 @@ mod imp {
obj, obj,
move |_| { move |_| {
obj.action_set_enabled( obj.action_set_enabled(
"win.previous", &Action::Previous,
obj.imp().image_view.is_previous_available(), obj.imp().image_view.is_previous_available(),
); );
} }
@ -363,7 +363,7 @@ mod imp {
obj, obj,
move |_| { move |_| {
obj.action_set_enabled( obj.action_set_enabled(
"win.next", &Action::Next,
obj.imp().image_view.is_next_available(), obj.imp().image_view.is_next_available(),
); );
} }
@ -741,25 +741,59 @@ impl LpWindow {
imp.toast_overlay.add_toast(toast); imp.toast_overlay.add_toast(toast);
} }
pub fn set_actions_enabled(&self, enabled: bool) { /// Set status for actions that are related to the active image
const ACTIONS: &[&str] = &[ fn update_image_actions_status(&self) {
"win.open-with", // Actions that are enabled if the image is shown
"win.set-background", const ACTIONS_SHOWN: &[Action] = &[
"win.toggle-fullscreen", Action::SetBackground,
"win.print", Action::Print,
"win.rotate-cw", Action::RotateCw,
"win.rotate-ccw", Action::RotateCcw,
"win.copy-image", Action::ZoomBestFit,
"win.zoom-best-fit", Action::ZoomToExact1,
"win.zoom-to-exact", Action::ZoomToExact2,
"win.toggle-properties",
]; ];
for action in ACTIONS { let enabled_shown = self
self.action_set_enabled(action, enabled); .imp()
.image_view
.current_page()
.is_some_and(|x| x.image().is_loaded() && x.image().error().is_none());
for action in ACTIONS_SHOWN {
self.action_set_enabled(action, enabled_shown);
} }
self.action_set_enabled(Action::Reload.as_ref(), true); // Zoom specific actions
let can_zoom_out = self
.image_view()
.current_image()
.is_some_and(|image| !image.is_best_fit());
let can_zoom_in = self
.image_view()
.current_image()
.is_some_and(|image| !image.is_max_zoom());
self.action_set_enabled(&Action::ZoomOutCursor, can_zoom_out && enabled_shown);
self.action_set_enabled(&Action::ZoomOutCenter, can_zoom_out && enabled_shown);
self.action_set_enabled(&Action::ZoomInCursor, can_zoom_in && enabled_shown);
self.action_set_enabled(&Action::ZoomInCenter, can_zoom_in && enabled_shown);
// Actions that are available if there is an current image, even if it's not shown
const ACTIONS_CURRENT: &[Action] = &[
Action::OpenWith,
Action::CopyImage,
Action::ToggleProperties,
Action::Reload,
Action::ToggleFullscreen,
];
let enabled_current = self.imp().image_view.current_page().is_some();
for action in ACTIONS_CURRENT {
self.action_set_enabled(action, enabled_current);
}
} }
/// Handles change in image and availability of images /// Handles change in image and availability of images
@ -781,9 +815,9 @@ impl LpWindow {
let has_image = current_page.is_some(); let has_image = current_page.is_some();
self.set_actions_enabled(has_image); self.update_image_actions_status();
self.action_set_enabled( self.action_set_enabled(
"win.trash", &Action::Trash,
imp.image_view imp.image_view
.current_file() .current_file()
.is_some_and(|file| file.path().is_some()), .is_some_and(|file| file.path().is_some()),
@ -885,24 +919,6 @@ impl LpWindow {
} }
} }
fn on_zoom_status_changed(&self) {
let can_zoom_out = self
.image_view()
.current_image()
.map(|image| !image.is_best_fit())
.unwrap_or_default();
let can_zoom_in = self
.image_view()
.current_image()
.map(|image| !image.is_max_zoom())
.unwrap_or_default();
self.action_set_enabled("win.zoom-out-cursor", can_zoom_out);
self.action_set_enabled("win.zoom-out-center", can_zoom_out);
self.action_set_enabled("win.zoom-in-cursor", can_zoom_in);
self.action_set_enabled("win.zoom-in-center", can_zoom_in);
}
fn on_fullscreen_changed(&self) { fn on_fullscreen_changed(&self) {
self.imp() self.imp()
.image_view .image_view