image: Unify error handling in one type

This commit is contained in:
Sophie Herold 2025-12-21 15:16:11 +01:00
parent 1d617bc4b2
commit b9668c7262
8 changed files with 108 additions and 129 deletions

View file

@ -90,7 +90,7 @@ mod imp {
// Handles opening files from the command line or other applications // Handles opening files from the command line or other applications
fn open(&self, files: &[gio::File], _hint: &str) { fn open(&self, files: &[gio::File], _hint: &str) {
tracing::trace!("Open {} file(s)", files.len()); tracing::debug!("Open {} file(s)", files.len());
let application = self.obj(); let application = self.obj();
let win = LpWindow::new(&*application); let win = LpWindow::new(&*application);
win.image_view().set_images_from_files(files.to_vec()); win.image_view().set_images_from_files(files.to_vec());

View file

@ -59,15 +59,10 @@ pub enum DecoderUpdate {
Redraw, Redraw,
/// Start animation /// Start animation
Animated, Animated,
SpecificError(DecoderError), Error(DecoderError),
/// And error occurred during decoding
GenericError(anyhow::Error),
} }
#[derive(glib::Enum, Debug, Clone, Copy, Default, PartialEq, Eq)] #[derive(Debug, Clone, Default, PartialEq, Eq)]
#[enum_type(name = "LpDecoderError")]
//#[enum_dynamic(lazy_registration = true)]
#[repr(i32)]
pub enum DecoderError { pub enum DecoderError {
/// Image format not supported or unknown /// Image format not supported or unknown
UnsupportedFormat, UnsupportedFormat,
@ -75,26 +70,23 @@ pub enum DecoderError {
NoLoadersConfigured, NoLoadersConfigured,
/// Memory limit exceeded /// Memory limit exceeded
OutOfMemory, OutOfMemory,
Generic(String),
//ImageSource(String),
#[default] #[default]
None, None,
} }
impl DecoderError { impl DecoderError {
pub fn is_err(self) -> bool { pub fn is_err(&self) -> bool {
matches!(self, Self::None) matches!(self, Self::None)
} }
pub fn is_ok(self) -> bool { pub fn is_ok(&self) -> bool {
!matches!(self, Self::None) !matches!(self, Self::None)
} }
} }
impl glib::value::ToValueOptional for DecoderError {
fn to_value_optional(s: Option<&Self>) -> glib::Value {
s.unwrap_or(&Self::None).into()
}
}
impl UpdateSender { impl UpdateSender {
pub fn send(&self, update: DecoderUpdate) { pub fn send(&self, update: DecoderUpdate) {
let result = self.sender.force_send(update); let result = self.sender.force_send(update);
@ -124,23 +116,20 @@ impl UpdateSender {
} }
pub fn send_error(&self, err: glycin::ErrorCtx) { pub fn send_error(&self, err: glycin::ErrorCtx) {
dbg!(&err);
if let Some(mime_type) = err.unsupported_format() { if let Some(mime_type) = err.unsupported_format() {
let mut metadata = Metadata::default(); let mut metadata = Metadata::default();
metadata.set_mime_type(mime_type); metadata.set_mime_type(mime_type);
self.send(DecoderUpdate::Metadata(Box::new(metadata))); self.send(DecoderUpdate::Metadata(Box::new(metadata)));
self.send(DecoderUpdate::SpecificError( self.send(DecoderUpdate::Error(DecoderError::UnsupportedFormat));
DecoderError::UnsupportedFormat,
));
} }
if err.is_out_of_memory() { if err.is_out_of_memory() {
self.send(DecoderUpdate::SpecificError(DecoderError::OutOfMemory)); self.send(DecoderUpdate::Error(DecoderError::OutOfMemory));
} else if matches!(err.error(), glycin::Error::NoLoadersConfigured(_)) { } else if matches!(err.error(), glycin::Error::NoLoadersConfigured(_)) {
self.send(DecoderUpdate::SpecificError( self.send(DecoderUpdate::Error(DecoderError::NoLoadersConfigured));
DecoderError::NoLoadersConfigured,
));
} }
self.send(DecoderUpdate::GenericError(err.into())); self.send(DecoderUpdate::Error(DecoderError::Generic(err.to_string())));
} }
} }
@ -220,7 +209,8 @@ impl Decoder {
pub fn request(&self, tile_request: TileRequest) { pub fn request(&self, tile_request: TileRequest) {
if let FormatDecoder::Svg(svg) = &self.decoder { if let FormatDecoder::Svg(svg) = &self.decoder {
if let Err(err) = svg.request(tile_request) { if let Err(err) = svg.request(tile_request) {
self.update_sender.send(DecoderUpdate::GenericError(err)); self.update_sender
.send(DecoderUpdate::Error(DecoderError::Generic(err.to_string())));
} }
} }
} }

View file

@ -82,10 +82,7 @@ impl Entry {
} }
Err(err) => { Err(err) => {
tracing::error!( tracing::error!("Failed to obtain information for sorting '{uri}': {err}",);
"Failed to obtain information for sorting '{}': {err}",
uri
);
break; break;
} }
} }

View file

@ -154,14 +154,13 @@ mod imp {
pub(super) queued_reload: RefCell<Option<gio::File>>, pub(super) queued_reload: RefCell<Option<gio::File>>,
#[property(get)] #[property(get)]
pub(super) is_deleted: Cell<bool>, pub(super) is_deleted: Cell<bool>,
#[property(get, builder(DecoderError::None))] /// Set if an error has occurred, shown on error_page
pub(super) specific_error: Cell<DecoderError>, pub(super) specific_error: RefCell<DecoderError>,
#[property(get=Self::is_error)]
pub(super) is_error: Cell<bool>,
/// Set to true when image is ready for displaying /// Set to true when image is ready for displaying
#[property(get)] #[property(get)]
pub(super) is_loaded: Cell<bool>, pub(super) is_loaded: Cell<bool>,
/// Set if an error has occurred, shown on error_page
#[property(get)]
pub(super) error: RefCell<Option<String>>,
pub(super) background_color: RefCell<Option<gdk::RGBA>>, pub(super) background_color: RefCell<Option<gdk::RGBA>>,
pub(super) fixed_background_color: RefCell<Option<gdk::RGBA>>, pub(super) fixed_background_color: RefCell<Option<gdk::RGBA>>,
/// Animations disabled /// Animations disabled

View file

@ -142,18 +142,11 @@ impl imp::LpImage {
self.tick_callback.replace(Some(callback_id)); self.tick_callback.replace(Some(callback_id));
} }
} }
DecoderUpdate::SpecificError(err) => { DecoderUpdate::Error(err) => {
if self.obj().is_loaded() { if obj.is_loaded() {
tracing::warn!("Error occured while loading additional data: {err:?}"); tracing::warn!("Error occured while loading additional data: {err:?}");
} else { } else {
self.set_specific_error(err); obj.set_specific_error(err);
}
}
DecoderUpdate::GenericError(err) => {
if self.obj().is_loaded() {
tracing::warn!("Error occured while loading additional data: {err:?}");
} else {
self.set_error(Some(err));
} }
} }
} }
@ -233,7 +226,7 @@ impl imp::LpImage {
} else { } else {
let old_file = file_a.clone(); let old_file = file_a.clone();
glib::spawn_future_local(async move { glib::spawn_future_local(async move {
if obj.error().is_some() { if obj.is_error() {
// Tmp files might be renamed quickly after creation. In this case // Tmp files might be renamed quickly after creation. In this case
// the original load fails, because the filename is already invalid. // the original load fails, because the filename is already invalid.
// Therefore reload on name change loading caused an error. // Therefore reload on name change loading caused an error.
@ -274,32 +267,8 @@ impl imp::LpImage {
} }
} }
fn set_error(&self, err: Option<anyhow::Error>) { pub(super) fn is_error(&self) -> bool {
tracing::debug!("Decoding error: {err:?}"); self.specific_error.borrow().is_err()
// Keeping first error instead of replacing with new error
if err.is_some() && self.error.borrow().is_some() {
return;
}
self.error.replace(err.as_ref().map(|x| x.to_string()));
self.obj().notify_error();
if err.is_some() {
self.set_loaded(false);
}
}
fn set_specific_error(&self, error: DecoderError) {
let obj = self.obj();
if obj.specific_error() != error {
self.specific_error.set(error);
obj.notify_specific_error();
if error.is_err() {
self.set_loaded(false);
}
}
} }
fn set_loaded(&self, is_loaded: bool) { fn set_loaded(&self, is_loaded: bool) {
@ -310,8 +279,7 @@ impl imp::LpImage {
obj.notify_is_loaded(); obj.notify_is_loaded();
if is_loaded { if is_loaded {
self.set_error(None); obj.set_specific_error(DecoderError::None);
self.set_specific_error(DecoderError::None);
} }
} }
} }
@ -388,4 +356,27 @@ impl LpImage {
imp.decoder.replace(Some(Arc::new(decoder))); imp.decoder.replace(Some(Arc::new(decoder)));
} }
pub fn specific_error(&self) -> DecoderError {
self.imp().specific_error.borrow().clone()
}
pub fn set_specific_error(&self, error: DecoderError) {
let imp = self.imp();
// Keeping first error instead of replacing with new error
if error.is_err() && self.is_error() {
return;
}
if self.specific_error() != error {
let is_err = error.is_err();
imp.specific_error.replace(error);
self.notify_is_error();
if is_err {
imp.set_loaded(false);
}
}
}
} }

