Add clear-board button and clipboard auto-watch toggle
Board.clear() drops the Nest position, spotters, reference points, and targets; wired to a header button behind a confirm dialog since it's not reversible. The clipboard-watch toggle connects to Gdk.Clipboard's 'changed' signal and re-runs the merge-all OCR flow automatically whenever the clipboard gains new image content, skipping non-image changes (e.g. text copied elsewhere) so it doesn't spam toasts. Off by default. Disconnected on window destroy.
This commit is contained in:
parent
139601b107
commit
419674b5af
@ -152,6 +152,7 @@ class MainWindow(Adw.ApplicationWindow):
|
|||||||
_install_css()
|
_install_css()
|
||||||
|
|
||||||
self.board = Board()
|
self.board = Board()
|
||||||
|
self._clipboard_watch_handler = None
|
||||||
|
|
||||||
self.toast_overlay = Adw.ToastOverlay()
|
self.toast_overlay = Adw.ToastOverlay()
|
||||||
self.set_content(self.toast_overlay)
|
self.set_content(self.toast_overlay)
|
||||||
@ -178,6 +179,13 @@ class MainWindow(Adw.ApplicationWindow):
|
|||||||
clip_btn.connect("clicked", lambda _b: self._fetch_clipboard())
|
clip_btn.connect("clicked", lambda _b: self._fetch_clipboard())
|
||||||
header.pack_start(clip_btn)
|
header.pack_start(clip_btn)
|
||||||
|
|
||||||
|
self._watch_btn = Gtk.ToggleButton(icon_name="media-playback-start-symbolic")
|
||||||
|
self._watch_btn.set_tooltip_text(
|
||||||
|
"Auto-watch clipboard: apply new screenshots as soon as they're copied"
|
||||||
|
)
|
||||||
|
self._watch_btn.connect("toggled", self._on_toggle_clipboard_watch)
|
||||||
|
header.pack_start(self._watch_btn)
|
||||||
|
|
||||||
header.pack_start(Gtk.Separator(orientation=Gtk.Orientation.VERTICAL))
|
header.pack_start(Gtk.Separator(orientation=Gtk.Orientation.VERTICAL))
|
||||||
|
|
||||||
for label, popover_builder in (
|
for label, popover_builder in (
|
||||||
@ -193,6 +201,11 @@ class MainWindow(Adw.ApplicationWindow):
|
|||||||
strike_btn.connect("clicked", lambda _b: self._add_strike())
|
strike_btn.connect("clicked", lambda _b: self._add_strike())
|
||||||
header.pack_start(strike_btn)
|
header.pack_start(strike_btn)
|
||||||
|
|
||||||
|
clear_btn = Gtk.Button(icon_name="edit-clear-all-symbolic")
|
||||||
|
clear_btn.set_tooltip_text("Clear board (drop everything)")
|
||||||
|
clear_btn.connect("clicked", lambda _b: self._clear_board())
|
||||||
|
header.pack_start(clear_btn)
|
||||||
|
|
||||||
firing_btn = Gtk.Button(icon_name="sidebar-show-right-symbolic")
|
firing_btn = Gtk.Button(icon_name="sidebar-show-right-symbolic")
|
||||||
firing_btn.set_tooltip_text("Firing commands")
|
firing_btn.set_tooltip_text("Firing commands")
|
||||||
firing_btn.connect("clicked", lambda _b: self._toggle_firing_panel())
|
firing_btn.connect("clicked", lambda _b: self._toggle_firing_panel())
|
||||||
@ -235,6 +248,7 @@ class MainWindow(Adw.ApplicationWindow):
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
self.add_controller(controller)
|
self.add_controller(controller)
|
||||||
|
self.connect("destroy", self._on_window_destroy)
|
||||||
|
|
||||||
# -- generic helpers -----------------------------------------------------
|
# -- generic helpers -----------------------------------------------------
|
||||||
def _make_menu_button(self, label: str, build_popover) -> Gtk.MenuButton:
|
def _make_menu_button(self, label: str, build_popover) -> Gtk.MenuButton:
|
||||||
@ -334,6 +348,58 @@ class MainWindow(Adw.ApplicationWindow):
|
|||||||
|
|
||||||
on_parsed(info)
|
on_parsed(info)
|
||||||
|
|
||||||
|
def _on_toggle_clipboard_watch(self, btn: Gtk.ToggleButton) -> None:
|
||||||
|
"""Auto-watch toggle: while on, every clipboard change that looks
|
||||||
|
like an image (not e.g. text copied elsewhere) is OCR'd and merged
|
||||||
|
automatically, no manual fetch needed between screenshots."""
|
||||||
|
clipboard = Gdk.Display.get_default().get_clipboard()
|
||||||
|
if btn.get_active():
|
||||||
|
self._clipboard_watch_handler = clipboard.connect("changed", self._on_clipboard_changed)
|
||||||
|
btn.set_icon_name("media-playback-stop-symbolic")
|
||||||
|
btn.set_tooltip_text("Auto-watch clipboard: on, click to stop")
|
||||||
|
self.toast("Watching clipboard, new screenshots apply automatically.")
|
||||||
|
else:
|
||||||
|
if self._clipboard_watch_handler is not None:
|
||||||
|
clipboard.disconnect(self._clipboard_watch_handler)
|
||||||
|
self._clipboard_watch_handler = None
|
||||||
|
btn.set_icon_name("media-playback-start-symbolic")
|
||||||
|
btn.set_tooltip_text("Auto-watch clipboard: apply new screenshots as soon as they're copied")
|
||||||
|
|
||||||
|
def _on_clipboard_changed(self, clipboard: Gdk.Clipboard) -> None:
|
||||||
|
formats = clipboard.get_formats()
|
||||||
|
if formats is None or not formats.contain_gtype(Gdk.Texture):
|
||||||
|
return # not an image (e.g. text copied elsewhere), ignore quietly
|
||||||
|
self._run_ocr_from_clipboard(self._merge_all)
|
||||||
|
|
||||||
|
def _on_window_destroy(self, *_a) -> None:
|
||||||
|
if self._clipboard_watch_handler is not None:
|
||||||
|
Gdk.Display.get_default().get_clipboard().disconnect(self._clipboard_watch_handler)
|
||||||
|
self._clipboard_watch_handler = None
|
||||||
|
|
||||||
|
def _clear_board(self) -> None:
|
||||||
|
board = self.board
|
||||||
|
if board.nest.coord is None and not board.spotters and not board.reference_points and not board.targets:
|
||||||
|
return # nothing to clear
|
||||||
|
dialog = Adw.AlertDialog(
|
||||||
|
heading="Clear board?",
|
||||||
|
body="Drops the Nest position and every spotter, reference point, and target. This can't be undone.",
|
||||||
|
)
|
||||||
|
dialog.add_response("cancel", "Cancel")
|
||||||
|
dialog.add_response("clear", "Clear")
|
||||||
|
dialog.set_response_appearance("clear", Adw.ResponseAppearance.DESTRUCTIVE)
|
||||||
|
dialog.set_default_response("cancel")
|
||||||
|
dialog.set_close_response("cancel")
|
||||||
|
dialog.connect("response", self._on_clear_board_response)
|
||||||
|
dialog.present(self)
|
||||||
|
|
||||||
|
def _on_clear_board_response(self, _dialog, response: str) -> None:
|
||||||
|
if response != "clear":
|
||||||
|
return
|
||||||
|
self.board.clear()
|
||||||
|
self._set_selection(None)
|
||||||
|
self._refresh()
|
||||||
|
self.toast("Board cleared.")
|
||||||
|
|
||||||
def _merge_all(self, info: "ocr.ParsedInfo") -> None:
|
def _merge_all(self, info: "ocr.ParsedInfo") -> None:
|
||||||
changed = []
|
changed = []
|
||||||
if info.nest_coord is not None:
|
if info.nest_coord is not None:
|
||||||
|
|||||||
@ -384,6 +384,17 @@ class Board:
|
|||||||
def remove_target(self, target: Target) -> None:
|
def remove_target(self, target: Target) -> None:
|
||||||
self.targets.remove(target)
|
self.targets.remove(target)
|
||||||
|
|
||||||
|
# -- reset ----------------------------------------------------------
|
||||||
|
def clear(self) -> None:
|
||||||
|
"""Drop everything: Nest position, spotters, reference points,
|
||||||
|
targets. Used by the "clear board" action for a fresh start
|
||||||
|
without restarting the app."""
|
||||||
|
self.nest = Nest()
|
||||||
|
self.spotters.clear()
|
||||||
|
self.reference_points.clear()
|
||||||
|
self.targets.clear()
|
||||||
|
self._spotter_seq = 0
|
||||||
|
|
||||||
def reorder_target(self, target: Target, new_index: int) -> None:
|
def reorder_target(self, target: Target, new_index: int) -> None:
|
||||||
"""Manual drag-order: `self.targets`' list order is itself the
|
"""Manual drag-order: `self.targets`' list order is itself the
|
||||||
persisted order (saved/loaded as a plain JSON array), and is what
|
persisted order (saved/loaded as a plain JSON array), and is what
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user