Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions data/be.alexandervanhee.gradia.gschema.xml
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,14 @@
<default>true</default>
<summary>Whether to compress the exported file (if supported)</summary>
</key>
<key name="fast-screenshot-mode" type="s">
<default>'INTERACTIVE'</default>
<summary>Mode used for fast screenshots</summary>
</key>
<key name="fast-screenshot-overwrite-original" type="b">
<default>true</default>
<summary>Whether to overwrite the original screenshot file when using fast screenshot mode</summary>
</key>
<key name="trash-screenshots-on-close" type="b">
<default>false</default>
<summary>Whether to trash all taken screenshots from the current session on close.</summary>
Expand Down
2 changes: 2 additions & 0 deletions data/help.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
16 changes: 16 additions & 0 deletions data/ui/preferences_window.blp
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
16 changes: 16 additions & 0 deletions gradia/backend/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -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")
Expand Down
18 changes: 15 additions & 3 deletions gradia/gradia.in
Original file line number Diff line number Diff line change
Expand Up @@ -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@'
Expand All @@ -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()
Expand Down Expand Up @@ -87,15 +88,26 @@ 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:
print("Screenshot was cancelled or failed. Exiting.")
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
Expand Down
15 changes: 10 additions & 5 deletions gradia/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -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()

Expand Down Expand Up @@ -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)
Expand All @@ -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…")
Expand Down
43 changes: 43 additions & 0 deletions gradia/ui/image_exporters.py
Original file line number Diff line number Diff line change
Expand Up @@ -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"))
1 change: 1 addition & 0 deletions gradia/ui/image_loaders.py
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand Down
32 changes: 32 additions & 0 deletions gradia/ui/preferences/preferences_window.py
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand All @@ -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()
Expand Down Expand Up @@ -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()
Expand Down Expand Up @@ -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:
Expand Down
8 changes: 8 additions & 0 deletions gradia/ui/window.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -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
Expand Down Expand Up @@ -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)

Expand Down Expand Up @@ -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"]:
Expand Down