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()
|
||||
|
||||
self.board = Board()
|
||||
self._clipboard_watch_handler = None
|
||||
|
||||
self.toast_overlay = Adw.ToastOverlay()
|
||||
self.set_content(self.toast_overlay)
|
||||
@ -178,6 +179,13 @@ class MainWindow(Adw.ApplicationWindow):
|
||||
clip_btn.connect("clicked", lambda _b: self._fetch_clipboard())
|
||||
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))
|
||||
|
||||
for label, popover_builder in (
|
||||
@ -193,6 +201,11 @@ class MainWindow(Adw.ApplicationWindow):
|
||||
strike_btn.connect("clicked", lambda _b: self._add_strike())
|
||||
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.set_tooltip_text("Firing commands")
|
||||
firing_btn.connect("clicked", lambda _b: self._toggle_firing_panel())
|
||||
@ -235,6 +248,7 @@ class MainWindow(Adw.ApplicationWindow):
|
||||
)
|
||||
)
|
||||
self.add_controller(controller)
|
||||
self.connect("destroy", self._on_window_destroy)
|
||||
|
||||
# -- generic helpers -----------------------------------------------------
|
||||
def _make_menu_button(self, label: str, build_popover) -> Gtk.MenuButton:
|
||||
@ -334,6 +348,58 @@ class MainWindow(Adw.ApplicationWindow):
|
||||
|
||||
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:
|
||||
changed = []
|
||||
if info.nest_coord is not None:
|
||||
|
||||
@ -384,6 +384,17 @@ class Board:
|
||||
def remove_target(self, target: Target) -> None:
|
||||
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:
|
||||
"""Manual drag-order: `self.targets`' list order is itself the
|
||||
persisted order (saved/loaded as a plain JSON array), and is what
|
||||
|
||||
Loading…
Reference in New Issue
Block a user