From f2d4fa6e0d9119e5791afb6d75cfd97f34be9181 Mon Sep 17 00:00:00 2001 From: Abhinandan Thakur Date: Wed, 31 Dec 2025 11:35:54 +0530 Subject: [PATCH] Save-Dialog: Added a Save Dialog for Ctrl+Q Unsaved Changes Changed the logic of app.quit accelerator, introduced a dialog in it. Also made functions such is_save_senstive() and save_overwrite() publicly accessible for logic building. Fixes: #537 Part-of: --- src/application.rs | 51 +++++++++++++++++++++++++++++++++++++- src/widgets/edit_window.rs | 18 ++++++++++++-- src/widgets/window.rs | 7 ++++++ 3 files changed, 73 insertions(+), 3 deletions(-) diff --git a/src/application.rs b/src/application.rs index 526295f..255a0e6 100644 --- a/src/application.rs +++ b/src/application.rs @@ -20,6 +20,7 @@ // // SPDX-License-Identifier: GPL-3.0-or-later +use crate::util::gettext::*; use adw::prelude::*; use adw::subclass::prelude::*; @@ -133,7 +134,55 @@ impl LpApplication { .activate(|app: &Self, _, _| app.show_help()) .build(), gio::ActionEntryBuilder::new("quit") - .activate(|app: &Self, _, _| app.quit()) + .activate(|app: &Self, _, _| { + if let Some(win) = app.active_window() { + if let Some(lp_window) = win.downcast_ref::() { + if let Some(edit_window) = lp_window.edit_window() { + if edit_window.has_unsaved_changes() { + let dialog = adw::AlertDialog::new( + Some(&gettext("Unsaved Changes")), + Some(&gettext( + "Changes which are not lost will be permanently lost", + )), + ); + + dialog.add_response("cancel", &gettext("Cancel")); + dialog.add_response("discard", &gettext("Discard")); + dialog.add_response("save", &gettext("Save")); + + let app_clone = app.clone(); + let win_clone = win.clone(); + let edit_window_clone = edit_window.clone(); + let dialog_clone = dialog.clone(); + + dialog.set_response_appearance( + "discard", + adw::ResponseAppearance::Destructive, + ); + + glib::MainContext::default().spawn_local(async move { + let res = dialog_clone.choose_future(&win_clone).await; + println!("User response: {}", res); + match res.as_str() { + "cancel" => {} + "discard" => { + app_clone.quit(); + } + "save" => { + edit_window_clone.save_as(); + } + _ => {} + } + }); + + dialog.present(Some(&win)); + return; + } + } + } + app.quit(); + } + }) .build(), gio::ActionEntryBuilder::new("new-window") .activate(|app: &Self, _, _| { diff --git a/src/widgets/edit_window.rs b/src/widgets/edit_window.rs index 1df4cf3..3ee907f 100644 --- a/src/widgets/edit_window.rs +++ b/src/widgets/edit_window.rs @@ -244,7 +244,7 @@ mod imp { } } - async fn save_overwrite(&self) { + pub(super) async fn save_overwrite(&self) { let obj = self.obj(); self.save.popdown(); @@ -434,7 +434,7 @@ mod imp { } } - fn is_save_sensitive(&self) -> bool { + pub(super) fn is_save_sensitive(&self) -> bool { let obj = self.obj(); self.operations @@ -485,6 +485,20 @@ impl LpEditWindow { imp.update_save_state(false); } + pub fn has_unsaved_changes(&self) -> bool { + self.imp().is_save_sensitive() + } + + pub fn save_as(&self) { + glib::spawn_future_local(glib::clone!( + #[weak(rename_to=win)] + self, + async move { + win.imp().save_overwrite().await; + } + )); + } + pub fn add_operation(&self, operation: glycin::Operation) { let imp = self.imp(); diff --git a/src/widgets/window.rs b/src/widgets/window.rs index f829743..902a794 100644 --- a/src/widgets/window.rs +++ b/src/widgets/window.rs @@ -268,6 +268,13 @@ impl LpWindow { } } + pub fn edit_window(&self) -> Option { + self.imp() + .edit_window_child + .child() + .and_then(|w| w.downcast::().ok()) + } + pub fn image_view(&self) -> LpImageView { self.imp().image_window.image_view() }