View file

@ -136,7 +136,7 @@ mod imp {
} }
)); ));
obj.image().connect_error_notify(clone!( obj.image().connect_is_error_notify(clone!(
#[weak] #[weak]
obj, obj,
move |_| { move |_| {
@ -214,7 +214,8 @@ impl LpImagePage {
let imp = self.imp(); let imp = self.imp();
let image = self.image(); let image = self.image();
if image.specific_error() == DecoderError::NoLoadersConfigured { match image.specific_error() {
DecoderError::NoLoadersConfigured => {
// Translators: {} is replaced with a version number // Translators: {} is replaced with a version number
let message = gettext_f( let message = gettext_f(
"No image loaders available. Maybe the “glycin-loaders” package with compatibility version “{}” is not installed.", "No image loaders available. Maybe the “glycin-loaders” package with compatibility version “{}” is not installed.",
@ -224,7 +225,8 @@ impl LpImagePage {
imp.error_page.set_description(Some(&message)); imp.error_page.set_description(Some(&message));
imp.stack.set_visible_child(&*imp.error_page); imp.stack.set_visible_child(&*imp.error_page);
imp.error_more_info.set_visible(false); imp.error_more_info.set_visible(false);
} else if image.specific_error() == DecoderError::UnsupportedFormat { }
DecoderError::UnsupportedFormat => {
let mime_type = image.metadata().mime_type().unwrap_or_default(); let mime_type = image.metadata().mime_type().unwrap_or_default();
let content_type = gio::content_type_from_mime_type(&mime_type).unwrap_or_default(); let content_type = gio::content_type_from_mime_type(&mime_type).unwrap_or_default();
let description = gio::content_type_get_description(&content_type).to_string(); let description = gio::content_type_get_description(&content_type).to_string();
@ -245,7 +247,6 @@ impl LpImagePage {
) )
}; };
if let Some(err) = image.error() {
imp.error_more_info.connect_clicked(glib::clone!( imp.error_more_info.connect_clicked(glib::clone!(
#[weak(rename_to = obj)] #[weak(rename_to = obj)]
self, self,
@ -253,11 +254,11 @@ impl LpImagePage {
LpErrorDetails::new(&obj.root().unwrap(), &err, ErrorType::Loader); LpErrorDetails::new(&obj.root().unwrap(), &err, ErrorType::Loader);
} }
)); ));
}
imp.error_page.set_description(Some(&message)); imp.error_page.set_description(Some(&message));
imp.stack.set_visible_child(&*imp.error_page); imp.stack.set_visible_child(&*imp.error_page);
} else if let Some(err) = image.error() { }
DecoderError::Generic(err) => {
imp.error_more_info.connect_clicked(glib::clone!( imp.error_more_info.connect_clicked(glib::clone!(
#[weak(rename_to = obj)] #[weak(rename_to = obj)]
self, self,
@ -266,7 +267,7 @@ impl LpImagePage {
} }
)); ));
if image.specific_error() == DecoderError::OutOfMemory { if matches!(image.specific_error(), DecoderError::OutOfMemory) {
imp.error_page.set_description(Some(&gettext( imp.error_page.set_description(Some(&gettext(
"There is not enough free memory available to load this image.", "There is not enough free memory available to load this image.",
))); )));
@ -280,4 +281,5 @@ impl LpImagePage {
imp.stack.set_visible_child(&*imp.error_page); imp.stack.set_visible_child(&*imp.error_page);
} }
} }
}
} }

View file

@ -646,11 +646,11 @@ impl LpImageView {
fn new_image_page(&self, file: &gio::File) -> LpImagePage { fn new_image_page(&self, file: &gio::File) -> LpImagePage {
let page = LpImagePage::from_file(file); let page = LpImagePage::from_file(file);
page.image().connect_specific_error_notify(glib::clone!( page.image().connect_is_error_notify(glib::clone!(
#[weak(rename_to = obj)] #[weak(rename_to = obj)]
self, self,
move |image| { move |image| {
if image.specific_error() == DecoderError::UnsupportedFormat { if matches!(image.specific_error(), DecoderError::UnsupportedFormat) {
if obj.current_image().as_ref() == Some(image) { if obj.current_image().as_ref() == Some(image) {
tracing::debug!( tracing::debug!(
"Image format unsupported but not removing since current image" "Image format unsupported but not removing since current image"

View file

@ -452,7 +452,7 @@ mod imp {
self.image_view.try_get().is_some_and(|image_view| { self.image_view.try_get().is_some_and(|image_view| {
image_view image_view
.current_page() .current_page()
.is_some_and(|page| page.image().error().is_none()) .is_some_and(|page| !page.image().is_error())
}) })
} }
} }
@ -748,7 +748,7 @@ impl LpImageWindow {
.imp() .imp()
.image_view .image_view
.current_page() .current_page()
.is_some_and(|x| x.image().is_loaded() && x.image().error().is_none()); .is_some_and(|x| x.image().is_loaded() && !x.image().is_error());
for action in ACTIONS_SHOWN { for action in ACTIONS_SHOWN {
self.action_set_enabled(action, enabled_shown); self.action_set_enabled(action, enabled_shown);
@ -914,7 +914,7 @@ impl LpImageWindow {
let current_page = self.imp().image_view.current_page(); let current_page = self.imp().image_view.current_page();
if current_page.is_some_and(|page| page.image().error().is_some()) { if current_page.is_some_and(|page| page.image().is_error()) {
tracing::debug!("Showing window because loading image failed"); tracing::debug!("Showing window because loading image failed");
self.window_inspect(|w| w.present()); self.window_inspect(|w| w.present());
} }