diff --git a/src/core/screenshot_controller.py b/src/core/screenshot_controller.py index 817acec..46f1173 100644 --- a/src/core/screenshot_controller.py +++ b/src/core/screenshot_controller.py @@ -1,4 +1,5 @@ # Python imports +import time # Lib imports import gi @@ -76,9 +77,30 @@ class ScreenshotController: window = settings.get_main_window() window.show() - def _grab_region(self, region_window): + def _grab_region(self, region_window, x1, y1, x2, y2): + def show_region_window(): + # NOTE: No clue why showing window has it move outta prior place. + # So, move back to original spot before showing... + region_window.move(0, 0) + region_window.show() + + @daemon_threaded + def do_bounding_box_grab(x1, y1, x2, y2): + while region_window.is_visible(): + ... + + time.sleep(0.5) + im = capture.grab(bbox = (x1, y1, x2, y2), childprocess = False) + im.save( settings.generate_screenshot_name() ) + GLib.idle_add(show_region_window) + + region_window.hide() + offset = 1 + do_bounding_box_grab(x1 - offset, y1 - offset, x2 + offset, y2 + offset) + + def _old_grab_region(self, region_window): logger.info("Grabbing Selected Region...") - x, y = region_window.get_position() + x1, y1 = region_window.get_position() w, h = region_window.get_size() x2 = x + w y2 = y + h @@ -94,13 +116,14 @@ class ScreenshotController: while region_window.is_visible(): ... + time.sleep(0.5) im = capture.grab(bbox = (x1, y1, x2, y2), childprocess = False) im.save( settings.generate_screenshot_name() ) GLib.idle_add(show_region_window) region_window.hide() offset = 1 - do_bounding_box_grab(x - offset, y - offset, x2 + offset, y2 + offset) + do_bounding_box_grab(x1 - offset, y1 - offset, x2 + offset, y2 + offset) def grab_selected_monitor(self): @@ -119,4 +142,4 @@ class ScreenshotController: event_system.emit("grab_delay") window.hide() - do_bounding_box_grab(monitor.x, monitor.y, x2, y2) + do_bounding_box_grab(monitor.x, monitor.y, x2, y2) \ No newline at end of file diff --git a/src/core/widgets/_old_region/__init__.py b/src/core/widgets/_old_region/__init__.py new file mode 100644 index 0000000..72b072b --- /dev/null +++ b/src/core/widgets/_old_region/__init__.py @@ -0,0 +1,3 @@ +""" + Widgets Module +""" diff --git a/src/core/widgets/region/body_grid.py b/src/core/widgets/_old_region/body_grid.py similarity index 100% rename from src/core/widgets/region/body_grid.py rename to src/core/widgets/_old_region/body_grid.py diff --git a/src/core/widgets/_old_region/window.py b/src/core/widgets/_old_region/window.py new file mode 100644 index 0000000..6f8b731 --- /dev/null +++ b/src/core/widgets/_old_region/window.py @@ -0,0 +1,72 @@ +# Python imports + +# Lib imports +import gi +import cairo +gi.require_version('Gtk', '3.0') +gi.require_version('Gdk', '3.0') +from gi.repository import Gtk +from gi.repository import Gdk + +# Application imports +from .body_grid import BodyGrid + + + +class RegionWindow(Gtk.Window): + def __init__(self): + super(RegionWindow, self).__init__() + + self._set_window_data() + self._setup_styling() + self._setup_signals() + self._subscribe_to_events() + self._load_widgets() + + + def _setup_styling(self): + self.set_default_size(600, 480) + self.set_keep_above(True) + self.set_deletable(False) + self.set_decorated(False) + self.set_resizable(True) + self.set_skip_pager_hint(True) + self.set_skip_taskbar_hint(True) + self.set_has_resize_grip(True) + + + def _setup_signals(self): + ... + + def _subscribe_to_events(self): + event_system.subscribe("show_region_window", self._show_region_window) + + def _load_widgets(self): + gdk_window = self.get_screen().get_root_window() + self.add( BodyGrid(self, gdk_window) ) + + def _set_window_data(self) -> None: + screen = self.get_screen() + visual = screen.get_rgba_visual() + + if visual != None and screen.is_composited(): + self.set_visual(visual) + self.set_app_paintable(True) + self.connect("draw", self._area_draw) + + # bind css file + cssProvider = Gtk.CssProvider() + cssProvider.load_from_path( settings.get_css_file() ) + screen = Gdk.Screen.get_default() + styleContext = Gtk.StyleContext() + styleContext.add_provider_for_screen(screen, cssProvider, Gtk.STYLE_PROVIDER_PRIORITY_USER) + + def _area_draw(self, widget: Gtk.ApplicationWindow, cr: cairo.Context) -> None: + cr.set_source_rgba( *(0, 0, 0, 0.0) ) + cr.set_operator(cairo.OPERATOR_SOURCE) + cr.paint() + cr.set_operator(cairo.OPERATOR_OVER) + + + def _show_region_window(self): + self.show() \ No newline at end of file diff --git a/src/core/widgets/region/__init__.py b/src/core/widgets/region/__init__.py index 72b072b..8f8bf72 100644 --- a/src/core/widgets/region/__init__.py +++ b/src/core/widgets/region/__init__.py @@ -1,3 +1,3 @@ """ - Widgets Module -""" + Region Module +""" \ No newline at end of file diff --git a/src/core/widgets/region/draw_area.py b/src/core/widgets/region/draw_area.py new file mode 100644 index 0000000..5dde65c --- /dev/null +++ b/src/core/widgets/region/draw_area.py @@ -0,0 +1,167 @@ +# Python imports + +# Lib imports +import gi +gi.require_version('Gtk', '3.0') +gi.require_version('Gdk', '3.0') +from gi.repository import Gtk +from gi.repository import Gdk + +# Application imports +from .draw_types.draw_cross import DrawCross +from .draw_types.draw_grab_region import DrawGrabRegion + + + +class DrawArea(Gtk.DrawingArea): + def __init__(self): + super(DrawArea, self).__init__() + + + self.draw_type: DrawType = DrawCross() + self.region_mode: bool = False + self.grab_mode: bool = False + self.mouse_x: int = 0 + self.mouse_y: int = 0 + + + self._setup_style() + self._setup_signals() + self._load_widgets() + + self.show() + + self._render_start_cross_hair() + + + def _setup_style(self): + self.set_property("can-focus", True) + + def _setup_signals(self): + self.add_events( Gdk.EventMask.ALL_EVENTS_MASK ) + + self.connect("motion-notify-event", self._motion_notify_event) + self.connect("button-press-event", self._button_press_event) + self.connect("button-release-event", self._button_release_event) + self.connect("realize", self._realize) + self.connect("draw", self._draw) + + def _load_widgets(self): + ... + + def _render_start_cross_hair(self): + self.mouse_x = self.get_allocated_width() / 2 + self.mouse_y = self.get_allocated_height() / 2 + self.queue_draw() + + def _realize(self, widget): + self._hide_cursor() + + def _draw(self, area, cr): + self.draw_type.draw(area, cr) + + def _motion_notify_event(self, widget, eve): + if self.grab_mode: return + self._set_coords(eve) + self.queue_draw() + + def _button_press_event(self, widget, eve): + if self.grab_mode: return + if not eve.button == 1: return + self._set_to_region_mode(eve) + + def _button_release_event(self, widget, eve): + if self.grab_mode and eve.button == 2: # m-click + self._do_grab() + return + + if not eve.button in [1, 3]: return # l or r-click + + self._show_cursor() + + if eve.button == 3: # r-click + event_system.emit("grab_region_hide", (self.get_parent(),)) + return + + if self.grab_mode: + self._hide_cursor() + self._unset_grab_mode(eve) + return + + self._set_to_grab_mode(eve) + + def _show_cursor(self): + window = self.get_parent() + watch_cursor = Gdk.Cursor(Gdk.CursorType.ARROW) + window.get_window().set_cursor(watch_cursor) + + def _hide_cursor(self): + window = self.get_parent() + watch_cursor = Gdk.Cursor(Gdk.CursorType.BLANK_CURSOR ) + window.get_window().set_cursor(watch_cursor) + + def _set_to_region_mode(self, eve): + self.region_mode = True + self.draw_type = DrawGrabRegion() + self._set_coords(eve) + + def _set_to_grab_mode(self, eve): + self._set_coords(eve) + self.region_mode = False + self.grab_mode = True + + def _unset_grab_mode(self, eve): + self.grab_mode = False + self.draw_type = DrawCross() + self._set_coords(eve) + self.queue_draw() + + def _set_coords(self, eve): + if not self.region_mode: + self.mouse_x = int(eve.x) + self.mouse_y = int(eve.y) + self.region_start_x = int(eve.x) + self.region_start_y = int(eve.y) + else: + self.region_end_x = int(eve.x) + self.region_end_y = int(eve.y) + + def _do_grab(self): + sx = self.region_start_x + sy = self.region_start_y + ex = self.region_end_x + ey = self.region_end_y + + x1 = sx + y1 = sy + x2 = ex + y2 = ey + + if sx > ex and sy < ey: # NE to SW + x1 = sx - (sx - ex) + y1 = sy + x2 = sx + y2 = sy + (ey - sy) + elif not ex > sx and not ey > sy: # SE to NW + x1 = ex + y1 = ey + x2 = sx + y2 = sy + elif ex > sx and ey < sy: # SW to NE + x1 = ex - (ex - sx) + y1 = ey + x2 = ex + y2 = ey + (sy - ey) + + event_system.emit( + "grab_region", + ( + self.get_parent(), + x1, + y1, + x2, + y2, + ) + ) + + diff --git a/src/core/widgets/region/draw_types/__init__.py b/src/core/widgets/region/draw_types/__init__.py new file mode 100644 index 0000000..d077b11 --- /dev/null +++ b/src/core/widgets/region/draw_types/__init__.py @@ -0,0 +1,3 @@ +""" + Draw Types Module +""" \ No newline at end of file diff --git a/src/core/widgets/region/draw_types/draw_cross.py b/src/core/widgets/region/draw_types/draw_cross.py new file mode 100644 index 0000000..6b1423b --- /dev/null +++ b/src/core/widgets/region/draw_types/draw_cross.py @@ -0,0 +1,31 @@ +# Python imports + +# Lib imports + +# Application imports +from .draw_type_base import DrawTypeBase + + + +class DrawCross(DrawTypeBase): + def __init__(self): + super(DrawCross, self).__init__() + + + def draw(self, area, cr): + if not area.mouse_x or not area.mouse_y: return + + cr.set_source_rgba(1.0, 1.0, 1.0, 1.0) + cr.set_line_width(1.0) + cr.set_line_cap(2) # 0 = BUTT, 1 = ROUND, 2 = SQUARE + cr.set_line_join(1) # 0 = BEVEL, 1 = MITER, 2 = ROUND + + # Horizon + cr.move_to(0, area.mouse_y) + cr.line_to(area.get_allocated_width(), area.mouse_y) + cr.stroke() + + # Vertical + cr.move_to(area.mouse_x, 0) + cr.line_to(area.mouse_x, area.get_allocated_height()) + cr.stroke() \ No newline at end of file diff --git a/src/core/widgets/region/draw_types/draw_grab_region.py b/src/core/widgets/region/draw_types/draw_grab_region.py new file mode 100644 index 0000000..d6a543a --- /dev/null +++ b/src/core/widgets/region/draw_types/draw_grab_region.py @@ -0,0 +1,48 @@ +# Python imports + +# Lib imports + +# Application imports +from .draw_type_base import DrawTypeBase + + + +class DrawGrabRegion(DrawTypeBase): + def __init__(self): + super(DrawGrabRegion, self).__init__() + + + def draw(self, area, cr): + cr.set_source_rgba(1.0, 1.0, 0.0, 1.0) + cr.set_line_width(1.0) + cr.set_line_cap(2) # 0 = BUTT, 1 = ROUND, 2 = SQUARE + cr.set_line_join(1) # 0 = BEVEL, 1 = MITER, 2 = ROUND + + self._draw_start_cross(area, cr) + self._draw_end_cross(area, cr) + + def _draw_start_cross(self, area, cr): + # Horizon + cr.move_to(0, area.region_start_y) + cr.line_to(area.get_allocated_width(), area.region_start_y) + cr.stroke() + + # Vertical + cr.move_to(area.region_start_x, 0) + cr.line_to(area.region_start_x, area.get_allocated_height()) + cr.stroke() + + def _draw_end_cross(self, area, cr): + # Horizon + cr.move_to(0, area.region_end_y) + cr.line_to(area.get_allocated_width(), area.region_end_y) + cr.stroke() + + # Vertical + cr.move_to(area.region_end_x, 0) + cr.line_to(area.region_end_x, area.get_allocated_height()) + cr.stroke() + + + + diff --git a/src/core/widgets/region/draw_types/draw_type_base.py b/src/core/widgets/region/draw_types/draw_type_base.py new file mode 100644 index 0000000..e780368 --- /dev/null +++ b/src/core/widgets/region/draw_types/draw_type_base.py @@ -0,0 +1,15 @@ +# Python imports + +# Lib imports + +# Application imports + + + +class OverrideExceptionw(Exception): + ... + + +class DrawTypeBase: + def draw(self, widget, cr): + raise OverrideException("Method hasn't been overriden...") \ No newline at end of file diff --git a/src/core/widgets/region/window.py b/src/core/widgets/region/window.py index c7ebb29..3fe8062 100644 --- a/src/core/widgets/region/window.py +++ b/src/core/widgets/region/window.py @@ -9,7 +9,7 @@ from gi.repository import Gtk from gi.repository import Gdk # Application imports -from .body_grid import BodyGrid +from .draw_area import DrawArea @@ -25,7 +25,9 @@ class RegionWindow(Gtk.Window): def _setup_styling(self): - self.set_default_size(600, 480) + screen = Gdk.Screen.get_default() + ctx = self.get_style_context() + self.set_keep_above(True) self.set_deletable(False) self.set_decorated(False) @@ -34,6 +36,14 @@ class RegionWindow(Gtk.Window): self.set_skip_taskbar_hint(True) self.set_has_resize_grip(True) + self.move(0, 0) + self.set_size_request( + *self.get_screen_size( + Gdk.Display.get_default() + ) + ) + + def _setup_signals(self): ... @@ -41,8 +51,7 @@ class RegionWindow(Gtk.Window): event_system.subscribe("show_region_window", self._show_region_window) def _load_widgets(self): - gdk_window = self.get_screen().get_root_window() - self.add( BodyGrid(self, gdk_window) ) + self.add( DrawArea() ) def _set_window_data(self) -> None: screen = self.get_screen() @@ -51,7 +60,6 @@ class RegionWindow(Gtk.Window): if visual != None and screen.is_composited(): self.set_visual(visual) self.set_app_paintable(True) - self.connect("draw", self._area_draw) # bind css file cssProvider = Gtk.CssProvider() @@ -60,6 +68,19 @@ class RegionWindow(Gtk.Window): styleContext = Gtk.StyleContext() styleContext.add_provider_for_screen(screen, cssProvider, Gtk.STYLE_PROVIDER_PRIORITY_USER) + def get_screen_size(self, display): + mon_geoms = [ + display.get_monitor(i).get_geometry() + for i in range(display.get_n_monitors()) + ] + + x0 = min(r.x for r in mon_geoms) + y0 = min(r.y for r in mon_geoms) + x1 = max(r.x + r.width for r in mon_geoms) + y1 = max(r.y + r.height for r in mon_geoms) + + return x1 - x0, y1 - y0 + def _area_draw(self, widget: Gtk.ApplicationWindow, cr: cairo.Context) -> None: cr.set_source_rgba( *(0, 0, 0, 0.0) ) cr.set_operator(cairo.OPERATOR_SOURCE) @@ -68,4 +89,4 @@ class RegionWindow(Gtk.Window): def _show_region_window(self): - self.show() + self.show() \ No newline at end of file