Skip to content

Commit 9c9c74e

Browse files
add screenshot button and command line argument (#14)
1 parent 9c8518a commit 9c9c74e

File tree

8 files changed

+129
-15
lines changed

8 files changed

+129
-15
lines changed

be.alexandervanhee.gradia.json

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,26 @@
3636
}
3737
]
3838
},
39+
{
40+
"name":"libportal",
41+
"buildsystem":"meson",
42+
"config-opts":[
43+
"-Ddocs=false",
44+
"-Dbackend-gtk4=enabled"
45+
],
46+
"sources":[
47+
{
48+
"type":"archive",
49+
"url":"https://github.com/flatpak/libportal/archive/refs/tags/0.9.1.tar.gz",
50+
"sha256":"ea422b789ae487e04194d387bea031fd7485bf88a18aef8c767f7d1c29496a4e",
51+
"x-checker-data":{
52+
"type":"anitya",
53+
"project-id":230124,
54+
"url-template":"https://github.com/flatpak/libportal/archive/refs/tags/$version.tar.gz"
55+
}
56+
}
57+
]
58+
},
3959
{
4060
"name": "gradia",
4161
"builddir": true,
Lines changed: 2 additions & 0 deletions
Loading

data/resources.data.gresource.xml.in

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
<file preprocess="xml-stripblanks">icons/scalable/actions/draw-line-symbolic.svg</file>
1111
<file preprocess="xml-stripblanks">icons/scalable/actions/text-insert2-symbolic.svg</file>
1212
<file preprocess="xml-stripblanks">icons/scalable/actions/pointer-primary-click-symbolic.svg</file>
13+
<file preprocess="xml-stripblanks">icons/scalable/actions/screenshooter-symbolic.svg</file>
1314

1415
</gresource>
1516
</gresources>

gradia/gradia.in

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ if __name__ == '__main__':
4141

4242
gi.require_version('Gtk', '4.0')
4343
gi.require_version('Adw', '1')
44+
gi.require_version('Xdp', '1.0')
4445

4546
from gi.repository import Gio
4647

gradia/main.py

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -32,14 +32,25 @@ class GradiaApp(Adw.Application):
3232
def __init__(self, version: str):
3333
super().__init__(
3434
application_id="be.alexandervanhee.gradia",
35-
flags=Gio.ApplicationFlags.HANDLES_OPEN
35+
flags=Gio.ApplicationFlags.HANDLES_COMMAND_LINE
3636
)
3737
self.temp_dir = tempfile.mkdtemp()
3838
self.version = version
39-
self.file_to_open = None
39+
self.init_with_screenshot = False
40+
41+
def do_command_line(self, command_line: Gio.ApplicationCommandLine) -> int:
42+
args = command_line.get_arguments()[1:]
43+
self.init_with_screenshot = "--screenshot" in args
44+
self.activate()
45+
return 0
4046

4147
def do_activate(self):
42-
self.ui = GradientWindow(self.temp_dir, version=self.version, application=self)
48+
self.ui = GradientWindow(
49+
self.temp_dir,
50+
version=self.version,
51+
application=self,
52+
init_with_screenshot=self.init_with_screenshot
53+
)
4354
self.ui.build_ui()
4455
self.ui.show()
4556

@@ -55,11 +66,11 @@ def do_shutdown(self):
5566
finally:
5667
Gio.Application.do_shutdown(self)
5768

58-
5969
def main(version: str):
6070
try:
6171
app = GradiaApp(version=version)
6272
return app.run(sys.argv)
6373
except Exception as e:
6474
print('Application closed with an exception:', e)
6575
return 1
76+

gradia/ui/image_loaders.py

Lines changed: 62 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,10 @@
1515
#
1616
# SPDX-License-Identifier: GPL-3.0-or-later
1717

18+
1819
import os
1920
from typing import Optional, Tuple
20-
from gi.repository import Gtk, Gio, Gdk
21+
from gi.repository import Gtk, Gio, Gdk, GLib, Xdp
2122
from gradia.clipboard import save_texture_to_file
2223

2324
ImportFormat = Tuple[str, str]
@@ -176,6 +177,63 @@ def _handle_clipboard_texture(
176177
self.window._set_loading_state(False)
177178

178179

180+
class ScreenshotImageLoader(BaseImageLoader):
181+
"""Handles loading images through screenshot capture"""
182+
183+
def __init__(self, window: Gtk.ApplicationWindow, temp_dir: str) -> None:
184+
super().__init__(window, temp_dir)
185+
self.portal = Xdp.Portal()
186+
187+
def take_screenshot(self) -> None:
188+
"""Initiate screenshot capture"""
189+
try:
190+
self.portal.take_screenshot(
191+
None,
192+
Xdp.ScreenshotFlags.INTERACTIVE,
193+
None,
194+
self._on_screenshot_taken,
195+
None
196+
)
197+
except Exception as e:
198+
print(f"Failed to initiate screenshot: {e}")
199+
self.window._show_notification(_("Failed to take screenshot"))
200+
201+
def _on_screenshot_taken(self, portal_object, result, user_data) -> None:
202+
"""Handle screenshot completion"""
203+
try:
204+
uri = self.portal.take_screenshot_finish(result)
205+
self._handle_screenshot_uri(uri)
206+
except GLib.Error as e:
207+
print(f"Screenshot error: {e}")
208+
self.window._show_notification(_("Screenshot cancelled"))
209+
210+
def _handle_screenshot_uri(self, uri: str) -> None:
211+
"""Process the screenshot URI and convert to local file"""
212+
try:
213+
file = Gio.File.new_for_uri(uri)
214+
success, contents, _unused = file.load_contents(None)
215+
if not success or not contents:
216+
raise Exception("Failed to load screenshot data")
217+
218+
temp_filename = f"screenshot_{os.urandom(6).hex()}.png"
219+
temp_path = os.path.join(self.temp_dir, temp_filename)
220+
221+
with open(temp_path, 'wb') as f:
222+
f.write(contents)
223+
224+
filename = _("Screenshot")
225+
location = _("Screenshot")
226+
227+
self._set_image_and_update_ui(temp_path, filename, location)
228+
self.window._show_notification(_("Screenshot captured!"))
229+
230+
except Exception as e:
231+
print(f"Error processing screenshot: {e}")
232+
self.window._show_notification(_("Failed to process screenshot"))
233+
finally:
234+
self.window._set_loading_state(False)
235+
236+
179237
class ImportManager:
180238
def __init__(self, window: Gtk.ApplicationWindow, temp_dir: str) -> None:
181239
self.window: Gtk.ApplicationWindow = window
@@ -184,6 +242,7 @@ def __init__(self, window: Gtk.ApplicationWindow, temp_dir: str) -> None:
184242
self.file_loader: FileDialogImageLoader = FileDialogImageLoader(window, temp_dir)
185243
self.drag_drop_loader: DragDropImageLoader = DragDropImageLoader(window, temp_dir)
186244
self.clipboard_loader: ClipboardImageLoader = ClipboardImageLoader(window, temp_dir)
245+
self.screenshot_loader: ScreenshotImageLoader = ScreenshotImageLoader(window, temp_dir)
187246

188247
def open_file_dialog(self) -> None:
189248
self.file_loader.open_file_dialog()
@@ -197,3 +256,5 @@ def _on_drop_action(self, action: Optional[object], param: object) -> None:
197256
def load_from_clipboard(self) -> None:
198257
self.clipboard_loader.load_from_clipboard()
199258

259+
def take_screenshot(self) -> None:
260+
self.screenshot_loader.take_screenshot()

gradia/ui/ui_parts.py

Lines changed: 23 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -31,12 +31,12 @@ def create_header_bar() -> Adw.HeaderBar:
3131
open_btn.set_action_name("app.open")
3232
header_bar.pack_start(open_btn)
3333

34-
# Copy from clipboard button
35-
copy_btn = Gtk.Button.new_from_icon_name("clipboard-symbolic")
36-
copy_btn.get_style_context().add_class("flat")
37-
copy_btn.set_tooltip_text(_("Paste from Clipboard"))
38-
copy_btn.set_action_name("app.paste")
39-
header_bar.pack_start(copy_btn)
34+
# Screenshot button
35+
screenshot_btn = Gtk.Button.new_from_icon_name("screenshooter-symbolic")
36+
screenshot_btn.get_style_context().add_class("flat")
37+
screenshot_btn.set_tooltip_text(_("Take a screenshot"))
38+
screenshot_btn.set_action_name("app.screenshot")
39+
header_bar.pack_start(screenshot_btn)
4040

4141
# About menu button with popover menu
4242
about_menu_btn = Gtk.MenuButton(icon_name="open-menu-symbolic")
@@ -170,22 +170,37 @@ def create_spinner_widget() -> Gtk.Widget:
170170
return spinner_box, spinner
171171

172172
def create_status_page() -> Gtk.Widget:
173+
screenshot_btn = Gtk.Button.new_with_label("_Take a screenshot…")
174+
screenshot_btn.set_use_underline(True)
175+
screenshot_btn.set_halign(Gtk.Align.CENTER)
176+
177+
style_context = screenshot_btn.get_style_context()
178+
style_context.add_class("pill")
179+
style_context.add_class("text-button")
180+
style_context.add_class("suggested-action")
181+
182+
screenshot_btn.set_action_name("app.screenshot")
183+
173184
open_status_btn = Gtk.Button.new_with_label("_Open Image…")
174185
open_status_btn.set_use_underline(True)
175186
open_status_btn.set_halign(Gtk.Align.CENTER)
176187

177188
style_context = open_status_btn.get_style_context()
178189
style_context.add_class("pill")
179190
style_context.add_class("text-button")
180-
style_context.add_class("suggested-action")
181191

182192
open_status_btn.set_action_name("app.open")
183193

194+
button_box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=12)
195+
button_box.set_halign(Gtk.Align.CENTER)
196+
button_box.append(screenshot_btn)
197+
button_box.append(open_status_btn)
198+
184199
status_page = Adw.StatusPage.new()
185200
status_page.set_icon_name("image-x-generic-symbolic")
186201
status_page.set_title("No Image Loaded")
187202
status_page.set_description("Drag and drop one here")
188-
status_page.set_child(open_status_btn)
203+
status_page.set_child(button_box)
189204

190205
return status_page
191206

gradia/ui/window.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ class GradientWindow(Adw.ApplicationWindow):
4545
# Temp file names
4646
TEMP_PROCESSED_FILENAME: str = "processed.png"
4747

48-
def __init__(self, temp_dir: str, version: str, **kwargs) -> None:
48+
def __init__(self, temp_dir: str, version: str, init_with_screenshot: bool = False, **kwargs) -> None:
4949
super().__init__(**kwargs)
5050

5151
self.app: Adw.Application = kwargs['application']
@@ -77,6 +77,7 @@ def __init__(self, temp_dir: str, version: str, **kwargs) -> None:
7777
self.create_action("open", lambda *_: self.import_manager.open_file_dialog(), ["<Primary>o"])
7878
self.create_action("load-drop", self.import_manager._on_drop_action)
7979
self.create_action("paste", lambda *_: self.import_manager.load_from_clipboard(), ["<Primary>v"])
80+
self.create_action("screenshot", lambda *_: self.import_manager.take_screenshot(), ["<Primary>a"])
8081

8182
self.create_action("save", lambda *_: self.export_manager.save_to_file(), ["<Primary>s"], enabled=False)
8283
self.create_action("copy", lambda *_: self.export_manager.copy_to_clipboard(), ["<Primary>c"], enabled=False)
@@ -87,13 +88,15 @@ def __init__(self, temp_dir: str, version: str, **kwargs) -> None:
8788
self.create_action("undo", lambda *_: self.drawing_overlay.undo(), ["<Primary>z"])
8889
self.create_action("redo", lambda *_: self.drawing_overlay.redo(), ["<Primary><Shift>z"])
8990
self.create_action("clear", lambda *_: self.drawing_overlay.clear_drawing())
90-
self.create_action("draw-mode", lambda *_: self.drawing_overlay.set_drawing_mode(mode))
9191
self.create_action_with_param("draw-mode", lambda action, param: self.drawing_overlay.set_drawing_mode(DrawingMode(param.get_string())))
9292

9393
self.create_action_with_param("pen-color", lambda action, param: self._set_pen_color_from_string(param.get_string()))
9494
self.create_action_with_param("fill-color", lambda action, param: self._set_fill_color_from_string(param.get_string()))
9595
self.create_action("del-selected", lambda *_: self.drawing_overlay.remove_selected_action(), ["<Primary>x", "Delete"])
9696

97+
if init_with_screenshot:
98+
self.import_manager.take_screenshot()
99+
97100

98101
def create_action(self, name: str, callback: Callable[..., None], shortcuts: Optional[list[str]] = None, enabled: bool = True) -> None:
99102
action: Gio.SimpleAction = Gio.SimpleAction.new(name, None)

0 commit comments

Comments
 (0)