diff --git a/data/be.alexandervanhee.gradia.gschema.xml b/data/be.alexandervanhee.gradia.gschema.xml
index 4e4adc5..e4e1a04 100644
--- a/data/be.alexandervanhee.gradia.gschema.xml
+++ b/data/be.alexandervanhee.gradia.gschema.xml
@@ -60,6 +60,14 @@
true
Whether to compress the exported file (if supported)
+
+ 'INTERACTIVE'
+ Mode used for fast screenshots
+
+
+ true
+ Whether to overwrite the original screenshot file when using fast screenshot mode
+
false
Whether to trash all taken screenshots from the current session on close.
diff --git a/data/help.txt b/data/help.txt
index 084b7ed..55a70d4 100644
--- a/data/help.txt
+++ b/data/help.txt
@@ -4,6 +4,7 @@ Usage: gradia [OPTIONS] [FILES...]
Options:
-h, --help Show this help message and exit
+ --fast Save screenshot with default settings without opening editor
--screenshot[=MODE] Take a screenshot on startup
MODE can be:
INTERACTIVE (default) - Interactive screenshot
@@ -20,6 +21,7 @@ Examples:
gradia --screenshot=FULL Take a full screen screenshot
gradia --screenshot --delay=3000 Take screenshot after 3 second delay
gradia --screenshot=FULL --delay=1500 Take full screenshot after 1.5 second delay
+ gradia --fast --screenshot-file=/path/to/screenshot.png Save screenshot with defaults
cat image.png | gradia Open image from standard input (stdin)
Report bugs to: https://github.com/alexandervanhee/gradia
diff --git a/data/ui/preferences_window.blp b/data/ui/preferences_window.blp
index e0e6869..a671a84 100644
--- a/data/ui/preferences_window.blp
+++ b/data/ui/preferences_window.blp
@@ -38,6 +38,22 @@ template $GradiaPreferencesWindow: Adw.PreferencesDialog {
}
}
}
+
+ Adw.ComboRow fast_screenshot_mode_combo {
+ name: "fast_screenshot_mode_combo";
+ title: _("Fast Screenshot Mode");
+ use-underline: true;
+ subtitle: _("Choose the default mode for fast screenshots");
+ }
+
+ Adw.SwitchRow overwrite_fast_screenshot_switch {
+ name: "overwrite_fast_screenshot_switch";
+ title: _("Overwrite Original Screenshot on Fast Save");
+ use-underline: true;
+ subtitle: _("Replace the original screenshot file when using fast screenshot mode");
+ tooltip-text: _("Use the source screenshot file as the save destination for fast screenshots");
+ activatable: true;
+ }
}
Adw.PreferencesGroup {
diff --git a/gradia/backend/settings.py b/gradia/backend/settings.py
index 06ef0bc..5436d7b 100644
--- a/gradia/backend/settings.py
+++ b/gradia/backend/settings.py
@@ -83,6 +83,22 @@ def export_format(self, value: str) -> None:
def export_compress(self) -> bool:
return self._settings.get_boolean("export-compress")
+ @property
+ def fast_screenshot_mode(self) -> str:
+ return self._settings.get_string("fast-screenshot-mode")
+
+ @fast_screenshot_mode.setter
+ def fast_screenshot_mode(self, value: str) -> None:
+ self._settings.set_string("fast-screenshot-mode", value)
+
+ @property
+ def fast_screenshot_overwrite_original(self) -> bool:
+ return self._settings.get_boolean("fast-screenshot-overwrite-original")
+
+ @fast_screenshot_overwrite_original.setter
+ def fast_screenshot_overwrite_original(self, value: bool) -> None:
+ self._settings.set_boolean("fast-screenshot-overwrite-original", value)
+
@property
def delete_screenshots_on_close(self) -> bool:
return self._settings.get_boolean("trash-screenshots-on-close")
diff --git a/gradia/gradia.in b/gradia/gradia.in
index b00af5b..c67aeec 100755
--- a/gradia/gradia.in
+++ b/gradia/gradia.in
@@ -26,7 +26,7 @@ import logging
from datetime import datetime
import gi
gi.require_version('Xdp', '1.0')
-from gi.repository import Xdp, GLib
+from gi.repository import Xdp, GLib, Gio
logging.getLogger("PIL").setLevel(logging.WARNING)
VERSION = '@VERSION@'
pkgdatadir = '@PKGDATA_DIR@'
@@ -36,6 +36,7 @@ signal.signal(signal.SIGINT, signal.SIG_DFL)
locale.bindtextdomain('gradia', localedir)
locale.textdomain('gradia')
gettext.install('gradia', localedir)
+from gradia.constants import app_id
class QuickStartScreenshotTaker:
def __init__(self):
self.portal = Xdp.Portal()
@@ -87,8 +88,17 @@ if __name__ == '__main__':
except ValueError:
print("Invalid delay value. Using default of 0.")
delay = 0
- if mode in ('INTERACTIVE', 'FULL'):
- flags = Xdp.ScreenshotFlags.INTERACTIVE if mode == 'INTERACTIVE' else Xdp.ScreenshotFlags.NONE
+ if mode in ('INTERACTIVE', 'FULL', 'FAST'):
+ if mode == 'FAST':
+ settings = Gio.Settings.new(app_id)
+ fast_mode = settings.get_string('fast-screenshot-mode')
+ if fast_mode == 'FULL':
+ flags = Xdp.ScreenshotFlags.NONE
+ else:
+ flags = Xdp.ScreenshotFlags.INTERACTIVE
+ else:
+ flags = Xdp.ScreenshotFlags.INTERACTIVE if mode == 'INTERACTIVE' else Xdp.ScreenshotFlags.NONE
+
screenshotter = QuickStartScreenshotTaker()
screenshot_path = screenshotter.take_screenshot(flags=flags, delay_ms=delay)
if screenshot_path is None:
@@ -96,6 +106,8 @@ if __name__ == '__main__':
sys.exit(1)
# Add the screenshot path to command line arguments
sys.argv.append(f"--screenshot-file={screenshot_path}")
+ if mode == 'FAST':
+ sys.argv.append("--fast")
gi.require_version('Gtk', '4.0')
gi.require_version('Adw', '1')
from gi.repository import Gio
diff --git a/gradia/main.py b/gradia/main.py
index b2f1c8f..a040a3a 100644
--- a/gradia/main.py
+++ b/gradia/main.py
@@ -61,11 +61,14 @@ def do_command_line(self, command_line: Gio.ApplicationCommandLine) -> int:
files_to_open = []
screenshot_file = None
+ fast = False
for arg in args:
if arg.startswith("--screenshot-file="):
screenshot_file = arg.split("=", 1)[1]
logging.info(f"Screenshot file detected: {screenshot_file}")
+ elif arg == '--fast':
+ fast = True
elif not arg.startswith("--"):
try:
file = Gio.File.new_for_commandline_arg(arg)
@@ -82,7 +85,7 @@ def do_command_line(self, command_line: Gio.ApplicationCommandLine) -> int:
for path in files_to_open:
self._open_window(file_path=path)
elif screenshot_file:
- self._open_window(start_screenshot=screenshot_file)
+ self._open_window(start_screenshot=screenshot_file, fast=fast)
else:
self.activate()
@@ -113,8 +116,8 @@ def do_activate(self):
else:
self._open_window(None)
- def _open_window(self, file_path: Optional[str] = None, start_screenshot: Optional[str] = None):
- logging.info(f"Opening window with file_path={file_path}")
+ def _open_window(self, file_path: Optional[str] = None, start_screenshot: Optional[str] = None, fast: bool = False):
+ logging.info(f"Opening window with file_path={file_path}, fast={fast}")
temp_dir = tempfile.mkdtemp()
logging.debug(f"Created temp directory: {temp_dir}")
self.temp_dirs.append(temp_dir)
@@ -124,9 +127,11 @@ def _open_window(self, file_path: Optional[str] = None, start_screenshot: Option
version=self.version,
application=self,
file_path=file_path,
- start_screenshot=start_screenshot
+ start_screenshot=start_screenshot,
+ fast=fast
)
- window.show()
+ if not fast:
+ window.show()
def on_shutdown(self, application):
logging.info("Application shutdown started, cleaning temp directories…")
diff --git a/gradia/ui/image_exporters.py b/gradia/ui/image_exporters.py
index 5a3477b..8999655 100644
--- a/gradia/ui/image_exporters.py
+++ b/gradia/ui/image_exporters.py
@@ -514,3 +514,46 @@ def close_handler(self, copy: bool, save: bool, callback: callable = None):
def is_export_available(self) -> bool:
"""Check if export operations are available"""
return bool(self.window.processed_pixbuf)
+
+ def auto_save_to_screenshot_folder(self) -> None:
+ """Automatically save processed image to screenshot folder"""
+ logger.info("Starting auto-save to screenshot folder")
+ if not self.is_export_available():
+ logger.warning("Export not available, no processed pixbuf")
+ return
+
+ from gradia.utils.timestamp_filename import TimestampedFilenameGenerator
+ import os
+ from gi.repository import GLib
+
+
+ if hasattr(self.window, 'start_screenshot') and self.window.start_screenshot and self.window.settings.fast_screenshot_overwrite_original:
+ save_path = self.window.start_screenshot
+ logger.info(f"Saving back to original file: {save_path}")
+ else:
+ folder = self.window.settings.screenshot_folder
+ if not folder:
+ folder = GLib.get_user_special_dir(GLib.UserDirectory.DIRECTORY_PICTURES)
+ if not folder:
+ folder = os.path.expanduser("~/Pictures/Screenshots")
+ if not os.path.exists(folder):
+ os.makedirs(folder)
+ logger.info(f"Saving to folder: {folder}")
+
+ # Generate filename
+ generator = TimestampedFilenameGenerator()
+ base_name = generator.generate(_("Edited Screenshot From %Y-%m-%d %H-%M-%S"))
+ ext = SUPPORTED_EXPORT_FORMATS[self.window.settings.export_format]['extensions'][0]
+ filename = base_name + ext
+ save_path = os.path.join(folder, filename)
+ logger.info(f"Generated save path: {save_path}")
+
+ # Save
+ pixbuf = self.file_exporter.get_processed_pixbuf()
+ try:
+ pixbuf.savev(save_path, self.window.settings.export_format, [], [])
+ self.window._show_notification(_("Image saved to screenshot folder"))
+ logger.info(f"Auto-saved image to: {save_path}")
+ except Exception as e:
+ logger.error(f"Failed to auto-save image: {e}")
+ self.window._show_notification(_("Failed to save image"))
diff --git a/gradia/ui/image_loaders.py b/gradia/ui/image_loaders.py
index 73709da..70e5d00 100644
--- a/gradia/ui/image_loaders.py
+++ b/gradia/ui/image_loaders.py
@@ -307,6 +307,7 @@ def _handle_screenshot_uri(self, uri: str) -> None:
self.window._show_notification(_("Failed to process screenshot"))
def load_path_as_screenshot(self, file_path: str) -> None:
+ logger.info(f"Loading screenshot from path: {file_path}")
try:
file = Gio.File.new_for_path(file_path)
uri = file.get_uri()
diff --git a/gradia/ui/preferences/preferences_window.py b/gradia/ui/preferences/preferences_window.py
index eb5acdb..0821a9f 100644
--- a/gradia/ui/preferences/preferences_window.py
+++ b/gradia/ui/preferences/preferences_window.py
@@ -40,6 +40,8 @@ class PreferencesWindow(Adw.PreferencesDialog):
delete_screenshot_switch: Adw.SwitchRow = Gtk.Template.Child()
overwrite_screenshot_switch: Adw.SwitchRow = Gtk.Template.Child()
confirm_upload_switch: Adw.SwitchRow = Gtk.Template.Child()
+ fast_screenshot_mode_combo: Adw.ComboRow = Gtk.Template.Child()
+ overwrite_fast_screenshot_switch: Adw.SwitchRow = Gtk.Template.Child()
save_format_combo: Adw.ComboRow = Gtk.Template.Child()
provider_name: Gtk.Label = Gtk.Template.Child()
exiting_combo: Adw.ComboRow = Gtk.Template.Child()
@@ -66,6 +68,7 @@ def __init__(self, parent_window: Adw.ApplicationWindow, **kwargs):
self.add_controller(shortcut_controller)
def _setup_widgets(self):
+ self._setup_fast_screenshot_mode_combo()
self._setup_save_format_combo()
self._setup_exiting_combo()
self._setup_provider_display()
@@ -106,6 +109,34 @@ def _setup_save_format_combo(self):
self.save_format_combo.connect("notify::selected", self._on_save_format_changed)
+ def _setup_fast_screenshot_mode_combo(self):
+ current_mode = self.settings.fast_screenshot_mode
+ string_list = Gtk.StringList()
+
+ fast_mode_options = [
+ ("INTERACTIVE", _("Interactive")),
+ ("FULL", _("Full Screen"))
+ ]
+ self.fast_mode_keys = [key for key, _ in fast_mode_options]
+
+ for key, display_name in fast_mode_options:
+ string_list.append(display_name)
+
+ self.fast_screenshot_mode_combo.set_model(string_list)
+
+ try:
+ current_index = self.fast_mode_keys.index(current_mode)
+ self.fast_screenshot_mode_combo.set_selected(current_index)
+ except ValueError:
+ self.fast_screenshot_mode_combo.set_selected(0)
+
+ self.fast_screenshot_mode_combo.connect("notify::selected", self._on_fast_screenshot_mode_changed)
+
+ def _on_fast_screenshot_mode_changed(self, combo_row, pspec) -> None:
+ selected = combo_row.get_selected()
+ if selected < len(self.fast_mode_keys):
+ self.settings.fast_screenshot_mode = self.fast_mode_keys[selected]
+
def _setup_exiting_combo(self):
current_exit_method = self.settings.exit_method
string_list = Gtk.StringList()
@@ -161,6 +192,7 @@ def _bind_settings(self):
self.settings.bind_switch(self.delete_screenshot_switch,"trash-screenshots-on-close")
self.settings.bind_switch(self.confirm_upload_switch,"show-export-confirm-dialog")
self.settings.bind_switch(self.overwrite_screenshot_switch,"overwrite-screenshot")
+ self.settings.bind_switch(self.overwrite_fast_screenshot_switch,"fast-screenshot-overwrite-original")
@Gtk.Template.Callback()
def on_choose_provider_clicked(self, button: Gtk.Button) -> None:
diff --git a/gradia/ui/window.py b/gradia/ui/window.py
index 89c186e..9694073 100644
--- a/gradia/ui/window.py
+++ b/gradia/ui/window.py
@@ -76,6 +76,7 @@ def __init__(
version: str,
file_path: Optional[str] = None,
start_screenshot: Optional[str] = None,
+ fast: bool = False,
**kwargs
) -> None:
super().__init__(**kwargs)
@@ -85,6 +86,7 @@ def __init__(
self.app: Adw.Application = kwargs['application']
self.temp_dir: str = temp_dir
self.version: str = version
+ self.fast = fast
self.start_screenshot = start_screenshot
self.file_path: Optional[str] = file_path
self.image: Optional[LoadedImage] = None
@@ -374,6 +376,8 @@ def after_process():
self.export_manager.copy_to_clipboard(silent=True)
self._set_export_ready(True)
self.lookup_action("open-folder").set_enabled(image.has_proper_folder())
+ if self.fast:
+ self._auto_save_and_exit()
self.process_image(callback=after_process)
@@ -444,6 +448,10 @@ def _set_loading_state(self, is_loading: bool) -> None:
child: str = getattr(self, "_previous_stack_child", self.PAGE_IMAGE)
self.image_stack.set_visible_child_name(child)
+ def _auto_save_and_exit(self) -> None:
+ self.export_manager.auto_save_to_screenshot_folder()
+ self.app.quit()
+
def _set_export_ready(self, enabled: bool) -> None:
self.image_ready = True
for action_name in ["save", "copy"]: