image: Unify error handling in one type
This commit is contained in:
parent
1d617bc4b2
commit
b9668c7262
8 changed files with 108 additions and 129 deletions
|
|
@ -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());
|
||||||
|
|
|
||||||
|
|
@ -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())));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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,38 +214,39 @@ 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() {
|
||||||
// Translators: {} is replaced with a version number
|
DecoderError::NoLoadersConfigured => {
|
||||||
let message = gettext_f(
|
// Translators: {} is replaced with a version number
|
||||||
"No image loaders available. Maybe the “glycin-loaders” package with compatibility version “{}” is not installed.",
|
let message = gettext_f(
|
||||||
[format!("{}+", glycin::COMPAT_VERSION)],
|
"No image loaders available. Maybe the “glycin-loaders” package with compatibility version “{}” is not installed.",
|
||||||
);
|
[format!("{}+", glycin::COMPAT_VERSION)],
|
||||||
|
);
|
||||||
|
|
||||||
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 {
|
}
|
||||||
let mime_type = image.metadata().mime_type().unwrap_or_default();
|
DecoderError::UnsupportedFormat => {
|
||||||
let content_type = gio::content_type_from_mime_type(&mime_type).unwrap_or_default();
|
let mime_type = image.metadata().mime_type().unwrap_or_default();
|
||||||
let description = gio::content_type_get_description(&content_type).to_string();
|
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 message = if glycin::Loader::DEFAULT_MIME_TYPES.contains(&mime_type.as_str()) {
|
let message = if glycin::Loader::DEFAULT_MIME_TYPES.contains(&mime_type.as_str()) {
|
||||||
// Translators: The first occurance of {} is replace with a description of the
|
// Translators: The first occurance of {} is replace with a description of the
|
||||||
// format and the second with an id (mime-type) of the format.
|
// format and the second with an id (mime-type) of the format.
|
||||||
gettext_f(
|
gettext_f(
|
||||||
"The image format “{} ({})” is known but not installed.",
|
"The image format “{} ({})” is known but not installed.",
|
||||||
[&description, &mime_type],
|
[&description, &mime_type],
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
// Translators: The first occurance of {} is replace with a description of the
|
// Translators: The first occurance of {} is replace with a description of the
|
||||||
// format and the second with an id (mime-type) of the format.
|
// format and the second with an id (mime-type) of the format.
|
||||||
gettext_f(
|
gettext_f(
|
||||||
"Unknown image format “{} ({}).”",
|
"Unknown image format “{} ({}).”",
|
||||||
[&description, &mime_type],
|
[&description, &mime_type],
|
||||||
)
|
)
|
||||||
};
|
};
|
||||||
|
|
||||||
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,31 +254,32 @@ 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() {
|
}
|
||||||
imp.error_more_info.connect_clicked(glib::clone!(
|
DecoderError::Generic(err) => {
|
||||||
#[weak(rename_to = obj)]
|
imp.error_more_info.connect_clicked(glib::clone!(
|
||||||
self,
|
#[weak(rename_to = obj)]
|
||||||
move |_| {
|
self,
|
||||||
LpErrorDetails::new(&obj.root().unwrap(), &err, ErrorType::Loader);
|
move |_| {
|
||||||
|
LpErrorDetails::new(&obj.root().unwrap(), &err, ErrorType::Loader);
|
||||||
|
}
|
||||||
|
));
|
||||||
|
|
||||||
|
if matches!(image.specific_error(), DecoderError::OutOfMemory) {
|
||||||
|
imp.error_page.set_description(Some(&gettext(
|
||||||
|
"There is not enough free memory available to load this image.",
|
||||||
|
)));
|
||||||
|
} else {
|
||||||
|
imp.error_page.set_description(Some(&gettext(
|
||||||
|
"Either the image file is corrupted or it contains unsupported elements.",
|
||||||
|
)));
|
||||||
}
|
}
|
||||||
));
|
|
||||||
|
|
||||||
if image.specific_error() == DecoderError::OutOfMemory {
|
imp.error_more_info.set_visible(true);
|
||||||
imp.error_page.set_description(Some(&gettext(
|
imp.stack.set_visible_child(&*imp.error_page);
|
||||||
"There is not enough free memory available to load this image.",
|
|
||||||
)));
|
|
||||||
} else {
|
|
||||||
imp.error_page.set_description(Some(&gettext(
|
|
||||||
"Either the image file is corrupted or it contains unsupported elements.",
|
|
||||||
)));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
imp.error_more_info.set_visible(true);
|
|
||||||
imp.stack.set_visible_child(&*imp.error_page);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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"
|
||||||
|
|
|
||||||
|
|
@ -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());
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue