From 1a4c84f9d56f3c8f68120dc90e0c58b0a53be092 Mon Sep 17 00:00:00 2001 From: Arda Aydin Date: Sun, 18 Oct 2026 14:35:34 +0300 Subject: [PATCH] Wiresheet: arrow keys nudge selected boxes one grid step Co-Authored-By: Claude Opus 5.5 --- app_state.py | 2 +- main.py | 38 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 39 insertions(+), 1 deletion(-) diff --git a/app_state.py b/app_state.py index 1a899a6..092a810 100644 --- a/app_state.py +++ b/app_state.py @@ -8,7 +8,7 @@ so later rebinding would only ever be seen by the module that did it. import configparser import os -VERSION = "0.0.0.178" +VERSION = "0.0.0.179" PROPERTIES_FILE = os.path.join(os.path.dirname(os.path.abspath(__file__)), "editor.properties") DEFAULT_GRID_SIZE = 10 DEFAULT_ADD_TIMESTAMP = True diff --git a/main.py b/main.py index 23bb77b..55c9ae2 100644 --- a/main.py +++ b/main.py @@ -2241,6 +2241,39 @@ def on_canvas_release(event): wiresheet.redraw_links() +def nudge_selected_boxes(dx, dy): + """Arrow keys: moves every selected box one meta step, as one undoable move.""" + canvas = app_state.canvas + tags = [t for t in app_state.selected_canvas_tags if t in app_state.canvas_comp_map] + if not tags or app_state.drag_data.get("tag"): + return "break" + step = app_state.meta_scale * app_state.zoom + margin = wiresheet.layout_margin(app_state.meta_scale) * app_state.zoom + limit = wiresheet.META_COORD_MAX * step + origins = {t: wiresheet.get_box_origin(t) for t in tags} + if any(o is None for o in origins.values()): + return "break" + # The group stops as one at the margin strip and the sheet's far edge. + shift_x = min(limit - max(o[0] for o in origins.values()), + max(margin - min(o[0] for o in origins.values()), dx * step)) + shift_y = min(limit - max(o[1] for o in origins.values()), + max(margin - min(o[1] for o in origins.values()), dy * step)) + if not shift_x and not shift_y: + return "break" + + # Moved on the canvas, then written back exactly as a finished mouse drag is. + group = [] + for tag in tags: + comp = app_state.canvas_comp_map[tag] + meta_prop = comp.find("prop[@name='meta']") + group.append({"tag": tag, "origin": origins[tag], + "meta_before": meta_prop.get("val") if meta_prop is not None else None}) + canvas.move(tag, shift_x, shift_y) + app_state.drag_data.update(tag=tags[0], group=group, moved=True, collapse_to=None) + on_canvas_release(None) + return "break" + + def fit_tree_column(tree): """Sizes a Treeview's tree column to its widest visible row. @@ -3830,6 +3863,11 @@ link_drag.install(canvas, on_canvas_right_click, on_canvas_click, on_canvas_drag, on_canvas_release, link_dialog=open_link_dialog, select_boxes=select_canvas_boxes) canvas.bind("", on_canvas_right_click) +# A click gives the sheet the keyboard, so the arrows nudge its selected boxes rather +# than walking whatever tree or list last had focus. +canvas.bind("", lambda e: canvas.focus_set(), add="+") +for _key, _dx, _dy in (("Left", -1, 0), ("Right", 1, 0), ("Up", 0, -1), ("Down", 0, 1)): + canvas.bind(f"<{_key}>", lambda e, dx=_dx, dy=_dy: nudge_selected_boxes(dx, dy)) canvas.bind("", on_canvas_double_click) # Wheel zoom. Windows and macOS report a delta; X11 sends button 4 and 5 instead.